@sumaris-net/ngx-components 0.24.2 → 0.25.2
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.
- package/bundles/{ngx-sumaris-components.umd.js → sumaris-net.ngx-components.umd.js} +1038 -926
- package/bundles/sumaris-net.ngx-components.umd.js.map +1 -0
- package/bundles/sumaris-net.ngx-components.umd.min.js +25 -0
- package/bundles/sumaris-net.ngx-components.umd.min.js.map +1 -0
- package/doc/changelog.md +4 -0
- package/esm2015/public_api.js +2 -1
- package/esm2015/src/app/admin/users/list/users.js +6 -4
- package/esm2015/src/app/core/graphql/graphql.service.js +25 -72
- package/esm2015/src/app/core/graphql/graphql.utils.js +1 -1
- package/esm2015/src/app/core/install/install-upgrade-card.component.js +105 -46
- package/esm2015/src/app/core/services/account.service.js +146 -149
- package/esm2015/src/app/core/services/config.service.js +21 -14
- package/esm2015/src/app/core/services/local-settings.service.js +72 -87
- package/esm2015/src/app/core/services/network.service.js +136 -171
- package/esm2015/src/app/core/services/platform.service.js +34 -17
- package/esm2015/src/app/core/services/storage/entities-storage.service.js +59 -63
- package/esm2015/src/app/shared/audio/audio.js +10 -39
- package/esm2015/src/app/shared/material/boolean/material.boolean.js +1 -1
- package/esm2015/src/app/shared/services/startable-service.class.js +77 -0
- package/esm2015/src/environments/environment.class.js +1 -1
- package/esm2015/src/environments/environment.js +5 -1
- package/esm2015/sumaris-net.ngx-components.js +13 -0
- package/fesm2015/{ngx-sumaris-components.js → sumaris-net.ngx-components.js} +669 -646
- package/fesm2015/sumaris-net.ngx-components.js.map +1 -0
- package/package.json +13 -9
- package/public_api.d.ts +1 -0
- package/src/app/admin/users/list/users.d.ts +1 -0
- package/src/app/core/graphql/graphql.service.d.ts +5 -13
- package/src/app/core/graphql/graphql.utils.d.ts +2 -1
- package/src/app/core/install/install-upgrade-card.component.d.ts +12 -7
- package/src/app/core/services/account.service.d.ts +11 -10
- package/src/app/core/services/config.service.d.ts +6 -5
- package/src/app/core/services/local-settings.service.d.ts +4 -9
- package/src/app/core/services/network.service.d.ts +27 -38
- package/src/app/core/services/platform.service.d.ts +2 -1
- package/src/app/core/services/storage/entities-storage.service.d.ts +13 -13
- package/src/app/shared/audio/audio.d.ts +4 -8
- package/src/app/shared/services/startable-service.class.d.ts +26 -0
- package/src/assets/i18n/fr.json +2 -0
- package/src/environments/environment.class.d.ts +1 -0
- package/{ngx-sumaris-components.d.ts → sumaris-net.ngx-components.d.ts} +0 -0
- package/sumaris-net.ngx-components.metadata.json +1 -0
- package/bundles/ngx-sumaris-components.umd.js.map +0 -1
- package/bundles/ngx-sumaris-components.umd.min.js +0 -25
- package/bundles/ngx-sumaris-components.umd.min.js.map +0 -1
- package/esm2015/ngx-sumaris-components.js +0 -13
- package/fesm2015/ngx-sumaris-components.js.map +0 -1
- package/ngx-sumaris-components.metadata.json +0 -1
|
@@ -36,7 +36,7 @@ import { MatAutocomplete, MatAutocompleteTrigger, MatAutocompleteModule, MAT_AUT
|
|
|
36
36
|
import { __awaiter, __decorate } from 'tslib';
|
|
37
37
|
import * as i1$6 from '@angular/forms';
|
|
38
38
|
import { NG_VALUE_ACCESSOR, FormGroupDirective, ReactiveFormsModule, AbstractControl, Validators, FormBuilder, FormControl, FormGroup, FormArray } from '@angular/forms';
|
|
39
|
-
import { timer, merge, fromEvent, Subscription, BehaviorSubject, Subject, isObservable, of, noop as noop$8, Observable, defer,
|
|
39
|
+
import { timer, merge, fromEvent, Subscription, BehaviorSubject, Subject, isObservable, of, noop as noop$8, Observable, defer, combineLatest, EMPTY } from 'rxjs';
|
|
40
40
|
import { filter, first, map, takeUntil, startWith, debounceTime, takeWhile, switchMap, tap, distinctUntilChanged, mergeMap, catchError, throttleTime } from 'rxjs/operators';
|
|
41
41
|
import * as i1$1 from '@ngx-translate/core';
|
|
42
42
|
import { TranslateService, TranslateModule } from '@ngx-translate/core';
|
|
@@ -4966,7 +4966,7 @@ MatBooleanField.decorators = [
|
|
|
4966
4966
|
DEFAULT_VALUE_ACCESSOR$1
|
|
4967
4967
|
],
|
|
4968
4968
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
4969
|
-
styles: [":host{width:100%}.mat-form-field.mat-boolean-field-radio{margin-top:-2px}.mat-form-field.mat-boolean-field-button{margin-top:-4px}.mat-form-field .mat-form-field-buttons{width:100%;display:flex;flex-flow:row wrap;justify-content:flex-start;align-items:center}.mat-form-field .mat-form-field-buttons .mat-form-field-button{--flex-basis:calc(100% / var(--buttons-col-count, 2));flex:0 1 var(--flex-basis);max-width:
|
|
4969
|
+
styles: [":host{width:100%}.mat-form-field.mat-boolean-field-radio{margin-top:-2px}.mat-form-field.mat-boolean-field-button{margin-top:-4px}.mat-form-field .mat-form-field-buttons{width:100%;display:flex;flex-flow:row wrap;justify-content:flex-start;align-items:center}.mat-form-field .mat-form-field-buttons .mat-form-field-button{--flex-basis:calc(100% / var(--buttons-col-count, 2));flex:0 1 var(--flex-basis);max-width:min(var(--flex-basis),150px);white-space:normal;padding:0 2px;height:40px}"]
|
|
4970
4970
|
},] }
|
|
4971
4971
|
];
|
|
4972
4972
|
MatBooleanField.ctorParameters = () => [
|
|
@@ -6888,31 +6888,97 @@ AppFormField.propDecorators = {
|
|
|
6888
6888
|
matInput: [{ type: ViewChild, args: ['matInput',] }]
|
|
6889
6889
|
};
|
|
6890
6890
|
|
|
6891
|
+
class StartableService {
|
|
6892
|
+
constructor(platform) {
|
|
6893
|
+
this.onStart = new Subject();
|
|
6894
|
+
this._debug = false;
|
|
6895
|
+
this._data = null;
|
|
6896
|
+
this._started = false;
|
|
6897
|
+
this._startPrerequisite = platform
|
|
6898
|
+
? () => platform.ready()
|
|
6899
|
+
: () => Promise.resolve();
|
|
6900
|
+
}
|
|
6901
|
+
start() {
|
|
6902
|
+
if (this._startPromise)
|
|
6903
|
+
return this._startPromise;
|
|
6904
|
+
if (this._started)
|
|
6905
|
+
return Promise.resolve(this._data);
|
|
6906
|
+
this._startPromise = this._startPrerequisite()
|
|
6907
|
+
.then(() => this.ngOnStart())
|
|
6908
|
+
.then(data => {
|
|
6909
|
+
this._data = data;
|
|
6910
|
+
this._started = true;
|
|
6911
|
+
this._startPromise = undefined;
|
|
6912
|
+
this.onStart.next(this._data);
|
|
6913
|
+
return this._data;
|
|
6914
|
+
})
|
|
6915
|
+
.catch(err => {
|
|
6916
|
+
console.error('Failed to start a service: ' + (err && err.message || err), err);
|
|
6917
|
+
this._started = false;
|
|
6918
|
+
this._startPromise = null;
|
|
6919
|
+
return null;
|
|
6920
|
+
});
|
|
6921
|
+
return this._startPromise;
|
|
6922
|
+
}
|
|
6923
|
+
stop() {
|
|
6924
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
6925
|
+
try {
|
|
6926
|
+
yield this.ngOnStop();
|
|
6927
|
+
this._started = false;
|
|
6928
|
+
this._startPromise = undefined;
|
|
6929
|
+
}
|
|
6930
|
+
catch (err) {
|
|
6931
|
+
console.error('Failed to stop a service: ' + (err && err.message || err), err);
|
|
6932
|
+
}
|
|
6933
|
+
});
|
|
6934
|
+
}
|
|
6935
|
+
restart() {
|
|
6936
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
6937
|
+
if (this._startPromise)
|
|
6938
|
+
yield this._startPromise; // Wait end of previous loading
|
|
6939
|
+
if (this._started)
|
|
6940
|
+
yield this.stop(); // Then stop if started
|
|
6941
|
+
return this.start(); // Then start again
|
|
6942
|
+
});
|
|
6943
|
+
}
|
|
6944
|
+
get started() {
|
|
6945
|
+
return this._started;
|
|
6946
|
+
}
|
|
6947
|
+
get starting() {
|
|
6948
|
+
return !!this._startPromise;
|
|
6949
|
+
}
|
|
6950
|
+
ready() {
|
|
6951
|
+
if (this._started)
|
|
6952
|
+
return Promise.resolve(this._data);
|
|
6953
|
+
return this.start();
|
|
6954
|
+
}
|
|
6955
|
+
ngOnStop() {
|
|
6956
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
6957
|
+
// Can be override by subclasses
|
|
6958
|
+
});
|
|
6959
|
+
}
|
|
6960
|
+
}
|
|
6961
|
+
StartableService.ctorParameters = () => [
|
|
6962
|
+
{ type: undefined, decorators: [{ type: Optional }] }
|
|
6963
|
+
];
|
|
6964
|
+
|
|
6891
6965
|
const SYSTEM_SOUNDS = [
|
|
6892
6966
|
{ id: 'beep-confirm', assetPath: 'assets/audio/beep-confirm.mp3', vibration: 250 },
|
|
6893
6967
|
{ id: 'beep-error', assetPath: 'assets/audio/beep-error.mp3', vibration: 1000 },
|
|
6894
6968
|
{ id: 'startup', assetPath: 'assets/audio/unfa-ping.mp3', vibration: [1, 500, 250, 750] },
|
|
6895
6969
|
];
|
|
6896
|
-
class AudioProvider {
|
|
6970
|
+
class AudioProvider extends StartableService {
|
|
6897
6971
|
constructor(platform, nativeAudio, vibration, audioManagement) {
|
|
6972
|
+
super(platform);
|
|
6898
6973
|
this.platform = platform;
|
|
6899
6974
|
this.nativeAudio = nativeAudio;
|
|
6900
6975
|
this.vibration = vibration;
|
|
6901
6976
|
this.audioManagement = audioManagement;
|
|
6902
|
-
this._started = false;
|
|
6903
6977
|
this._audioMode = AudioManagement.AudioMode.NORMAL;
|
|
6904
6978
|
this._preloadedSounds = {};
|
|
6905
6979
|
this._htmlAudioCache = {};
|
|
6906
|
-
this.onStart = new Subject();
|
|
6907
6980
|
this.start();
|
|
6908
6981
|
}
|
|
6909
|
-
ready() {
|
|
6910
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
6911
|
-
if (this._started)
|
|
6912
|
-
return Promise.resolve();
|
|
6913
|
-
yield this.start();
|
|
6914
|
-
});
|
|
6915
|
-
}
|
|
6916
6982
|
playBeepConfirm() {
|
|
6917
6983
|
return this.play('beep-confirm', {
|
|
6918
6984
|
// Vibrate only if in vibration mode
|
|
@@ -6954,7 +7020,7 @@ class AudioProvider {
|
|
|
6954
7020
|
play(id, opts) {
|
|
6955
7021
|
return __awaiter(this, void 0, void 0, function* () {
|
|
6956
7022
|
// Make sure provider is ready
|
|
6957
|
-
if (!this.
|
|
7023
|
+
if (!this.started)
|
|
6958
7024
|
yield this.ready();
|
|
6959
7025
|
const sound = this._preloadedSounds[id];
|
|
6960
7026
|
if (!sound) {
|
|
@@ -7015,7 +7081,7 @@ class AudioProvider {
|
|
|
7015
7081
|
}
|
|
7016
7082
|
vibrate(timeInMs) {
|
|
7017
7083
|
return __awaiter(this, void 0, void 0, function* () {
|
|
7018
|
-
if (!this.
|
|
7084
|
+
if (!this.started)
|
|
7019
7085
|
yield this.ready();
|
|
7020
7086
|
if (!this.vibration)
|
|
7021
7087
|
return; // Skip if vibrate plugin
|
|
@@ -7023,45 +7089,24 @@ class AudioProvider {
|
|
|
7023
7089
|
});
|
|
7024
7090
|
}
|
|
7025
7091
|
/* -- protected methods -- */
|
|
7026
|
-
|
|
7027
|
-
|
|
7028
|
-
|
|
7029
|
-
if (this._started)
|
|
7030
|
-
return Promise.resolve();
|
|
7031
|
-
let cordova;
|
|
7032
|
-
this._startPromise = this.platform.ready()
|
|
7033
|
-
.then(() => __awaiter(this, void 0, void 0, function* () {
|
|
7034
|
-
cordova = this.platform.is('cordova');
|
|
7092
|
+
ngOnStart() {
|
|
7093
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
7094
|
+
let cordova = this.platform.is('cordova');
|
|
7035
7095
|
this._audioType = cordova && this.nativeAudio ? 'native' : 'html5';
|
|
7036
7096
|
console.info(`[audio] Starting audio provider {${this._audioType}}...`);
|
|
7037
7097
|
// Listen audio mode changed
|
|
7038
7098
|
if (cordova && this.audioManagement) {
|
|
7039
7099
|
yield this.readAudioMode();
|
|
7040
7100
|
}
|
|
7041
|
-
}))
|
|
7042
7101
|
// Pre-loading system sounds
|
|
7043
|
-
.then(() => {
|
|
7044
7102
|
console.debug('[audio] Preloading audio sounds...');
|
|
7045
|
-
|
|
7103
|
+
yield Promise.all(SYSTEM_SOUNDS.map(s => {
|
|
7046
7104
|
// Disable vibration is cordova not enabled
|
|
7047
7105
|
if (!cordova)
|
|
7048
7106
|
s.vibration = undefined;
|
|
7049
7107
|
return this.preload(s);
|
|
7050
7108
|
}));
|
|
7051
|
-
})
|
|
7052
|
-
.then(() => {
|
|
7053
|
-
this._started = true;
|
|
7054
|
-
this._startPromise = null;
|
|
7055
|
-
console.info('[audio] Audio provider started');
|
|
7056
|
-
// Emit event
|
|
7057
|
-
this.onStart.next();
|
|
7058
|
-
})
|
|
7059
|
-
.catch(err => {
|
|
7060
|
-
console.error('[audio] Unable to start audio provider: ' + (err && err.message || err), err);
|
|
7061
|
-
this._started = false;
|
|
7062
|
-
this._startPromise = null;
|
|
7063
7109
|
});
|
|
7064
|
-
return this._startPromise;
|
|
7065
7110
|
}
|
|
7066
7111
|
readAudioMode() {
|
|
7067
7112
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -7980,6 +8025,10 @@ const environment = Object.freeze({
|
|
|
7980
8025
|
{
|
|
7981
8026
|
host: 'sih.sfa.sc',
|
|
7982
8027
|
port: 80
|
|
8028
|
+
},
|
|
8029
|
+
{
|
|
8030
|
+
host: 'test.sumaris.net',
|
|
8031
|
+
port: 443
|
|
7983
8032
|
}
|
|
7984
8033
|
],
|
|
7985
8034
|
defaultAppName: 'SUMARiS',
|
|
@@ -8508,14 +8557,14 @@ const DEFAULT_SETTINGS = {
|
|
|
8508
8557
|
};
|
|
8509
8558
|
const APP_LOCAL_SETTINGS = new InjectionToken('DefaultLocalSettings');
|
|
8510
8559
|
const APP_LOCAL_SETTINGS_OPTIONS = new InjectionToken('LocalSettingsOptions');
|
|
8511
|
-
class LocalSettingsService {
|
|
8560
|
+
class LocalSettingsService extends StartableService {
|
|
8512
8561
|
constructor(translate, platform, storage, environment, defaultSettings, defaultOptionsMap) {
|
|
8562
|
+
super(platform);
|
|
8513
8563
|
this.translate = translate;
|
|
8514
8564
|
this.platform = platform;
|
|
8515
8565
|
this.storage = storage;
|
|
8516
8566
|
this.environment = environment;
|
|
8517
8567
|
this.defaultSettings = defaultSettings;
|
|
8518
|
-
this._started = false;
|
|
8519
8568
|
this.onChange = new Subject();
|
|
8520
8569
|
this.defaultSettings = Object.assign(Object.assign({}, DEFAULT_SETTINGS), this.defaultSettings);
|
|
8521
8570
|
this._optionDefs = Object.values(defaultOptionsMap);
|
|
@@ -8525,60 +8574,40 @@ class LocalSettingsService {
|
|
|
8525
8574
|
console.debug('[settings] Creating service');
|
|
8526
8575
|
}
|
|
8527
8576
|
get settings() {
|
|
8528
|
-
return this.
|
|
8577
|
+
return this._data || this.defaultSettings;
|
|
8529
8578
|
}
|
|
8530
8579
|
get locale() {
|
|
8531
|
-
return this.
|
|
8580
|
+
return this._data && this._data.locale || this.translate.currentLang || this.translate.defaultLang;
|
|
8532
8581
|
}
|
|
8533
8582
|
get latLongFormat() {
|
|
8534
|
-
return this.
|
|
8583
|
+
return this._data && this._data.latLongFormat || 'DDMM';
|
|
8535
8584
|
}
|
|
8536
8585
|
get usageMode() {
|
|
8537
|
-
return (this.
|
|
8586
|
+
return (this._data && this._data.usageMode || (this.mobile ? 'FIELD' : 'DESK'));
|
|
8538
8587
|
}
|
|
8539
8588
|
get mobile() {
|
|
8540
|
-
return this.
|
|
8589
|
+
return this._data && toBoolean(this._data.mobile, this.platform.is('mobile'));
|
|
8541
8590
|
}
|
|
8542
8591
|
set mobile(value) {
|
|
8543
|
-
this.
|
|
8592
|
+
this._data.mobile = value;
|
|
8544
8593
|
}
|
|
8545
8594
|
get touchUi() {
|
|
8546
|
-
return this.
|
|
8595
|
+
return this._data.touchUi;
|
|
8547
8596
|
}
|
|
8548
8597
|
set touchUi(value) {
|
|
8549
|
-
this.
|
|
8598
|
+
this._data.touchUi = value;
|
|
8550
8599
|
}
|
|
8551
8600
|
get pageHistory() {
|
|
8552
|
-
return (this.
|
|
8601
|
+
return (this._data && this._data.pageHistory || []);
|
|
8553
8602
|
}
|
|
8554
|
-
|
|
8555
|
-
|
|
8556
|
-
return this._startPromise;
|
|
8557
|
-
if (this._started)
|
|
8558
|
-
return Promise.resolve(this.data);
|
|
8559
|
-
console.info('[settings] Starting settings...');
|
|
8603
|
+
ngOnStart() {
|
|
8604
|
+
console.info('[settings] Starting service...');
|
|
8560
8605
|
// Restoring local settings
|
|
8561
|
-
this.
|
|
8562
|
-
|
|
8563
|
-
|
|
8564
|
-
|
|
8565
|
-
|
|
8566
|
-
})
|
|
8567
|
-
.then(() => this.restoreLocally())
|
|
8568
|
-
.then(data => {
|
|
8569
|
-
this._started = true;
|
|
8570
|
-
this._startPromise = undefined;
|
|
8571
|
-
return data;
|
|
8572
|
-
});
|
|
8573
|
-
return this._startPromise;
|
|
8574
|
-
}
|
|
8575
|
-
get started() {
|
|
8576
|
-
return this._started;
|
|
8577
|
-
}
|
|
8578
|
-
ready() {
|
|
8579
|
-
if (this._started)
|
|
8580
|
-
return Promise.resolve(this.data);
|
|
8581
|
-
return this.start();
|
|
8606
|
+
this._data.mobile = isNotNil(this._data.mobile) ? this._data.mobile : this.platform.is('mobile');
|
|
8607
|
+
this._data.touchUi = this._data.mobile || this.platform.is('phablet') || this.platform.is('tablet');
|
|
8608
|
+
this._data.usageMode = this.platform.is('android') ? 'FIELD' : 'DESK'; // FIELD by default if Android
|
|
8609
|
+
// Restoring local settings
|
|
8610
|
+
return this.restoreLocally();
|
|
8582
8611
|
}
|
|
8583
8612
|
isUsageMode(mode) {
|
|
8584
8613
|
return this.usageMode === mode;
|
|
@@ -8588,6 +8617,7 @@ class LocalSettingsService {
|
|
|
8588
8617
|
}
|
|
8589
8618
|
restoreLocally() {
|
|
8590
8619
|
return __awaiter(this, void 0, void 0, function* () {
|
|
8620
|
+
let data = this._data || {};
|
|
8591
8621
|
// Restore from storage
|
|
8592
8622
|
const settingsStr = yield this.storage.get(SETTINGS_STORAGE_KEY);
|
|
8593
8623
|
// Restore local settings (or keep old settings)
|
|
@@ -8598,28 +8628,29 @@ class LocalSettingsService {
|
|
|
8598
8628
|
SETTINGS_TRANSIENT_PROPERTIES.forEach(transientKey => {
|
|
8599
8629
|
delete restoredData[transientKey];
|
|
8600
8630
|
});
|
|
8601
|
-
|
|
8631
|
+
// Merge into existing data
|
|
8632
|
+
data = Object.assign(data, restoredData);
|
|
8602
8633
|
}
|
|
8603
8634
|
// Emit event
|
|
8604
|
-
this.onChange.next(
|
|
8605
|
-
return
|
|
8635
|
+
this.onChange.next(data);
|
|
8636
|
+
return data;
|
|
8606
8637
|
});
|
|
8607
8638
|
}
|
|
8608
8639
|
setProperty(keyOrDef, value) {
|
|
8609
|
-
if (!this.
|
|
8640
|
+
if (!this._data)
|
|
8610
8641
|
return;
|
|
8611
8642
|
if (typeof keyOrDef === 'object') {
|
|
8612
8643
|
this.setProperty(keyOrDef.key, value);
|
|
8613
8644
|
return;
|
|
8614
8645
|
}
|
|
8615
|
-
this.
|
|
8616
|
-
this.
|
|
8646
|
+
this._data.properties = this._data.properties || {};
|
|
8647
|
+
this._data.properties[keyOrDef] = isNil(value) ? undefined : value.toString();
|
|
8617
8648
|
}
|
|
8618
8649
|
getProperty(keyOrDef, defaultValue) {
|
|
8619
8650
|
if (typeof keyOrDef === 'object') {
|
|
8620
8651
|
return this.getProperty(keyOrDef.key, isNil(defaultValue) ? keyOrDef.defaultValue : defaultValue);
|
|
8621
8652
|
}
|
|
8622
|
-
const value = this.
|
|
8653
|
+
const value = this._data && this._data.properties && this._data.properties[keyOrDef];
|
|
8623
8654
|
return isNotNil(value) ? value : defaultValue;
|
|
8624
8655
|
}
|
|
8625
8656
|
getPropertyAsBoolean(definition, defaultValue) {
|
|
@@ -8642,7 +8673,9 @@ class LocalSettingsService {
|
|
|
8642
8673
|
}
|
|
8643
8674
|
apply(settings, opts) {
|
|
8644
8675
|
return __awaiter(this, void 0, void 0, function* () {
|
|
8645
|
-
|
|
8676
|
+
if (!this.started)
|
|
8677
|
+
yield this.ready();
|
|
8678
|
+
this._data = Object.assign(Object.assign({}, this._data), settings);
|
|
8646
8679
|
// Save locally
|
|
8647
8680
|
if (opts && opts.persistImmediate) {
|
|
8648
8681
|
yield this.persistLocally(true);
|
|
@@ -8652,7 +8685,7 @@ class LocalSettingsService {
|
|
|
8652
8685
|
}
|
|
8653
8686
|
// Emit event
|
|
8654
8687
|
if (!opts || opts.emitEvent !== false) {
|
|
8655
|
-
this.onChange.next(this.
|
|
8688
|
+
this.onChange.next(this._data);
|
|
8656
8689
|
}
|
|
8657
8690
|
});
|
|
8658
8691
|
}
|
|
@@ -8664,38 +8697,38 @@ class LocalSettingsService {
|
|
|
8664
8697
|
});
|
|
8665
8698
|
}
|
|
8666
8699
|
getPageSettings(pageId, propertyName) {
|
|
8667
|
-
if (!this.
|
|
8700
|
+
if (!this._data || !this._data.pages)
|
|
8668
8701
|
return undefined;
|
|
8669
8702
|
const key = pageId.replace(/[/]/g, '__');
|
|
8670
8703
|
if (isNotNilOrBlank(propertyName)) {
|
|
8671
|
-
return getPropertyByPath(this.
|
|
8704
|
+
return getPropertyByPath(this._data.pages, key + '.' + propertyName);
|
|
8672
8705
|
}
|
|
8673
|
-
return this.
|
|
8706
|
+
return this._data.pages[key];
|
|
8674
8707
|
}
|
|
8675
8708
|
savePageSetting(pageId, value, propertyName) {
|
|
8676
8709
|
return __awaiter(this, void 0, void 0, function* () {
|
|
8677
|
-
this.
|
|
8678
|
-
this.
|
|
8710
|
+
this._data = this._data || this.defaultSettings;
|
|
8711
|
+
this._data.pages = this._data.pages || {};
|
|
8679
8712
|
const key = pageId.replace(/[/]/g, '__');
|
|
8680
8713
|
if (propertyName) {
|
|
8681
|
-
this.
|
|
8682
|
-
this.
|
|
8714
|
+
this._data.pages[key] = this._data.pages[key] || {};
|
|
8715
|
+
this._data.pages[key][propertyName] = value;
|
|
8683
8716
|
}
|
|
8684
8717
|
else {
|
|
8685
|
-
this.
|
|
8718
|
+
this._data.pages[key] = value;
|
|
8686
8719
|
}
|
|
8687
8720
|
// Update local settings
|
|
8688
8721
|
this.persistLocally();
|
|
8689
8722
|
});
|
|
8690
8723
|
}
|
|
8691
8724
|
getOfflineFeature(featureName) {
|
|
8692
|
-
if (!this.
|
|
8725
|
+
if (!this._data || !this._data.offlineFeatures || isEmptyArray(this._data.offlineFeatures))
|
|
8693
8726
|
return undefined;
|
|
8694
8727
|
if (!featureName)
|
|
8695
8728
|
throw Error('Missing \'featureName\' argument');
|
|
8696
8729
|
featureName = featureName.toLowerCase();
|
|
8697
8730
|
const featurePrefix = featureName + '#';
|
|
8698
|
-
const feature = this.
|
|
8731
|
+
const feature = this._data.offlineFeatures.find(f => {
|
|
8699
8732
|
if (typeof f === 'string')
|
|
8700
8733
|
return f.toLowerCase().startsWith(featurePrefix);
|
|
8701
8734
|
if (typeof f === 'object' && f.name)
|
|
@@ -8718,24 +8751,24 @@ class LocalSettingsService {
|
|
|
8718
8751
|
hasOfflineFeature(featureName) {
|
|
8719
8752
|
if (featureName)
|
|
8720
8753
|
return isNotNil(this.getOfflineFeature(featureName));
|
|
8721
|
-
return this.
|
|
8754
|
+
return this._data && isNotEmptyArray(this._data.offlineFeatures);
|
|
8722
8755
|
}
|
|
8723
8756
|
saveOfflineFeature(feature) {
|
|
8724
|
-
this.
|
|
8725
|
-
this.
|
|
8757
|
+
this._data = this._data || this.defaultSettings;
|
|
8758
|
+
this._data.offlineFeatures = this._data.offlineFeatures || [];
|
|
8726
8759
|
feature.name = feature.name.toLowerCase();
|
|
8727
8760
|
const featurePrefix = feature.name + '#';
|
|
8728
|
-
const existingIndex = this.
|
|
8761
|
+
const existingIndex = this._data.offlineFeatures.findIndex(f => {
|
|
8729
8762
|
if (typeof f === 'string')
|
|
8730
8763
|
return f.toLowerCase().startsWith(featurePrefix);
|
|
8731
8764
|
if (typeof f === 'object' && f.name)
|
|
8732
8765
|
return f.name === feature.name;
|
|
8733
8766
|
});
|
|
8734
8767
|
if (existingIndex !== -1) {
|
|
8735
|
-
this.
|
|
8768
|
+
this._data.offlineFeatures[existingIndex] = feature;
|
|
8736
8769
|
}
|
|
8737
8770
|
else {
|
|
8738
|
-
this.
|
|
8771
|
+
this._data.offlineFeatures.push(feature);
|
|
8739
8772
|
}
|
|
8740
8773
|
// Update local settings
|
|
8741
8774
|
this.persistLocally();
|
|
@@ -8754,14 +8787,14 @@ class LocalSettingsService {
|
|
|
8754
8787
|
this.saveOfflineFeature(feature);
|
|
8755
8788
|
}
|
|
8756
8789
|
removeOfflineFeatures() {
|
|
8757
|
-
if (this.
|
|
8758
|
-
this.
|
|
8790
|
+
if (this._data && this._data.offlineFeatures) {
|
|
8791
|
+
this._data.offlineFeatures = [];
|
|
8759
8792
|
// Update local settings
|
|
8760
8793
|
this.persistLocally();
|
|
8761
8794
|
}
|
|
8762
8795
|
}
|
|
8763
8796
|
getFieldDisplayAttributes(fieldName, defaultAttributes) {
|
|
8764
|
-
const value = this.
|
|
8797
|
+
const value = this._data && this._data.properties && this._data.properties[`sumaris.field.${fieldName}.attributes`];
|
|
8765
8798
|
// Nothing found in settings: return defaults
|
|
8766
8799
|
if (!value)
|
|
8767
8800
|
return defaultAttributes || ['label', 'name'];
|
|
@@ -8790,7 +8823,7 @@ class LocalSettingsService {
|
|
|
8790
8823
|
// If not inside recursive call: fill page history defaults
|
|
8791
8824
|
if (!pageHistory)
|
|
8792
8825
|
this.fillPageHistoryDefaults(page, opts);
|
|
8793
|
-
pageHistory = pageHistory || this.
|
|
8826
|
+
pageHistory = pageHistory || this._data.pageHistory;
|
|
8794
8827
|
const index = pageHistory.findIndex(p => (
|
|
8795
8828
|
// same path
|
|
8796
8829
|
p.path === page.path
|
|
@@ -8826,10 +8859,10 @@ class LocalSettingsService {
|
|
|
8826
8859
|
}
|
|
8827
8860
|
}
|
|
8828
8861
|
// Save locally (only if not a recursive execution)
|
|
8829
|
-
if (pageHistory === this.
|
|
8862
|
+
if (pageHistory === this._data.pageHistory) {
|
|
8830
8863
|
// If max has been reached, remove old pages
|
|
8831
|
-
if (this.
|
|
8832
|
-
const removedPages = pageHistory.splice(this.
|
|
8864
|
+
if (this._data.pageHistory.length > this._data.pageHistoryMaxSize) {
|
|
8865
|
+
const removedPages = pageHistory.splice(this._data.pageHistoryMaxSize, pageHistory.length - this._data.pageHistoryMaxSize);
|
|
8833
8866
|
console.debug('[settings] Pages removed from history: ', removedPages);
|
|
8834
8867
|
}
|
|
8835
8868
|
// Apply new value
|
|
@@ -8840,7 +8873,7 @@ class LocalSettingsService {
|
|
|
8840
8873
|
removePageHistory(path, opts, pageHistory // used for recursive call to children)
|
|
8841
8874
|
) {
|
|
8842
8875
|
return __awaiter(this, void 0, void 0, function* () {
|
|
8843
|
-
pageHistory = pageHistory || this.
|
|
8876
|
+
pageHistory = pageHistory || this._data.pageHistory;
|
|
8844
8877
|
const index = pageHistory.findIndex(p => p.path === path);
|
|
8845
8878
|
let found = index !== -1;
|
|
8846
8879
|
if (found) {
|
|
@@ -8856,9 +8889,9 @@ class LocalSettingsService {
|
|
|
8856
8889
|
.findIndex(children => this.removePageHistory(path, opts, children)) !== -1;
|
|
8857
8890
|
}
|
|
8858
8891
|
// Save locally (only if not a recursive execution)
|
|
8859
|
-
if (found && pageHistory === this.
|
|
8892
|
+
if (found && pageHistory === this._data.pageHistory) {
|
|
8860
8893
|
// Apply changes
|
|
8861
|
-
yield this.applyProperty('pageHistory', this.
|
|
8894
|
+
yield this.applyProperty('pageHistory', this._data.pageHistory);
|
|
8862
8895
|
}
|
|
8863
8896
|
return found;
|
|
8864
8897
|
});
|
|
@@ -8871,26 +8904,26 @@ class LocalSettingsService {
|
|
|
8871
8904
|
}
|
|
8872
8905
|
/* -- Protected methods -- */
|
|
8873
8906
|
resetData() {
|
|
8874
|
-
this.
|
|
8875
|
-
this.
|
|
8876
|
-
this.
|
|
8877
|
-
this.
|
|
8878
|
-
this.
|
|
8907
|
+
this._data = Object.assign(Object.assign({}, this._data), this.defaultSettings);
|
|
8908
|
+
this._data.locale = this.translate.currentLang || this.translate.defaultLang;
|
|
8909
|
+
this._data.mobile = undefined;
|
|
8910
|
+
this._data.usageMode = undefined;
|
|
8911
|
+
this._data.pageHistory = [];
|
|
8879
8912
|
const defaultPeer = this.environment.defaultPeer && Peer.fromObject(this.environment.defaultPeer);
|
|
8880
|
-
this.
|
|
8881
|
-
if (this.
|
|
8882
|
-
this.onChange.next(this.
|
|
8913
|
+
this._data.peerUrl = defaultPeer && defaultPeer.url || undefined;
|
|
8914
|
+
if (this.started)
|
|
8915
|
+
this.onChange.next(this._data);
|
|
8883
8916
|
}
|
|
8884
8917
|
persistLocally(immediate) {
|
|
8885
8918
|
// Execute immediate
|
|
8886
8919
|
if (immediate) {
|
|
8887
|
-
if (!this.
|
|
8920
|
+
if (!this._data) {
|
|
8888
8921
|
console.debug('[settings] Removing local settings from storage');
|
|
8889
8922
|
return this.storage.remove(SETTINGS_STORAGE_KEY);
|
|
8890
8923
|
}
|
|
8891
8924
|
else {
|
|
8892
|
-
console.debug('[settings] Store local settings', this.
|
|
8893
|
-
return this.storage.set(SETTINGS_STORAGE_KEY, JSON.stringify(this.
|
|
8925
|
+
console.debug('[settings] Store local settings', this._data);
|
|
8926
|
+
return this.storage.set(SETTINGS_STORAGE_KEY, JSON.stringify(this._data));
|
|
8894
8927
|
}
|
|
8895
8928
|
}
|
|
8896
8929
|
// Execute with delay
|
|
@@ -8900,7 +8933,7 @@ class LocalSettingsService {
|
|
|
8900
8933
|
this._$persist = new EventEmitter(true);
|
|
8901
8934
|
this._$persist
|
|
8902
8935
|
.pipe(debounceTime(2000), // add a delay of 2s
|
|
8903
|
-
filter(() => this.
|
|
8936
|
+
filter(() => this.started))
|
|
8904
8937
|
.subscribe(() => this.persistLocally(true));
|
|
8905
8938
|
}
|
|
8906
8939
|
this._$persist.emit();
|
|
@@ -9062,6 +9095,8 @@ function getConnectionType(type) {
|
|
|
9062
9095
|
switch (type) {
|
|
9063
9096
|
case Connection.NONE:
|
|
9064
9097
|
return 'none';
|
|
9098
|
+
case Connection.ETHERNET:
|
|
9099
|
+
return 'ethernet';
|
|
9065
9100
|
case Connection.WIFI:
|
|
9066
9101
|
return 'wifi';
|
|
9067
9102
|
case Connection.CELL:
|
|
@@ -9069,8 +9104,6 @@ function getConnectionType(type) {
|
|
|
9069
9104
|
case Connection.CELL_3G:
|
|
9070
9105
|
case Connection.CELL_4G:
|
|
9071
9106
|
return 'cell';
|
|
9072
|
-
case Connection.ETHERNET:
|
|
9073
|
-
return 'ethernet';
|
|
9074
9107
|
case Connection.UNKNOWN:
|
|
9075
9108
|
return 'unknown';
|
|
9076
9109
|
default:
|
|
@@ -9086,8 +9119,9 @@ const NetworkRefreshTimerPeriod = {
|
|
|
9086
9119
|
MOBILE: 1000,
|
|
9087
9120
|
DESKTOP: 1000
|
|
9088
9121
|
}*/
|
|
9089
|
-
class NetworkService {
|
|
9122
|
+
class NetworkService extends StartableService {
|
|
9090
9123
|
constructor(_document, platform, modalCtrl, cryptoService, storage, settings, cache, http, environment, network, splashScreen, translate, toastController) {
|
|
9124
|
+
super(platform);
|
|
9091
9125
|
this._document = _document;
|
|
9092
9126
|
this.platform = platform;
|
|
9093
9127
|
this.modalCtrl = modalCtrl;
|
|
@@ -9101,13 +9135,11 @@ class NetworkService {
|
|
|
9101
9135
|
this.splashScreen = splashScreen;
|
|
9102
9136
|
this.translate = translate;
|
|
9103
9137
|
this.toastController = toastController;
|
|
9104
|
-
this._started = false;
|
|
9105
|
-
this._subscription = new Subscription();
|
|
9106
|
-
this._listeners = {};
|
|
9107
|
-
this.onStart = new Subject();
|
|
9108
9138
|
this.onPeerChanges = this.onStart.pipe(map(peer => peer && peer.url), filter(isNotNilOrBlank), distinctUntilChanged());
|
|
9109
9139
|
this.onNetworkStatusChanges = new BehaviorSubject(null);
|
|
9110
9140
|
this.onResetNetworkCache = new EventEmitter(true);
|
|
9141
|
+
this._subscription = new Subscription();
|
|
9142
|
+
this._listeners = {};
|
|
9111
9143
|
this._mobile = this.platform.is('mobile');
|
|
9112
9144
|
if (this._mobile) {
|
|
9113
9145
|
this._timerRefreshPeriod = NetworkRefreshTimerPeriod.MOBILE;
|
|
@@ -9118,6 +9150,7 @@ class NetworkService {
|
|
|
9118
9150
|
this._timerRefreshCondition = () => true; // Always check
|
|
9119
9151
|
}
|
|
9120
9152
|
this.resetData();
|
|
9153
|
+
this.onStart.subscribe(() => this.ngOnAfterStart());
|
|
9121
9154
|
// For DEV only
|
|
9122
9155
|
this._debug = !environment.production;
|
|
9123
9156
|
}
|
|
@@ -9131,16 +9164,14 @@ class NetworkService {
|
|
|
9131
9164
|
// If force offline: return 'none'
|
|
9132
9165
|
return this._forceOffline && 'none'
|
|
9133
9166
|
// Else, return device connection type (or unknown)
|
|
9134
|
-
|| (this.
|
|
9167
|
+
|| (this.started && this._deviceConnectionType || 'unknown');
|
|
9135
9168
|
}
|
|
9136
9169
|
get peer() {
|
|
9137
|
-
return this.
|
|
9170
|
+
return this._data && this._data.clone();
|
|
9138
9171
|
}
|
|
9139
9172
|
set peer(peer) {
|
|
9140
|
-
this.
|
|
9141
|
-
|
|
9142
|
-
get started() {
|
|
9143
|
-
return this._started;
|
|
9173
|
+
this._startingPeer = peer;
|
|
9174
|
+
this.restart();
|
|
9144
9175
|
}
|
|
9145
9176
|
/**
|
|
9146
9177
|
* Register to network event
|
|
@@ -9162,78 +9193,6 @@ class NetworkService {
|
|
|
9162
9193
|
return this.addListener(eventType, callback);
|
|
9163
9194
|
}
|
|
9164
9195
|
}
|
|
9165
|
-
start(peer) {
|
|
9166
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
9167
|
-
if (this._startPromise)
|
|
9168
|
-
return this._startPromise;
|
|
9169
|
-
if (this._started)
|
|
9170
|
-
return;
|
|
9171
|
-
console.info('[network] Starting network...');
|
|
9172
|
-
// Restoring local settings
|
|
9173
|
-
this._startPromise = (!peer && this.restoreLocally() || Promise.resolve(peer))
|
|
9174
|
-
.then((peer) => __awaiter(this, void 0, void 0, function* () {
|
|
9175
|
-
// Make sure to hide the splashscreen, before open the modal
|
|
9176
|
-
if (!peer && this.splashScreen)
|
|
9177
|
-
this.splashScreen.hide();
|
|
9178
|
-
// No peer in settings: ask user to choose
|
|
9179
|
-
while (!peer) {
|
|
9180
|
-
console.debug('[network] No peer defined. Asking user to choose a peer.');
|
|
9181
|
-
peer = yield this.showSelectPeerModal({ allowSelectDownPeer: false });
|
|
9182
|
-
}
|
|
9183
|
-
this._peer = peer;
|
|
9184
|
-
this._started = true;
|
|
9185
|
-
this._startPromise = undefined;
|
|
9186
|
-
this.onStart.next(peer);
|
|
9187
|
-
console.info(`[platform] Starting network [OK] {online: ${this.online}}`);
|
|
9188
|
-
}))
|
|
9189
|
-
.catch((err) => {
|
|
9190
|
-
console.error(err && err.message || err, err);
|
|
9191
|
-
this._started = false;
|
|
9192
|
-
this._startPromise = undefined;
|
|
9193
|
-
})
|
|
9194
|
-
// Wait settings starts, then save peer in settings
|
|
9195
|
-
.then(() => this.settings.ready())
|
|
9196
|
-
.then(() => this.settings.apply({ peerUrl: this._peer.url }))
|
|
9197
|
-
.then(() => this.onDeviceConnectionChanged(this.network && this.network.type || 'unknown'))
|
|
9198
|
-
// Start the refresh timer
|
|
9199
|
-
.then(() => this.startRefreshTimer());
|
|
9200
|
-
// Listen for device network changes
|
|
9201
|
-
if (this.network) {
|
|
9202
|
-
this._subscription.add(this.network.onDisconnect().subscribe(() => this.onDeviceConnectionChanged('none')));
|
|
9203
|
-
this._subscription.add(this.network.onConnect().subscribe(() => this.onDeviceConnectionChanged(this.network.type)));
|
|
9204
|
-
}
|
|
9205
|
-
return this._startPromise;
|
|
9206
|
-
});
|
|
9207
|
-
}
|
|
9208
|
-
ready() {
|
|
9209
|
-
if (this._started)
|
|
9210
|
-
return Promise.resolve();
|
|
9211
|
-
return this.start();
|
|
9212
|
-
}
|
|
9213
|
-
stop() {
|
|
9214
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
9215
|
-
this.resetData();
|
|
9216
|
-
this._started = false;
|
|
9217
|
-
this._startPromise = undefined;
|
|
9218
|
-
// Stop timer if cannot refresh anymore
|
|
9219
|
-
if (this._timerRefreshCondition() === false) {
|
|
9220
|
-
this.stopRefreshTimer();
|
|
9221
|
-
}
|
|
9222
|
-
this._subscription.unsubscribe();
|
|
9223
|
-
this._subscription = new Subscription();
|
|
9224
|
-
});
|
|
9225
|
-
}
|
|
9226
|
-
restart(peer) {
|
|
9227
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
9228
|
-
if (this._started) {
|
|
9229
|
-
yield this.stop()
|
|
9230
|
-
.then(() => this.start(peer));
|
|
9231
|
-
}
|
|
9232
|
-
else {
|
|
9233
|
-
yield this.start(peer);
|
|
9234
|
-
}
|
|
9235
|
-
});
|
|
9236
|
-
}
|
|
9237
9196
|
tryOnline(opts) {
|
|
9238
9197
|
return __awaiter(this, void 0, void 0, function* () {
|
|
9239
9198
|
// If offline mode not forced, and device says there is no connection: skip
|
|
@@ -9260,7 +9219,8 @@ class NetworkService {
|
|
|
9260
9219
|
// Disable the offline mode
|
|
9261
9220
|
this.setForceOffline(false);
|
|
9262
9221
|
// Restart
|
|
9263
|
-
|
|
9222
|
+
this._startingPeer = peer;
|
|
9223
|
+
yield this.restart();
|
|
9264
9224
|
// Wait a promise, before recheck
|
|
9265
9225
|
yield this.emit('beforeTryOnlineFinish', this.online);
|
|
9266
9226
|
}
|
|
@@ -9288,7 +9248,7 @@ class NetworkService {
|
|
|
9288
9248
|
// Display toast (without await, because not need to wait toast close event)
|
|
9289
9249
|
return this.showOfflineToast({ showRetryButton: false });
|
|
9290
9250
|
}
|
|
9291
|
-
return this.
|
|
9251
|
+
return this.started && online;
|
|
9292
9252
|
});
|
|
9293
9253
|
}
|
|
9294
9254
|
showOfflineToast(opts) {
|
|
@@ -9336,86 +9296,6 @@ class NetworkService {
|
|
|
9336
9296
|
return false;
|
|
9337
9297
|
});
|
|
9338
9298
|
}
|
|
9339
|
-
/**
|
|
9340
|
-
* Try to restore peer from the local storage
|
|
9341
|
-
*/
|
|
9342
|
-
restoreLocally() {
|
|
9343
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
9344
|
-
// Restore from storage
|
|
9345
|
-
const settingsStr = yield this.storage.get(SETTINGS_STORAGE_KEY);
|
|
9346
|
-
const settings = settingsStr && JSON.parse(settingsStr) || undefined;
|
|
9347
|
-
if (settings && settings.peerUrl) {
|
|
9348
|
-
console.debug(`[network] Use peer {${settings.peerUrl}} (found in the local storage)`);
|
|
9349
|
-
return Peer.parseUrl(settings.peerUrl);
|
|
9350
|
-
}
|
|
9351
|
-
// Else, use default peer in env, if exists
|
|
9352
|
-
if (this.environment.defaultPeer) {
|
|
9353
|
-
return Peer.fromObject(this.environment.defaultPeer);
|
|
9354
|
-
}
|
|
9355
|
-
// Else, if App is hosted, try the web site as a peer
|
|
9356
|
-
const location = this._document && this._document.location;
|
|
9357
|
-
if (location && location.protocol && location.protocol.startsWith('http')) {
|
|
9358
|
-
const hostname = this._document.location.host;
|
|
9359
|
-
const detectedPeer = Peer.parseUrl(`${this._document.location.protocol}${hostname}${this.environment.baseUrl}`);
|
|
9360
|
-
if (yield this.checkPeerAlive(detectedPeer)) {
|
|
9361
|
-
return detectedPeer;
|
|
9362
|
-
}
|
|
9363
|
-
}
|
|
9364
|
-
return undefined;
|
|
9365
|
-
});
|
|
9366
|
-
}
|
|
9367
|
-
/**
|
|
9368
|
-
* Refresh network state, using a ping to pod
|
|
9369
|
-
*/
|
|
9370
|
-
refreshPeerState(opts) {
|
|
9371
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
9372
|
-
});
|
|
9373
|
-
}
|
|
9374
|
-
/**
|
|
9375
|
-
* Stop to network state
|
|
9376
|
-
*
|
|
9377
|
-
* @protected
|
|
9378
|
-
*/
|
|
9379
|
-
stopRefreshTimer() {
|
|
9380
|
-
if (this._timerSubscription) {
|
|
9381
|
-
this._timerSubscription.unsubscribe();
|
|
9382
|
-
this._timerSubscription = undefined;
|
|
9383
|
-
}
|
|
9384
|
-
}
|
|
9385
|
-
/**
|
|
9386
|
-
* Refresh the network state
|
|
9387
|
-
*
|
|
9388
|
-
* @protected
|
|
9389
|
-
*/
|
|
9390
|
-
startRefreshTimer() {
|
|
9391
|
-
if (this._timerSubscription)
|
|
9392
|
-
return; // Already running: skip
|
|
9393
|
-
console.info(`[network] Starting refresh timer, every ${this._timerRefreshPeriod}ms...`);
|
|
9394
|
-
let lastInfo;
|
|
9395
|
-
this._timerSubscription = timer(this._timerRefreshPeriod, this._timerRefreshPeriod)
|
|
9396
|
-
.pipe(
|
|
9397
|
-
// Skip some timer event (see constructor)
|
|
9398
|
-
filter(this._timerRefreshCondition),
|
|
9399
|
-
// Checkin if peer alive
|
|
9400
|
-
tap(() => console.debug('[network] Checking connection to pod...')), mergeMap(() => this.checkPeerAlive(this.peer)),
|
|
9401
|
-
// Filter to keep only changes
|
|
9402
|
-
filter(info => !!info !== !!lastInfo), tap(info => lastInfo = info),
|
|
9403
|
-
// Check compatibility
|
|
9404
|
-
mergeMap((info) => this.checkPeerCompatible(info, { showToast: true })))
|
|
9405
|
-
.subscribe(alive => {
|
|
9406
|
-
if (alive && this.offline) {
|
|
9407
|
-
this.setForceOffline(false);
|
|
9408
|
-
// Restart the service (to force re auth)
|
|
9409
|
-
this.restart();
|
|
9410
|
-
}
|
|
9411
|
-
else if (!alive && this.online) {
|
|
9412
|
-
this.setForceOffline(true);
|
|
9413
|
-
// Stop the service
|
|
9414
|
-
this.stop();
|
|
9415
|
-
}
|
|
9416
|
-
});
|
|
9417
|
-
this._timerSubscription.add(() => console.debug('[network] Refresh timer stopped'));
|
|
9418
|
-
}
|
|
9419
9299
|
/**
|
|
9420
9300
|
* Check if the peer is alive
|
|
9421
9301
|
*
|
|
@@ -9531,6 +9411,123 @@ class NetworkService {
|
|
|
9531
9411
|
});
|
|
9532
9412
|
});
|
|
9533
9413
|
}
|
|
9414
|
+
/* -- protected functions -- */
|
|
9415
|
+
ngOnStart() {
|
|
9416
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
9417
|
+
console.info('[network] Starting network...', this._startingPeer);
|
|
9418
|
+
// Restoring local settings
|
|
9419
|
+
let peer = this._startingPeer || (yield this.restoreLocally());
|
|
9420
|
+
// Make sure to hide the splashscreen, before open the modal
|
|
9421
|
+
if (!peer && this.splashScreen)
|
|
9422
|
+
this.splashScreen.hide();
|
|
9423
|
+
// No peer in settings: ask user to choose
|
|
9424
|
+
while (!peer) {
|
|
9425
|
+
console.debug('[network] No peer defined. Asking user to choose a peer.');
|
|
9426
|
+
peer = yield this.showSelectPeerModal({ allowSelectDownPeer: false });
|
|
9427
|
+
}
|
|
9428
|
+
console.info(`[network] Starting service [OK] {peer: '${peer.url}', online: ${this.online}}`);
|
|
9429
|
+
return peer;
|
|
9430
|
+
});
|
|
9431
|
+
}
|
|
9432
|
+
ngOnAfterStart() {
|
|
9433
|
+
var _a;
|
|
9434
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
9435
|
+
// Wait settings starts, then save peer in settings
|
|
9436
|
+
yield this.settings.apply({ peerUrl: this._data.url });
|
|
9437
|
+
this.onDeviceConnectionChanged(((_a = this.network) === null || _a === void 0 ? void 0 : _a.type) || 'unknown');
|
|
9438
|
+
// Start the refresh timer
|
|
9439
|
+
this.startRefreshTimer();
|
|
9440
|
+
// Listen for device network changes
|
|
9441
|
+
if (this.network) {
|
|
9442
|
+
this._subscription.add(this.network.onDisconnect().subscribe(() => this.onDeviceConnectionChanged('none')));
|
|
9443
|
+
this._subscription.add(this.network.onConnect().subscribe(() => this.onDeviceConnectionChanged(this.network.type)));
|
|
9444
|
+
}
|
|
9445
|
+
});
|
|
9446
|
+
}
|
|
9447
|
+
ngOnStop() {
|
|
9448
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
9449
|
+
this.resetData();
|
|
9450
|
+
// Stop timer if cannot refresh anymore
|
|
9451
|
+
if (this._timerRefreshCondition() === false) {
|
|
9452
|
+
this.stopRefreshTimer();
|
|
9453
|
+
}
|
|
9454
|
+
this._subscription.unsubscribe();
|
|
9455
|
+
this._subscription = new Subscription();
|
|
9456
|
+
});
|
|
9457
|
+
}
|
|
9458
|
+
/**
|
|
9459
|
+
* Try to restore peer from the local storage
|
|
9460
|
+
*/
|
|
9461
|
+
restoreLocally() {
|
|
9462
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
9463
|
+
// Restore from storage
|
|
9464
|
+
const settingsStr = yield this.storage.get(SETTINGS_STORAGE_KEY);
|
|
9465
|
+
const settings = settingsStr && JSON.parse(settingsStr) || undefined;
|
|
9466
|
+
if (settings && settings.peerUrl) {
|
|
9467
|
+
console.debug(`[network] Use peer {${settings.peerUrl}} (found in the local storage)`);
|
|
9468
|
+
return Peer.parseUrl(settings.peerUrl);
|
|
9469
|
+
}
|
|
9470
|
+
// Else, use default peer in env, if exists
|
|
9471
|
+
if (this.environment.defaultPeer) {
|
|
9472
|
+
return Peer.fromObject(this.environment.defaultPeer);
|
|
9473
|
+
}
|
|
9474
|
+
// Else, if App is hosted, try the web site as a peer
|
|
9475
|
+
const location = this._document && this._document.location;
|
|
9476
|
+
if (location && location.protocol && location.protocol.startsWith('http')) {
|
|
9477
|
+
const hostname = this._document.location.host;
|
|
9478
|
+
const detectedPeer = Peer.parseUrl(`${this._document.location.protocol}${hostname}${this.environment.baseUrl}`);
|
|
9479
|
+
if (yield this.checkPeerAlive(detectedPeer)) {
|
|
9480
|
+
return detectedPeer;
|
|
9481
|
+
}
|
|
9482
|
+
}
|
|
9483
|
+
return undefined;
|
|
9484
|
+
});
|
|
9485
|
+
}
|
|
9486
|
+
/**
|
|
9487
|
+
* Stop to network state
|
|
9488
|
+
*
|
|
9489
|
+
* @protected
|
|
9490
|
+
*/
|
|
9491
|
+
stopRefreshTimer() {
|
|
9492
|
+
if (this._timerSubscription) {
|
|
9493
|
+
this._timerSubscription.unsubscribe();
|
|
9494
|
+
this._timerSubscription = undefined;
|
|
9495
|
+
}
|
|
9496
|
+
}
|
|
9497
|
+
/**
|
|
9498
|
+
* Refresh the network state
|
|
9499
|
+
*
|
|
9500
|
+
* @protected
|
|
9501
|
+
*/
|
|
9502
|
+
startRefreshTimer() {
|
|
9503
|
+
if (this._timerSubscription)
|
|
9504
|
+
return; // Already running: skip
|
|
9505
|
+
console.info(`[network] Starting refresh timer, every ${this._timerRefreshPeriod}ms...`);
|
|
9506
|
+
let lastInfo;
|
|
9507
|
+
this._timerSubscription = timer(this._timerRefreshPeriod, this._timerRefreshPeriod)
|
|
9508
|
+
.pipe(
|
|
9509
|
+
// Skip some timer event (see constructor)
|
|
9510
|
+
filter(this._timerRefreshCondition),
|
|
9511
|
+
// Checkin if peer alive
|
|
9512
|
+
tap(() => console.debug('[network] Checking connection to pod...')), mergeMap(() => this.checkPeerAlive(this.peer)),
|
|
9513
|
+
// Filter to keep only changes
|
|
9514
|
+
filter(info => !!info !== !!lastInfo), tap(info => lastInfo = info),
|
|
9515
|
+
// Check compatibility
|
|
9516
|
+
mergeMap((info) => this.checkPeerCompatible(info, { showToast: true })))
|
|
9517
|
+
.subscribe(alive => {
|
|
9518
|
+
if (alive && this.offline) {
|
|
9519
|
+
this.setForceOffline(false);
|
|
9520
|
+
// Restart the service (to force re auth)
|
|
9521
|
+
this.restart();
|
|
9522
|
+
}
|
|
9523
|
+
else if (!alive && this.online) {
|
|
9524
|
+
this.setForceOffline(true);
|
|
9525
|
+
// Stop the service
|
|
9526
|
+
this.stop();
|
|
9527
|
+
}
|
|
9528
|
+
});
|
|
9529
|
+
this._timerSubscription.add(() => console.debug('[network] Refresh timer stopped'));
|
|
9530
|
+
}
|
|
9534
9531
|
get(path, opts) {
|
|
9535
9532
|
return __awaiter(this, void 0, void 0, function* () {
|
|
9536
9533
|
let uri = path;
|
|
@@ -9582,7 +9579,7 @@ class NetworkService {
|
|
|
9582
9579
|
}
|
|
9583
9580
|
}
|
|
9584
9581
|
resetData() {
|
|
9585
|
-
this.
|
|
9582
|
+
this._data = null;
|
|
9586
9583
|
}
|
|
9587
9584
|
/**
|
|
9588
9585
|
* Get default peers, from environment
|
|
@@ -9599,7 +9596,7 @@ class NetworkService {
|
|
|
9599
9596
|
addListener(name, callback) {
|
|
9600
9597
|
this._listeners[name] = this._listeners[name] || [];
|
|
9601
9598
|
this._listeners[name].push(callback);
|
|
9602
|
-
// When
|
|
9599
|
+
// When unsubscribe, remove from the listener
|
|
9603
9600
|
return new Subscription(() => {
|
|
9604
9601
|
const index = this._listeners[name].indexOf(callback);
|
|
9605
9602
|
if (index !== -1) {
|
|
@@ -10142,31 +10139,32 @@ class EntityStore {
|
|
|
10142
10139
|
}
|
|
10143
10140
|
|
|
10144
10141
|
const APP_LOCAL_STORAGE_TYPE_POLICIES = new InjectionToken('localStorageTypePolicies');
|
|
10145
|
-
|
|
10142
|
+
;
|
|
10143
|
+
class EntitiesStorage extends StartableService {
|
|
10146
10144
|
constructor(platform, progressBarService, storage, environment, typePolicies) {
|
|
10145
|
+
super(platform);
|
|
10147
10146
|
this.platform = platform;
|
|
10148
10147
|
this.progressBarService = progressBarService;
|
|
10149
10148
|
this.storage = storage;
|
|
10150
10149
|
this.environment = environment;
|
|
10151
|
-
this._started = false;
|
|
10152
10150
|
this._subscription = new Subscription();
|
|
10153
|
-
this._stores = {};
|
|
10154
10151
|
this._$save = new EventEmitter(true);
|
|
10155
10152
|
this._dirty = false;
|
|
10156
10153
|
this._saving = false;
|
|
10157
|
-
this.onStart = new Subject();
|
|
10158
10154
|
this._typePolicies = typePolicies || {};
|
|
10155
|
+
this._saveTimerPeriod = environment.storageSavePeriodMs || 10000 /* = 10s */;
|
|
10156
|
+
this._data = {};
|
|
10159
10157
|
// For DEV only
|
|
10160
10158
|
this._debug = !environment.production;
|
|
10161
10159
|
if (this._debug)
|
|
10162
10160
|
console.debug('[entities-storage] Creating service');
|
|
10163
10161
|
}
|
|
10164
10162
|
get dirty() {
|
|
10165
|
-
return this._dirty || Object.
|
|
10163
|
+
return this._dirty || Object.values(this._data).some(store => store.dirty);
|
|
10166
10164
|
}
|
|
10167
10165
|
watchAll(entityName, variables, opts) {
|
|
10168
10166
|
// Make sure store is ready
|
|
10169
|
-
if (!this.
|
|
10167
|
+
if (!this.started) {
|
|
10170
10168
|
return defer(() => this.ready())
|
|
10171
10169
|
.pipe(switchMap(() => this.watchAll(entityName, variables, opts))); // Loop
|
|
10172
10170
|
}
|
|
@@ -10180,7 +10178,7 @@ class EntitiesStorage {
|
|
|
10180
10178
|
loadAll(entityName, variables, opts) {
|
|
10181
10179
|
return __awaiter(this, void 0, void 0, function* () {
|
|
10182
10180
|
// Make sure store is ready
|
|
10183
|
-
if (!this.
|
|
10181
|
+
if (!this.started)
|
|
10184
10182
|
yield this.ready();
|
|
10185
10183
|
try {
|
|
10186
10184
|
this.progressBarService.increase();
|
|
@@ -10243,7 +10241,8 @@ class EntitiesStorage {
|
|
|
10243
10241
|
return __awaiter(this, void 0, void 0, function* () {
|
|
10244
10242
|
if (!entity)
|
|
10245
10243
|
return; // skip
|
|
10246
|
-
|
|
10244
|
+
if (!this.started)
|
|
10245
|
+
yield this.ready();
|
|
10247
10246
|
try {
|
|
10248
10247
|
this.progressBarService.increase();
|
|
10249
10248
|
this._dirty = true;
|
|
@@ -10263,7 +10262,8 @@ class EntitiesStorage {
|
|
|
10263
10262
|
return __awaiter(this, void 0, void 0, function* () {
|
|
10264
10263
|
if (isEmptyArray(entities) && (!opts || opts.reset !== true))
|
|
10265
10264
|
return entities; // Skip (nothing to save)
|
|
10266
|
-
|
|
10265
|
+
if (!this.started)
|
|
10266
|
+
yield this.ready();
|
|
10267
10267
|
try {
|
|
10268
10268
|
this.progressBarService.increase();
|
|
10269
10269
|
this._dirty = true;
|
|
@@ -10279,13 +10279,15 @@ class EntitiesStorage {
|
|
|
10279
10279
|
return __awaiter(this, void 0, void 0, function* () {
|
|
10280
10280
|
if (!entity)
|
|
10281
10281
|
return undefined; // skip
|
|
10282
|
-
|
|
10282
|
+
if (!this.started)
|
|
10283
|
+
yield this.ready();
|
|
10283
10284
|
return this.deleteById(entity.id, Object.assign(Object.assign({}, opts), { entityName: opts && opts.entityName || this.detectEntityName(entity) }));
|
|
10284
10285
|
});
|
|
10285
10286
|
}
|
|
10286
10287
|
deleteById(id, opts) {
|
|
10287
10288
|
return __awaiter(this, void 0, void 0, function* () {
|
|
10288
|
-
|
|
10289
|
+
if (!this.started)
|
|
10290
|
+
yield this.ready();
|
|
10289
10291
|
if (!opts || isNilOrBlank(opts.entityName))
|
|
10290
10292
|
throw new Error('Missing argument \'opts\' or \'entityName\'');
|
|
10291
10293
|
//if (id >= 0) throw new Error('Invalid id a local entity (not a negative number): ' + id);
|
|
@@ -10311,7 +10313,8 @@ class EntitiesStorage {
|
|
|
10311
10313
|
}
|
|
10312
10314
|
deleteMany(ids, opts) {
|
|
10313
10315
|
return __awaiter(this, void 0, void 0, function* () {
|
|
10314
|
-
|
|
10316
|
+
if (!this.started)
|
|
10317
|
+
yield this.ready();
|
|
10315
10318
|
if (!opts || isNilOrBlank(opts.entityName))
|
|
10316
10319
|
throw new Error('Missing argument \'opts\' or \'opts.entityName\'');
|
|
10317
10320
|
try {
|
|
@@ -10339,7 +10342,8 @@ class EntitiesStorage {
|
|
|
10339
10342
|
return __awaiter(this, void 0, void 0, function* () {
|
|
10340
10343
|
if (!entity)
|
|
10341
10344
|
return undefined; // skip
|
|
10342
|
-
|
|
10345
|
+
if (!this.started)
|
|
10346
|
+
yield this.ready();
|
|
10343
10347
|
return this.deleteFromTrashById(entity.id, Object.assign(Object.assign({}, opts), { entityName: opts && opts.entityName || this.detectEntityName(entity) }));
|
|
10344
10348
|
});
|
|
10345
10349
|
}
|
|
@@ -10355,7 +10359,8 @@ class EntitiesStorage {
|
|
|
10355
10359
|
return __awaiter(this, void 0, void 0, function* () {
|
|
10356
10360
|
if (!entity)
|
|
10357
10361
|
return undefined; // skip
|
|
10358
|
-
|
|
10362
|
+
if (!this.started)
|
|
10363
|
+
yield this.ready();
|
|
10359
10364
|
const entityName = opts && opts.entityName || this.detectEntityName(entity);
|
|
10360
10365
|
// Delete entity by id, if exists
|
|
10361
10366
|
const entityStore = this.getEntityStore(entityName);
|
|
@@ -10372,7 +10377,8 @@ class EntitiesStorage {
|
|
|
10372
10377
|
}
|
|
10373
10378
|
moveManyToTrash(ids, opts) {
|
|
10374
10379
|
return __awaiter(this, void 0, void 0, function* () {
|
|
10375
|
-
|
|
10380
|
+
if (!this.started)
|
|
10381
|
+
yield this.ready();
|
|
10376
10382
|
if (!opts || isNilOrBlank(opts.entityName))
|
|
10377
10383
|
throw new Error('Missing argument \'opts.entityName\'');
|
|
10378
10384
|
const entityStore = this.getEntityStore(opts.entityName, { create: false });
|
|
@@ -10399,7 +10405,8 @@ class EntitiesStorage {
|
|
|
10399
10405
|
return __awaiter(this, void 0, void 0, function* () {
|
|
10400
10406
|
if (!entity)
|
|
10401
10407
|
return undefined; // skip
|
|
10402
|
-
|
|
10408
|
+
if (!this.started)
|
|
10409
|
+
yield this.ready();
|
|
10403
10410
|
const entityName = opts && opts.entityName || this.detectEntityName(entity);
|
|
10404
10411
|
const trashName = EntitiesStorage.TRASH_PREFIX + entityName;
|
|
10405
10412
|
this.getEntityStore(trashName).save(entity, opts);
|
|
@@ -10409,7 +10416,8 @@ class EntitiesStorage {
|
|
|
10409
10416
|
}
|
|
10410
10417
|
clearTrash(entityName) {
|
|
10411
10418
|
return __awaiter(this, void 0, void 0, function* () {
|
|
10412
|
-
|
|
10419
|
+
if (!this.started)
|
|
10420
|
+
yield this.ready();
|
|
10413
10421
|
const trashName = EntitiesStorage.TRASH_PREFIX + entityName;
|
|
10414
10422
|
const entityStore = this.getEntityStore(trashName, { create: false });
|
|
10415
10423
|
if (!entityStore)
|
|
@@ -10425,36 +10433,24 @@ class EntitiesStorage {
|
|
|
10425
10433
|
}
|
|
10426
10434
|
return Promise.resolve();
|
|
10427
10435
|
}
|
|
10428
|
-
|
|
10429
|
-
|
|
10430
|
-
|
|
10431
|
-
|
|
10432
|
-
|
|
10433
|
-
|
|
10434
|
-
if (this._startPromise)
|
|
10435
|
-
return this._startPromise;
|
|
10436
|
-
if (this._started)
|
|
10437
|
-
return Promise.resolve();
|
|
10438
|
-
const now = Date.now();
|
|
10439
|
-
console.info(`[entities-storage] Starting entity storage...`);
|
|
10440
|
-
// Restore sequences
|
|
10441
|
-
this._startPromise = this.restoreLocally()
|
|
10442
|
-
.then(() => {
|
|
10436
|
+
ngOnStart() {
|
|
10437
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
10438
|
+
const now = Date.now();
|
|
10439
|
+
console.info(`[entities-storage] Starting entity storage...`);
|
|
10440
|
+
// Restore sequences
|
|
10441
|
+
yield this.restoreLocally();
|
|
10443
10442
|
// Start a save timer
|
|
10444
|
-
this._subscription.add(merge(this._$save, timer(
|
|
10445
|
-
.pipe(
|
|
10443
|
+
this._subscription.add(merge(this._$save, timer(this._saveTimerPeriod, this._saveTimerPeriod))
|
|
10444
|
+
.pipe(
|
|
10445
|
+
// Avoid to many call (e.g. when $save AND timer are triggered
|
|
10446
|
+
throttleTime(this._saveTimerPeriod))
|
|
10446
10447
|
.subscribe(() => this.storeLocally()));
|
|
10447
|
-
this._started = true;
|
|
10448
|
-
this._startPromise = undefined;
|
|
10449
10448
|
console.info(`[entities-storage] Starting [OK] in ${Date.now() - now}ms`);
|
|
10450
|
-
|
|
10451
|
-
this.onStart.next();
|
|
10449
|
+
return this._data;
|
|
10452
10450
|
});
|
|
10453
|
-
return this._startPromise;
|
|
10454
10451
|
}
|
|
10455
|
-
|
|
10452
|
+
ngOnStop() {
|
|
10456
10453
|
return __awaiter(this, void 0, void 0, function* () {
|
|
10457
|
-
this._started = false;
|
|
10458
10454
|
this._subscription.unsubscribe();
|
|
10459
10455
|
this._subscription = new Subscription();
|
|
10460
10456
|
if (this.dirty) {
|
|
@@ -10462,22 +10458,15 @@ class EntitiesStorage {
|
|
|
10462
10458
|
}
|
|
10463
10459
|
});
|
|
10464
10460
|
}
|
|
10465
|
-
restart() {
|
|
10466
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
10467
|
-
if (this._started)
|
|
10468
|
-
yield this.stop();
|
|
10469
|
-
yield this.start();
|
|
10470
|
-
});
|
|
10471
|
-
}
|
|
10472
10461
|
/* -- protected methods -- */
|
|
10473
10462
|
getEntityStore(name, opts) {
|
|
10474
|
-
let store = this.
|
|
10463
|
+
let store = this._data[name];
|
|
10475
10464
|
if (!store && (!opts || opts.create !== false)) {
|
|
10476
10465
|
if (this._debug)
|
|
10477
10466
|
console.debug(`[entities-storage] Creating store ${name}`);
|
|
10478
10467
|
const typePolicy = this._typePolicies[name];
|
|
10479
10468
|
store = new EntityStore(name, this.storage, typePolicy);
|
|
10480
|
-
this.
|
|
10469
|
+
this._data[name] = store;
|
|
10481
10470
|
}
|
|
10482
10471
|
return store;
|
|
10483
10472
|
}
|
|
@@ -10520,12 +10509,12 @@ class EntitiesStorage {
|
|
|
10520
10509
|
this._saving = true;
|
|
10521
10510
|
this._dirty = false;
|
|
10522
10511
|
this.progressBarService.increase();
|
|
10523
|
-
const entityNames = this.
|
|
10512
|
+
const entityNames = this._data && Object.keys(this._data) || [];
|
|
10524
10513
|
const now = Date.now();
|
|
10525
10514
|
if (this._debug)
|
|
10526
10515
|
console.debug('[entities-storage] Persisting...');
|
|
10527
10516
|
let currentEntityName;
|
|
10528
|
-
return
|
|
10517
|
+
return chainPromises(entityNames.map(entityName => () => {
|
|
10529
10518
|
currentEntityName = entityName;
|
|
10530
10519
|
const entityStore = this.getEntityStore(entityName, { create: false });
|
|
10531
10520
|
if (!entityStore) {
|
|
@@ -10539,18 +10528,20 @@ class EntitiesStorage {
|
|
|
10539
10528
|
entityNames.splice(entityNames.findIndex(e => e === entityName), 1);
|
|
10540
10529
|
}
|
|
10541
10530
|
});
|
|
10542
|
-
}))
|
|
10531
|
+
}))
|
|
10532
|
+
.then(() => {
|
|
10543
10533
|
currentEntityName = undefined;
|
|
10544
10534
|
return isEmptyArray(entityNames) ?
|
|
10545
10535
|
this.storage.remove(ENTITIES_STORAGE_KEY_PREFIX) :
|
|
10546
10536
|
this.storage.set(ENTITIES_STORAGE_KEY_PREFIX, entityNames);
|
|
10547
|
-
})
|
|
10537
|
+
})
|
|
10538
|
+
.then(() => {
|
|
10548
10539
|
if (this._debug)
|
|
10549
10540
|
console.debug(`[entities-storage] Persisting [OK] ${entityNames.length} stores saved in ${Date.now() - now}ms...`);
|
|
10550
10541
|
this._saving = false;
|
|
10551
10542
|
this.progressBarService.decrease();
|
|
10552
|
-
})
|
|
10553
|
-
.
|
|
10543
|
+
})
|
|
10544
|
+
.catch(err => {
|
|
10554
10545
|
this._saving = false;
|
|
10555
10546
|
this.progressBarService.decrease();
|
|
10556
10547
|
if (currentEntityName) {
|
|
@@ -10559,8 +10550,8 @@ class EntitiesStorage {
|
|
|
10559
10550
|
else {
|
|
10560
10551
|
console.error(`[entities-storage] Error while persisting: ${err && err.message || err}`, err);
|
|
10561
10552
|
}
|
|
10562
|
-
|
|
10563
|
-
})
|
|
10553
|
+
throw err;
|
|
10554
|
+
});
|
|
10564
10555
|
});
|
|
10565
10556
|
}
|
|
10566
10557
|
}
|
|
@@ -10574,7 +10565,7 @@ EntitiesStorage.ctorParameters = () => [
|
|
|
10574
10565
|
{ type: Platform },
|
|
10575
10566
|
{ type: ProgressBarService },
|
|
10576
10567
|
{ type: Storage },
|
|
10577
|
-
{ type:
|
|
10568
|
+
{ type: Environment, decorators: [{ type: Inject, args: [ENVIRONMENT,] }] },
|
|
10578
10569
|
{ type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [APP_LOCAL_STORAGE_TYPE_POLICIES,] }] }
|
|
10579
10570
|
];
|
|
10580
10571
|
|
|
@@ -11045,8 +11036,9 @@ function restoreTrackedQueries(opts) {
|
|
|
11045
11036
|
}
|
|
11046
11037
|
|
|
11047
11038
|
const APP_GRAPHQL_TYPE_POLICIES = new InjectionToken('graphqlTypePolicies');
|
|
11048
|
-
class GraphqlService {
|
|
11039
|
+
class GraphqlService extends StartableService {
|
|
11049
11040
|
constructor(platform, apollo, httpLink, network, storage, cryptoService, environment, typePolicies) {
|
|
11041
|
+
super(platform); // Wait network
|
|
11050
11042
|
this.platform = platform;
|
|
11051
11043
|
this.apollo = apollo;
|
|
11052
11044
|
this.httpLink = httpLink;
|
|
@@ -11055,21 +11047,12 @@ class GraphqlService {
|
|
|
11055
11047
|
this.cryptoService = cryptoService;
|
|
11056
11048
|
this.environment = environment;
|
|
11057
11049
|
this.typePolicies = typePolicies;
|
|
11058
|
-
this._started = false;
|
|
11059
11050
|
this._subscription = new Subscription();
|
|
11060
11051
|
this.connectionParams = {};
|
|
11061
11052
|
this.onNetworkError = new Subject();
|
|
11062
11053
|
this.customErrors = {};
|
|
11063
|
-
this.onStart = new Subject();
|
|
11064
11054
|
this._debug = !environment.production;
|
|
11065
11055
|
this._defaultFetchPolicy = environment.apolloFetchPolicy;
|
|
11066
|
-
// Restart if network restart
|
|
11067
|
-
this.network.on('start', () => this.restart());
|
|
11068
|
-
// Clear cache
|
|
11069
|
-
this.network.on('resetCache', () => __awaiter(this, void 0, void 0, function* () {
|
|
11070
|
-
yield this.ready();
|
|
11071
|
-
yield this.clearCache();
|
|
11072
|
-
}));
|
|
11073
11056
|
// Listen network status
|
|
11074
11057
|
this._networkStatusChanged$ = network.onNetworkStatusChanges
|
|
11075
11058
|
.pipe(filter(isNotNil), distinctUntilChanged());
|
|
@@ -11078,11 +11061,8 @@ class GraphqlService {
|
|
|
11078
11061
|
.pipe(throttleTime(300), filter(() => this.network.online), mergeMap(() => this.network.checkPeerAlive()), filter(alive => !alive))
|
|
11079
11062
|
.subscribe(() => this.network.setForceOffline(true, { showToast: true }));
|
|
11080
11063
|
}
|
|
11081
|
-
get started() {
|
|
11082
|
-
return this._started;
|
|
11083
|
-
}
|
|
11084
11064
|
get client() {
|
|
11085
|
-
return this.
|
|
11065
|
+
return this._data;
|
|
11086
11066
|
}
|
|
11087
11067
|
get cache() {
|
|
11088
11068
|
return this.apollo.client.cache;
|
|
@@ -11090,33 +11070,6 @@ class GraphqlService {
|
|
|
11090
11070
|
get defaultFetchPolicy() {
|
|
11091
11071
|
return this._defaultFetchPolicy;
|
|
11092
11072
|
}
|
|
11093
|
-
ready() {
|
|
11094
|
-
if (this._started)
|
|
11095
|
-
return Promise.resolve();
|
|
11096
|
-
return this.start();
|
|
11097
|
-
}
|
|
11098
|
-
start() {
|
|
11099
|
-
if (this._startPromise)
|
|
11100
|
-
return this._startPromise;
|
|
11101
|
-
if (this._started)
|
|
11102
|
-
return Promise.resolve();
|
|
11103
|
-
console.info('[graphql] Starting graphql...');
|
|
11104
|
-
// Waiting for network service
|
|
11105
|
-
this._startPromise = this.network.ready()
|
|
11106
|
-
.then(() => this.initApollo())
|
|
11107
|
-
.then(() => {
|
|
11108
|
-
this._started = true;
|
|
11109
|
-
this._startPromise = undefined;
|
|
11110
|
-
// Emit event
|
|
11111
|
-
this.onStart.next();
|
|
11112
|
-
console.info('[graphql] Starting graphql [OK]');
|
|
11113
|
-
})
|
|
11114
|
-
.catch((err) => {
|
|
11115
|
-
console.error(err && err.message || err, err);
|
|
11116
|
-
this._startPromise = undefined;
|
|
11117
|
-
});
|
|
11118
|
-
return this._startPromise;
|
|
11119
|
-
}
|
|
11120
11073
|
setAuthToken(token) {
|
|
11121
11074
|
if (token) {
|
|
11122
11075
|
console.debug('[graphql] Apply token authentication to headers');
|
|
@@ -11149,22 +11102,22 @@ class GraphqlService {
|
|
|
11149
11102
|
*/
|
|
11150
11103
|
addResolver(resolvers) {
|
|
11151
11104
|
return __awaiter(this, void 0, void 0, function* () {
|
|
11152
|
-
if (!this.
|
|
11153
|
-
|
|
11154
|
-
|
|
11155
|
-
const client = this.apollo.getClient();
|
|
11156
|
-
client.addResolvers(resolvers);
|
|
11105
|
+
if (!this.started)
|
|
11106
|
+
yield this.ready();
|
|
11107
|
+
this.apollo.client.addResolvers(resolvers);
|
|
11157
11108
|
});
|
|
11158
11109
|
}
|
|
11159
11110
|
query(opts) {
|
|
11160
11111
|
return __awaiter(this, void 0, void 0, function* () {
|
|
11112
|
+
if (!this.started)
|
|
11113
|
+
yield this.ready();
|
|
11161
11114
|
let res;
|
|
11162
11115
|
try {
|
|
11163
|
-
res = yield
|
|
11116
|
+
res = yield this.client.query({
|
|
11164
11117
|
query: opts.query,
|
|
11165
11118
|
variables: opts.variables,
|
|
11166
11119
|
fetchPolicy: opts.fetchPolicy || this._defaultFetchPolicy || undefined
|
|
11167
|
-
})
|
|
11120
|
+
});
|
|
11168
11121
|
}
|
|
11169
11122
|
catch (err) {
|
|
11170
11123
|
res = this.toApolloError(err, opts.error);
|
|
@@ -11504,10 +11457,11 @@ class GraphqlService {
|
|
|
11504
11457
|
}
|
|
11505
11458
|
clearCache(client) {
|
|
11506
11459
|
return __awaiter(this, void 0, void 0, function* () {
|
|
11507
|
-
client = (client || this.
|
|
11460
|
+
client = (client || this.client);
|
|
11508
11461
|
if (client) {
|
|
11509
|
-
const now = this._debug && Date.now();
|
|
11510
11462
|
console.info('[graphql] Clearing Apollo client\'s cache... ');
|
|
11463
|
+
const now = this._debug && Date.now();
|
|
11464
|
+
// Clearing the cache
|
|
11511
11465
|
yield client.cache.reset();
|
|
11512
11466
|
if (this._debug)
|
|
11513
11467
|
console.debug(`[graphql] Apollo client's cache cleared, in ${Date.now() - now}ms`);
|
|
@@ -11518,8 +11472,10 @@ class GraphqlService {
|
|
|
11518
11472
|
this.customErrors = Object.assign(Object.assign({}, this.customErrors), error);
|
|
11519
11473
|
}
|
|
11520
11474
|
/* -- protected methods -- */
|
|
11521
|
-
|
|
11475
|
+
ngOnStart() {
|
|
11522
11476
|
return __awaiter(this, void 0, void 0, function* () {
|
|
11477
|
+
yield this.network.ready();
|
|
11478
|
+
console.info('[graphql] Starting graphql...');
|
|
11523
11479
|
const mobile = this.platform.is('mobile') || this.platform.is('mobileweb');
|
|
11524
11480
|
const enableTrackMutationQueries = !mobile;
|
|
11525
11481
|
const peer = this.network.peer;
|
|
@@ -11654,24 +11610,20 @@ class GraphqlService {
|
|
|
11654
11610
|
console.error('[graphql] Failed to restore tracked queries from storage: ' + (err && err.message || err), err);
|
|
11655
11611
|
}
|
|
11656
11612
|
}
|
|
11613
|
+
// Listen for network restart
|
|
11614
|
+
this._subscription.add(this.network.on('start', () => this.restart()));
|
|
11615
|
+
// Listen for clear cache request, from network
|
|
11616
|
+
this._subscription.add(this.network.on('resetCache', () => this.clearCache()));
|
|
11617
|
+
console.info('[graphql] Starting graphql [OK]');
|
|
11618
|
+
return client;
|
|
11657
11619
|
});
|
|
11658
11620
|
}
|
|
11659
|
-
|
|
11621
|
+
ngOnStop() {
|
|
11660
11622
|
return __awaiter(this, void 0, void 0, function* () {
|
|
11661
11623
|
console.info('[graphql] Stopping graphql service...');
|
|
11662
11624
|
this._subscription.unsubscribe();
|
|
11663
11625
|
this._subscription = new Subscription();
|
|
11664
11626
|
yield this.resetClient();
|
|
11665
|
-
this._started = false;
|
|
11666
|
-
this._startPromise = undefined;
|
|
11667
|
-
});
|
|
11668
|
-
}
|
|
11669
|
-
restart() {
|
|
11670
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
11671
|
-
if (this.started) {
|
|
11672
|
-
return this.stop().then(() => this.start());
|
|
11673
|
-
}
|
|
11674
|
-
return this.start();
|
|
11675
11627
|
});
|
|
11676
11628
|
}
|
|
11677
11629
|
resetClient(client) {
|
|
@@ -11767,15 +11719,6 @@ class GraphqlService {
|
|
|
11767
11719
|
}
|
|
11768
11720
|
return undefined;
|
|
11769
11721
|
}
|
|
11770
|
-
getApollo() {
|
|
11771
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
11772
|
-
if (!this._started) {
|
|
11773
|
-
console.debug('[graphql] Waiting apollo client... ');
|
|
11774
|
-
yield this.onStart.toPromise();
|
|
11775
|
-
}
|
|
11776
|
-
return this.apollo;
|
|
11777
|
-
});
|
|
11778
|
-
}
|
|
11779
11722
|
}
|
|
11780
11723
|
GraphqlService.ɵprov = i0.ɵɵdefineInjectable({ factory: function GraphqlService_Factory() { return new GraphqlService(i0.ɵɵinject(i1$4.Platform), i0.ɵɵinject(i2$3.Apollo), i0.ɵɵinject(i3$2.HttpLink), i0.ɵɵinject(NetworkService), i0.ɵɵinject(i3$1.Storage), i0.ɵɵinject(CryptoService), i0.ɵɵinject(ENVIRONMENT), i0.ɵɵinject(APP_GRAPHQL_TYPE_POLICIES, 8)); }, token: GraphqlService, providedIn: "root" });
|
|
11781
11724
|
GraphqlService.decorators = [
|
|
@@ -12119,7 +12062,12 @@ class AccountService extends BaseGraphqlService {
|
|
|
12119
12062
|
this.storage = storage;
|
|
12120
12063
|
this.file = file;
|
|
12121
12064
|
this.environment = environment;
|
|
12122
|
-
this.
|
|
12065
|
+
this.onLogin = new Subject();
|
|
12066
|
+
this.onLogout = new Subject();
|
|
12067
|
+
this.onChange = new Subject();
|
|
12068
|
+
this.onAuthTokenChange = new Subject();
|
|
12069
|
+
this.onAuthBasicChange = new Subject();
|
|
12070
|
+
this._data = {
|
|
12123
12071
|
loaded: false,
|
|
12124
12072
|
keypair: null,
|
|
12125
12073
|
authToken: null,
|
|
@@ -12133,11 +12081,6 @@ class AccountService extends BaseGraphqlService {
|
|
|
12133
12081
|
this._started = false;
|
|
12134
12082
|
this._$additionalFields = new BehaviorSubject([]);
|
|
12135
12083
|
this._tokenType$ = new BehaviorSubject(undefined);
|
|
12136
|
-
this.onLogin = new Subject();
|
|
12137
|
-
this.onLogout = new Subject();
|
|
12138
|
-
this.onChange = new Subject();
|
|
12139
|
-
this.onAuthTokenChange = new Subject();
|
|
12140
|
-
this.onAuthBasicChange = new Subject();
|
|
12141
12084
|
this._debug = !environment.production;
|
|
12142
12085
|
if (this._debug)
|
|
12143
12086
|
console.debug('[account-service] Creating service');
|
|
@@ -12145,7 +12088,7 @@ class AccountService extends BaseGraphqlService {
|
|
|
12145
12088
|
// Send auth token to the graphql layer, when changed
|
|
12146
12089
|
this.onAuthTokenChange.subscribe((token) => this.graphql.setAuthToken(token));
|
|
12147
12090
|
this.onAuthBasicChange.subscribe((basic) => this.graphql.setAuthBasic(basic));
|
|
12148
|
-
// Listen
|
|
12091
|
+
// Listen graphql start (or restart)
|
|
12149
12092
|
this.graphql.onStart.subscribe(() => __awaiter(this, void 0, void 0, function* () {
|
|
12150
12093
|
if (!this._started) {
|
|
12151
12094
|
this.ready();
|
|
@@ -12167,19 +12110,19 @@ class AccountService extends BaseGraphqlService {
|
|
|
12167
12110
|
}));
|
|
12168
12111
|
}
|
|
12169
12112
|
get account() {
|
|
12170
|
-
return this.
|
|
12113
|
+
return this._data.loaded ? this._data.account : undefined;
|
|
12171
12114
|
}
|
|
12172
12115
|
get person() {
|
|
12173
|
-
if (this.
|
|
12174
|
-
this.
|
|
12116
|
+
if (this._data.loaded && !this._data.person) {
|
|
12117
|
+
this._data.person = this._data.loaded ? this._data.account.asPerson() : undefined;
|
|
12175
12118
|
}
|
|
12176
|
-
return this.
|
|
12119
|
+
return this._data.person;
|
|
12177
12120
|
}
|
|
12178
12121
|
get department() {
|
|
12179
|
-
if (this.
|
|
12180
|
-
this.
|
|
12122
|
+
if (this._data.loaded && !this._data.department) {
|
|
12123
|
+
this._data.department = this._data.loaded ? this._data.account.asPerson().department : undefined;
|
|
12181
12124
|
}
|
|
12182
|
-
return this.
|
|
12125
|
+
return this._data.department;
|
|
12183
12126
|
}
|
|
12184
12127
|
get tokenType() {
|
|
12185
12128
|
return this._tokenType$.value;
|
|
@@ -12189,28 +12132,17 @@ class AccountService extends BaseGraphqlService {
|
|
|
12189
12132
|
console.info('[account] Using authentication token type: ' + value);
|
|
12190
12133
|
this._tokenType$.next(value);
|
|
12191
12134
|
// Reset values
|
|
12192
|
-
this.
|
|
12135
|
+
this._data.authToken = undefined;
|
|
12193
12136
|
this.onAuthTokenChange.next(undefined);
|
|
12194
|
-
this.
|
|
12137
|
+
this._data.authBasic = undefined;
|
|
12195
12138
|
this.onAuthBasicChange.next(undefined);
|
|
12196
12139
|
}
|
|
12197
12140
|
}
|
|
12198
|
-
resetData() {
|
|
12199
|
-
this.data.loaded = false;
|
|
12200
|
-
this.data.keypair = null;
|
|
12201
|
-
this.data.authToken = null;
|
|
12202
|
-
this.data.authBasic = null;
|
|
12203
|
-
this.data.pubkey = null;
|
|
12204
|
-
this.data.mainProfile = null;
|
|
12205
|
-
this.data.account = new Account();
|
|
12206
|
-
this.data.person = null;
|
|
12207
|
-
this.data.department = null;
|
|
12208
|
-
}
|
|
12209
12141
|
start() {
|
|
12210
12142
|
if (this._startPromise)
|
|
12211
12143
|
return this._startPromise;
|
|
12212
12144
|
if (this._started)
|
|
12213
|
-
return Promise.resolve();
|
|
12145
|
+
return Promise.resolve(this.account);
|
|
12214
12146
|
// Restoring local settings
|
|
12215
12147
|
this._startPromise = Promise.all([
|
|
12216
12148
|
this.settings.ready(),
|
|
@@ -12221,6 +12153,7 @@ class AccountService extends BaseGraphqlService {
|
|
|
12221
12153
|
.then(() => {
|
|
12222
12154
|
this._started = true;
|
|
12223
12155
|
this._startPromise = undefined;
|
|
12156
|
+
return this.account;
|
|
12224
12157
|
});
|
|
12225
12158
|
return this._startPromise;
|
|
12226
12159
|
}
|
|
@@ -12232,8 +12165,8 @@ class AccountService extends BaseGraphqlService {
|
|
|
12232
12165
|
if (this._started || this._startPromise) {
|
|
12233
12166
|
this._started = false;
|
|
12234
12167
|
this._startPromise = undefined;
|
|
12235
|
-
const hadAuthToken = this.
|
|
12236
|
-
const hadAuthBasic = this.
|
|
12168
|
+
const hadAuthToken = this._data.authToken && true;
|
|
12169
|
+
const hadAuthBasic = this._data.authBasic && true;
|
|
12237
12170
|
const hadAuth = hadAuthToken || hadAuthBasic;
|
|
12238
12171
|
this.resetData();
|
|
12239
12172
|
if (hadAuth) {
|
|
@@ -12255,35 +12188,35 @@ class AccountService extends BaseGraphqlService {
|
|
|
12255
12188
|
}
|
|
12256
12189
|
ready() {
|
|
12257
12190
|
if (this._started)
|
|
12258
|
-
return Promise.resolve();
|
|
12191
|
+
return Promise.resolve(this.account);
|
|
12259
12192
|
return this.start();
|
|
12260
12193
|
}
|
|
12261
12194
|
isLogin() {
|
|
12262
|
-
return !!(this.
|
|
12195
|
+
return !!(this._data.pubkey && this._data.loaded);
|
|
12263
12196
|
}
|
|
12264
12197
|
isAuth() {
|
|
12265
|
-
return !!(this.
|
|
12198
|
+
return !!(this._data.pubkey && this._data.keypair && this._data.keypair.secretKey);
|
|
12266
12199
|
}
|
|
12267
12200
|
hasMinProfile(userProfile) {
|
|
12268
12201
|
// should be login, and status ENABLE or TEMPORARY
|
|
12269
|
-
if (!this.
|
|
12270
|
-
(this.
|
|
12202
|
+
if (!this._data.account || !this._data.account.pubkey ||
|
|
12203
|
+
(this._data.account.statusId !== StatusIds.ENABLE && this._data.account.statusId !== StatusIds.TEMPORARY)) {
|
|
12271
12204
|
return false;
|
|
12272
12205
|
}
|
|
12273
|
-
return PersonUtils.hasUpperOrEqualsProfile(this.
|
|
12206
|
+
return PersonUtils.hasUpperOrEqualsProfile(this._data.account.profiles, userProfile);
|
|
12274
12207
|
}
|
|
12275
12208
|
hasExactProfile(label) {
|
|
12276
12209
|
// should be login, and status ENABLE or TEMPORARY
|
|
12277
|
-
if (!this.
|
|
12278
|
-
(this.
|
|
12210
|
+
if (!this._data.account || !this._data.account.pubkey ||
|
|
12211
|
+
(this._data.account.statusId !== StatusIds.ENABLE && this._data.account.statusId !== StatusIds.TEMPORARY))
|
|
12279
12212
|
return false;
|
|
12280
|
-
return this.
|
|
12213
|
+
return this._data.account.profiles.some(profile => profile === label);
|
|
12281
12214
|
}
|
|
12282
12215
|
hasProfileAndIsEnable(userProfile) {
|
|
12283
12216
|
// should be login, and status ENABLE
|
|
12284
|
-
if (!this.
|
|
12217
|
+
if (!this._data.account || !this._data.account.pubkey || this._data.account.statusId !== StatusIds.ENABLE)
|
|
12285
12218
|
return false;
|
|
12286
|
-
return PersonUtils.hasUpperOrEqualsProfile(this.
|
|
12219
|
+
return PersonUtils.hasUpperOrEqualsProfile(this._data.account.profiles, userProfile);
|
|
12287
12220
|
}
|
|
12288
12221
|
isAdmin() {
|
|
12289
12222
|
return this.hasProfileAndIsEnable('ADMIN');
|
|
@@ -12303,11 +12236,11 @@ class AccountService extends BaseGraphqlService {
|
|
|
12303
12236
|
}
|
|
12304
12237
|
isOnlyGuest() {
|
|
12305
12238
|
// Should be login, and status ENABLE or TEMPORARY
|
|
12306
|
-
if (!this.
|
|
12307
|
-
(this.
|
|
12239
|
+
if (!this._data.account || !this._data.account.pubkey ||
|
|
12240
|
+
(this._data.account.statusId !== StatusIds.ENABLE && this._data.account.statusId !== StatusIds.TEMPORARY))
|
|
12308
12241
|
return false;
|
|
12309
12242
|
// Profile less then user
|
|
12310
|
-
return !PersonUtils.hasUpperOrEqualsProfile(this.
|
|
12243
|
+
return !PersonUtils.hasUpperOrEqualsProfile(this._data.account.profiles, 'USER');
|
|
12311
12244
|
}
|
|
12312
12245
|
canUserWriteDataForDepartment(recorderDepartment) {
|
|
12313
12246
|
if (ReferentialUtils.isEmpty(recorderDepartment)) {
|
|
@@ -12316,17 +12249,17 @@ class AccountService extends BaseGraphqlService {
|
|
|
12316
12249
|
return this.isAdmin();
|
|
12317
12250
|
}
|
|
12318
12251
|
// Should be login, and status ENABLE
|
|
12319
|
-
if (!this.
|
|
12252
|
+
if (!this._data.account || !this._data.account.pubkey || this._data.account.statusId !== StatusIds.ENABLE)
|
|
12320
12253
|
return false;
|
|
12321
|
-
if (!this.
|
|
12254
|
+
if (!this._data.account.department || !this._data.account.department.id) {
|
|
12322
12255
|
console.warn('User account has no department ! Unable to check write right against recorderDepartment');
|
|
12323
12256
|
return false;
|
|
12324
12257
|
}
|
|
12325
12258
|
// Same recorder department: OK, user can write
|
|
12326
|
-
if (this.
|
|
12259
|
+
if (this._data.account.department.id === recorderDepartment.id)
|
|
12327
12260
|
return true;
|
|
12328
12261
|
// Else, check if supervisor (or more)
|
|
12329
|
-
return PersonUtils.hasUpperOrEqualsProfile(this.
|
|
12262
|
+
return PersonUtils.hasUpperOrEqualsProfile(this._data.account.profiles, 'SUPERVISOR');
|
|
12330
12263
|
}
|
|
12331
12264
|
register(data) {
|
|
12332
12265
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -12337,7 +12270,7 @@ class AccountService extends BaseGraphqlService {
|
|
|
12337
12270
|
throw new Error('Missing required username or password');
|
|
12338
12271
|
if (this._debug)
|
|
12339
12272
|
console.debug('[account] Register new user account...', data.account);
|
|
12340
|
-
this.
|
|
12273
|
+
this._data.loaded = false;
|
|
12341
12274
|
const now = Date.now();
|
|
12342
12275
|
try {
|
|
12343
12276
|
const keypair = yield this.cryptoService.scryptKeypair(data.username, data.password);
|
|
@@ -12347,22 +12280,22 @@ class AccountService extends BaseGraphqlService {
|
|
|
12347
12280
|
data.account.settings.locale = this.settings.locale;
|
|
12348
12281
|
data.account.settings.latLongFormat = this.settings.latLongFormat;
|
|
12349
12282
|
data.account.department.id = data.account.department.id || this.environment.defaultDepartmentId;
|
|
12350
|
-
this.
|
|
12283
|
+
this._data.keypair = keypair;
|
|
12351
12284
|
const account = yield this.saveRemotely(data.account, keypair);
|
|
12352
12285
|
// Default values
|
|
12353
12286
|
account.avatar = account.avatar || (this.environment.baseUrl + DEFAULT_AVATAR_IMAGE);
|
|
12354
|
-
this.
|
|
12355
|
-
this.
|
|
12356
|
-
this.
|
|
12287
|
+
this._data.mainProfile = PersonUtils.getMainProfile(account.profiles);
|
|
12288
|
+
this._data.account = account;
|
|
12289
|
+
this._data.pubkey = account.pubkey;
|
|
12357
12290
|
// Try to auth on pod
|
|
12358
12291
|
yield this.authenticate(data);
|
|
12359
|
-
this.
|
|
12292
|
+
this._data.loaded = true;
|
|
12360
12293
|
yield this.saveLocally();
|
|
12361
12294
|
console.debug(`[account] Account successfully registered in ${Date.now() - now}ms`);
|
|
12362
12295
|
// Emit events
|
|
12363
|
-
this.onLogin.next(this.
|
|
12364
|
-
this.onChange.next(this.
|
|
12365
|
-
return this.
|
|
12296
|
+
this.onLogin.next(this._data.account);
|
|
12297
|
+
this.onChange.next(this._data.account);
|
|
12298
|
+
return this._data.account;
|
|
12366
12299
|
}
|
|
12367
12300
|
catch (error) {
|
|
12368
12301
|
console.error(error && error.message || error);
|
|
@@ -12378,20 +12311,20 @@ class AccountService extends BaseGraphqlService {
|
|
|
12378
12311
|
// Basic auth
|
|
12379
12312
|
if (tokenType === 'basic' || tokenType === 'basic-and-token') {
|
|
12380
12313
|
// Generate the authBasic, if used
|
|
12381
|
-
if (!this.
|
|
12314
|
+
if (!this._data.authBasic) {
|
|
12382
12315
|
// Skip if token already provided
|
|
12383
|
-
if (!(this.
|
|
12316
|
+
if (!(this._data.authToken && tokenType === 'basic-and-token')) {
|
|
12384
12317
|
if (!data || !data.username || !data.password)
|
|
12385
12318
|
throw new Error('Missing username and password');
|
|
12386
|
-
this.
|
|
12319
|
+
this._data.authBasic = this.cryptoService.encodeBase64(`${data.username}:${data.password}`);
|
|
12387
12320
|
}
|
|
12388
12321
|
}
|
|
12389
|
-
this.onAuthBasicChange.next(this.
|
|
12322
|
+
this.onAuthBasicChange.next(this._data.authBasic);
|
|
12390
12323
|
}
|
|
12391
12324
|
// Generate the authToken, if used
|
|
12392
12325
|
if (tokenType === 'token' || tokenType === 'basic-and-token') {
|
|
12393
12326
|
try {
|
|
12394
|
-
this.
|
|
12327
|
+
this._data.authToken = yield this.authenticateAndGetToken(this._data.authToken);
|
|
12395
12328
|
}
|
|
12396
12329
|
catch (error) {
|
|
12397
12330
|
// Never authenticate, or not ready for offline mode => exit
|
|
@@ -12402,7 +12335,7 @@ class AccountService extends BaseGraphqlService {
|
|
|
12402
12335
|
}
|
|
12403
12336
|
// Forget authBasic, to switch to authToken
|
|
12404
12337
|
if (tokenType === 'basic-and-token') {
|
|
12405
|
-
this.
|
|
12338
|
+
this._data.authBasic = undefined;
|
|
12406
12339
|
this.onAuthBasicChange.next(undefined);
|
|
12407
12340
|
}
|
|
12408
12341
|
});
|
|
@@ -12422,18 +12355,18 @@ class AccountService extends BaseGraphqlService {
|
|
|
12422
12355
|
throw { code: ErrorCodes$2.UNKNOWN_ERROR, message: 'ERROR.SCRYPT_ERROR' };
|
|
12423
12356
|
}
|
|
12424
12357
|
// Store pubkey+keypair
|
|
12425
|
-
this.
|
|
12426
|
-
this.
|
|
12358
|
+
this._data.pubkey = Base58.encode(keypair.publicKey);
|
|
12359
|
+
this._data.keypair = keypair;
|
|
12427
12360
|
// Try to load previous token
|
|
12428
12361
|
let previousToken = yield this.storage.get(TOKEN_STORAGE_KEY);
|
|
12429
|
-
previousToken = previousToken && previousToken.startsWith(this.
|
|
12362
|
+
previousToken = previousToken && previousToken.startsWith(this._data.pubkey) && previousToken || null;
|
|
12430
12363
|
// Offline mode
|
|
12431
12364
|
const offline = this.settings.hasOfflineFeature() && (this.network.offline || data.offline === true);
|
|
12432
12365
|
if (offline) {
|
|
12433
|
-
this.
|
|
12366
|
+
this._data.authToken = previousToken;
|
|
12434
12367
|
// Make sure network if set as offline
|
|
12435
12368
|
this.network.setForceOffline(true, { showToast: false });
|
|
12436
|
-
console.info(`[account] Login [OK] {pubkey: ${this.
|
|
12369
|
+
console.info(`[account] Login [OK] {pubkey: ${this._data.pubkey.substr(0, 8)}}, {offline: true}`);
|
|
12437
12370
|
}
|
|
12438
12371
|
// Online mode: try to auth on pod
|
|
12439
12372
|
else {
|
|
@@ -12483,14 +12416,14 @@ class AccountService extends BaseGraphqlService {
|
|
|
12483
12416
|
throw error;
|
|
12484
12417
|
}
|
|
12485
12418
|
// Emit event to observers
|
|
12486
|
-
this.onLogin.next(this.
|
|
12487
|
-
this.onChange.next(this.
|
|
12488
|
-
return this.
|
|
12419
|
+
this.onLogin.next(this._data.account);
|
|
12420
|
+
this.onChange.next(this._data.account);
|
|
12421
|
+
return this._data.account;
|
|
12489
12422
|
});
|
|
12490
12423
|
}
|
|
12491
12424
|
refresh() {
|
|
12492
12425
|
return __awaiter(this, void 0, void 0, function* () {
|
|
12493
|
-
if (!this.
|
|
12426
|
+
if (!this._data.pubkey)
|
|
12494
12427
|
throw new Error('User not logged');
|
|
12495
12428
|
if (this.network.offline)
|
|
12496
12429
|
throw new Error('Cannot check account in offline mode');
|
|
@@ -12498,9 +12431,9 @@ class AccountService extends BaseGraphqlService {
|
|
|
12498
12431
|
yield this.saveLocally();
|
|
12499
12432
|
console.debug('[account] Successfully reload account');
|
|
12500
12433
|
// Emit login event to subscribers
|
|
12501
|
-
this.onLogin.next(this.
|
|
12502
|
-
this.onChange.next(this.
|
|
12503
|
-
return this.
|
|
12434
|
+
this.onLogin.next(this._data.account);
|
|
12435
|
+
this.onChange.next(this._data.account);
|
|
12436
|
+
return this._data.account;
|
|
12504
12437
|
});
|
|
12505
12438
|
}
|
|
12506
12439
|
/**
|
|
@@ -12510,29 +12443,29 @@ class AccountService extends BaseGraphqlService {
|
|
|
12510
12443
|
*/
|
|
12511
12444
|
save(account) {
|
|
12512
12445
|
return __awaiter(this, void 0, void 0, function* () {
|
|
12513
|
-
if (!this.
|
|
12446
|
+
if (!this._data.pubkey)
|
|
12514
12447
|
return Promise.reject('User not logged');
|
|
12515
|
-
if (this.
|
|
12448
|
+
if (this._data.pubkey !== account.pubkey)
|
|
12516
12449
|
return Promise.reject('Not user account');
|
|
12517
|
-
account = yield this.saveRemotely(account, this.
|
|
12450
|
+
account = yield this.saveRemotely(account, this._data.keypair);
|
|
12518
12451
|
// Set defaults
|
|
12519
12452
|
account.avatar = account.avatar || (this.environment.baseUrl + DEFAULT_AVATAR_IMAGE);
|
|
12520
|
-
this.
|
|
12521
|
-
this.
|
|
12522
|
-
this.
|
|
12453
|
+
this._data.mainProfile = PersonUtils.getMainProfile(account.profiles);
|
|
12454
|
+
this._data.account = account;
|
|
12455
|
+
this._data.loaded = true;
|
|
12523
12456
|
// Save locally (in storage)
|
|
12524
12457
|
yield this.saveLocally();
|
|
12525
12458
|
// Send event
|
|
12526
|
-
this.onLogin.next(this.
|
|
12527
|
-
this.onChange.next(this.
|
|
12528
|
-
return this.
|
|
12459
|
+
this.onLogin.next(this._data.account);
|
|
12460
|
+
this.onChange.next(this._data.account);
|
|
12461
|
+
return this._data.account;
|
|
12529
12462
|
});
|
|
12530
12463
|
}
|
|
12531
12464
|
logout() {
|
|
12532
12465
|
return __awaiter(this, void 0, void 0, function* () {
|
|
12533
|
-
const hadAuthToken = this.
|
|
12534
|
-
const hadAuthBasic = this.
|
|
12535
|
-
const pubkey = this.
|
|
12466
|
+
const hadAuthToken = this._data.authToken && true;
|
|
12467
|
+
const hadAuthBasic = this._data.authBasic && true;
|
|
12468
|
+
const pubkey = this._data && this._data.pubkey;
|
|
12536
12469
|
this.resetData();
|
|
12537
12470
|
if (!this.settings.hasOfflineFeature()) {
|
|
12538
12471
|
// Remove all data from the local storage
|
|
@@ -12585,9 +12518,9 @@ class AccountService extends BaseGraphqlService {
|
|
|
12585
12518
|
if (offline) {
|
|
12586
12519
|
json = yield this.storage.get(ACCOUNT_STORAGE_KEY);
|
|
12587
12520
|
json = json && (typeof json === 'string') && JSON.parse(json) || json;
|
|
12588
|
-
json = json && this.
|
|
12589
|
-
if (!json && this.
|
|
12590
|
-
json = yield this.storage.get(ACCOUNT_STORAGE_KEY + '#' + this.
|
|
12521
|
+
json = json && this._data.pubkey && (json.pubkey === this._data.pubkey) && json || null;
|
|
12522
|
+
if (!json && this._data.pubkey) {
|
|
12523
|
+
json = yield this.storage.get(ACCOUNT_STORAGE_KEY + '#' + this._data.pubkey);
|
|
12591
12524
|
json = json && (typeof json === 'string') && JSON.parse(json) || json;
|
|
12592
12525
|
}
|
|
12593
12526
|
}
|
|
@@ -12682,7 +12615,7 @@ class AccountService extends BaseGraphqlService {
|
|
|
12682
12615
|
});
|
|
12683
12616
|
}
|
|
12684
12617
|
listenChanges() {
|
|
12685
|
-
if (!this.
|
|
12618
|
+
if (!this._data.pubkey)
|
|
12686
12619
|
return Subscription.EMPTY;
|
|
12687
12620
|
const self = this;
|
|
12688
12621
|
console.debug('[account] [WS] Listening account changes');
|
|
@@ -12696,34 +12629,30 @@ class AccountService extends BaseGraphqlService {
|
|
|
12696
12629
|
message: 'ERROR.ACCOUNT.SUBSCRIBE_ACCOUNT_ERROR'
|
|
12697
12630
|
}
|
|
12698
12631
|
}).subscribe({
|
|
12699
|
-
next({ data }) {
|
|
12632
|
+
next: ({ data }) => __awaiter(this, void 0, void 0, function* () {
|
|
12700
12633
|
var _a;
|
|
12701
|
-
|
|
12702
|
-
|
|
12703
|
-
|
|
12704
|
-
|
|
12705
|
-
|
|
12706
|
-
|
|
12707
|
-
|
|
12708
|
-
|
|
12709
|
-
|
|
12710
|
-
|
|
12711
|
-
|
|
12712
|
-
|
|
12713
|
-
|
|
12714
|
-
|
|
12715
|
-
|
|
12716
|
-
|
|
12717
|
-
|
|
12718
|
-
|
|
12719
|
-
|
|
12720
|
-
|
|
12721
|
-
|
|
12722
|
-
|
|
12723
|
-
}
|
|
12724
|
-
});
|
|
12725
|
-
},
|
|
12726
|
-
complete() {
|
|
12634
|
+
if (!data)
|
|
12635
|
+
return;
|
|
12636
|
+
const existingUpdateDate = toDateISOString((_a = self._data.account) === null || _a === void 0 ? void 0 : _a.updateDate);
|
|
12637
|
+
if (existingUpdateDate !== data.updateDate) {
|
|
12638
|
+
console.debug(`[account] [WS] Detected update on {${data.updateDate}}`);
|
|
12639
|
+
yield self.refresh();
|
|
12640
|
+
}
|
|
12641
|
+
}),
|
|
12642
|
+
error: (err) => __awaiter(this, void 0, void 0, function* () {
|
|
12643
|
+
if (err && +err.code === ServerErrorCodes.NOT_FOUND) {
|
|
12644
|
+
console.info('[account] Account not exists anymore: force user to logout...', err);
|
|
12645
|
+
yield self.logout();
|
|
12646
|
+
}
|
|
12647
|
+
else if (err && +err.code === ServerErrorCodes.UNAUTHORIZED) {
|
|
12648
|
+
console.info('[account] Account not authorized: force user to logout...', err);
|
|
12649
|
+
yield self.logout();
|
|
12650
|
+
}
|
|
12651
|
+
else {
|
|
12652
|
+
console.warn('[account] [WS] Received error:', err);
|
|
12653
|
+
}
|
|
12654
|
+
}),
|
|
12655
|
+
complete: () => {
|
|
12727
12656
|
console.debug('[account] [WS] Completed');
|
|
12728
12657
|
}
|
|
12729
12658
|
});
|
|
@@ -12764,29 +12693,40 @@ class AccountService extends BaseGraphqlService {
|
|
|
12764
12693
|
this._$additionalFields.next(values.concat(field));
|
|
12765
12694
|
}
|
|
12766
12695
|
/* -- protected method -- */
|
|
12696
|
+
resetData() {
|
|
12697
|
+
this._data.loaded = false;
|
|
12698
|
+
this._data.keypair = null;
|
|
12699
|
+
this._data.authToken = null;
|
|
12700
|
+
this._data.authBasic = null;
|
|
12701
|
+
this._data.pubkey = null;
|
|
12702
|
+
this._data.mainProfile = null;
|
|
12703
|
+
this._data.account = new Account();
|
|
12704
|
+
this._data.person = null;
|
|
12705
|
+
this._data.department = null;
|
|
12706
|
+
}
|
|
12767
12707
|
loadData(opts) {
|
|
12768
12708
|
return __awaiter(this, void 0, void 0, function* () {
|
|
12769
|
-
if (!this.
|
|
12709
|
+
if (!this._data.pubkey)
|
|
12770
12710
|
throw new Error('User not logged');
|
|
12771
|
-
this.
|
|
12711
|
+
this._data.loaded = false;
|
|
12772
12712
|
try {
|
|
12773
|
-
|
|
12713
|
+
const account = (yield this.load(opts)) || new Account();
|
|
12774
12714
|
// Set defaults
|
|
12775
12715
|
account.avatar = account.avatar || (this.environment.baseUrl + DEFAULT_AVATAR_IMAGE);
|
|
12776
12716
|
account.settings = account.settings || new UserSettings();
|
|
12777
12717
|
account.settings.locale = account.settings.locale || this.settings.locale;
|
|
12778
12718
|
account.settings.latLongFormat = account.settings.latLongFormat || this.settings.latLongFormat || 'DDMM';
|
|
12779
12719
|
// Read main profile
|
|
12780
|
-
this.
|
|
12720
|
+
this._data.mainProfile = PersonUtils.getMainProfile(account.profiles);
|
|
12781
12721
|
// Update, instead of replace it
|
|
12782
|
-
if (this.
|
|
12783
|
-
this.
|
|
12722
|
+
if (this._data.account) {
|
|
12723
|
+
this._data.account.fromObject(account);
|
|
12784
12724
|
}
|
|
12785
12725
|
else {
|
|
12786
|
-
this.
|
|
12726
|
+
this._data.account = account;
|
|
12787
12727
|
}
|
|
12788
|
-
this.
|
|
12789
|
-
return this.
|
|
12728
|
+
this._data.loaded = true;
|
|
12729
|
+
return this._data.account;
|
|
12790
12730
|
}
|
|
12791
12731
|
catch (error) {
|
|
12792
12732
|
this.resetData();
|
|
@@ -12820,9 +12760,9 @@ class AccountService extends BaseGraphqlService {
|
|
|
12820
12760
|
return;
|
|
12821
12761
|
if (this._debug)
|
|
12822
12762
|
console.debug(`[account] Account restoration...`);
|
|
12823
|
-
this.
|
|
12824
|
-
this.
|
|
12825
|
-
this.
|
|
12763
|
+
this._data.authToken = token;
|
|
12764
|
+
this._data.pubkey = pubkey;
|
|
12765
|
+
this._data.keypair = seckey && {
|
|
12826
12766
|
publicKey: Base58.decode(pubkey),
|
|
12827
12767
|
secretKey: Base58.decode(seckey)
|
|
12828
12768
|
} || null;
|
|
@@ -12830,7 +12770,7 @@ class AccountService extends BaseGraphqlService {
|
|
|
12830
12770
|
if (this.network.online) {
|
|
12831
12771
|
try {
|
|
12832
12772
|
yield this.authenticate();
|
|
12833
|
-
if (!this.
|
|
12773
|
+
if (!this._data.authToken && !this._data.authBasic)
|
|
12834
12774
|
throw new Error('Authentication failed');
|
|
12835
12775
|
}
|
|
12836
12776
|
catch (error) {
|
|
@@ -12861,14 +12801,14 @@ class AccountService extends BaseGraphqlService {
|
|
|
12861
12801
|
// Transform to entity
|
|
12862
12802
|
const account = Account.fromObject(jsonAccount);
|
|
12863
12803
|
// Update data
|
|
12864
|
-
this.
|
|
12865
|
-
this.
|
|
12866
|
-
this.
|
|
12804
|
+
this._data.account = account;
|
|
12805
|
+
this._data.mainProfile = PersonUtils.getMainProfile(account.profiles);
|
|
12806
|
+
this._data.loaded = true;
|
|
12867
12807
|
// Emit event
|
|
12868
|
-
this.onLogin.next(this.
|
|
12869
|
-
this.onChange.next(this.
|
|
12808
|
+
this.onLogin.next(this._data.account);
|
|
12809
|
+
this.onChange.next(this._data.account);
|
|
12870
12810
|
if (this._debug)
|
|
12871
|
-
console.debug(`[account] Account restoration [OK] {pubkey: ${pubkey.substr(0, 8)}}, {profile: ${this.
|
|
12811
|
+
console.debug(`[account] Account restoration [OK] {pubkey: ${pubkey.substr(0, 8)}}, {profile: ${this._data.mainProfile}}`);
|
|
12872
12812
|
return account;
|
|
12873
12813
|
});
|
|
12874
12814
|
}
|
|
@@ -12877,13 +12817,13 @@ class AccountService extends BaseGraphqlService {
|
|
|
12877
12817
|
*/
|
|
12878
12818
|
saveLocally() {
|
|
12879
12819
|
return __awaiter(this, void 0, void 0, function* () {
|
|
12880
|
-
if (!this.
|
|
12820
|
+
if (!this._data.pubkey)
|
|
12881
12821
|
throw new Error('User not logged');
|
|
12882
12822
|
if (this._debug)
|
|
12883
|
-
console.debug(`[account] Saving account {${this.
|
|
12823
|
+
console.debug(`[account] Saving account {${this._data.pubkey.substring(0, 6)}} in local storage...`);
|
|
12884
12824
|
// Convert account to json
|
|
12885
|
-
const json = this.
|
|
12886
|
-
const seckey = this.
|
|
12825
|
+
const json = this._data.account.asObject({ keepTypename: true });
|
|
12826
|
+
const seckey = this._data.keypair && this._data.keypair.secretKey && Base58.encode(this._data.keypair.secretKey) || null;
|
|
12887
12827
|
// Convert avatar URL to dataUrl (e.g. 'data:image/png:<base64 content>')
|
|
12888
12828
|
const hasAvatarUrl = json.avatar && !json.avatar.endsWith(DEFAULT_AVATAR_IMAGE) &&
|
|
12889
12829
|
(json.avatar.startsWith('http://') || (json.avatar.startsWith('https://')));
|
|
@@ -12904,9 +12844,9 @@ class AccountService extends BaseGraphqlService {
|
|
|
12904
12844
|
}
|
|
12905
12845
|
try {
|
|
12906
12846
|
yield Promise.all([
|
|
12907
|
-
this.storage.set(PUBKEY_STORAGE_KEY, this.
|
|
12908
|
-
this.storage.set(TOKEN_STORAGE_KEY, this.
|
|
12909
|
-
this.storage.set(`${ACCOUNT_STORAGE_KEY}#${this.
|
|
12847
|
+
this.storage.set(PUBKEY_STORAGE_KEY, this._data.pubkey),
|
|
12848
|
+
this.storage.set(TOKEN_STORAGE_KEY, this._data.authToken),
|
|
12849
|
+
this.storage.set(`${ACCOUNT_STORAGE_KEY}#${this._data.pubkey}`, json),
|
|
12910
12850
|
// Secret key (optional)
|
|
12911
12851
|
seckey && this.storage.set(SECKEY_STORAGE_KEY, seckey) || this.storage.remove(SECKEY_STORAGE_KEY),
|
|
12912
12852
|
// Remove old storage key
|
|
@@ -12964,7 +12904,7 @@ class AccountService extends BaseGraphqlService {
|
|
|
12964
12904
|
}
|
|
12965
12905
|
authenticateAndGetToken(token, counter) {
|
|
12966
12906
|
return __awaiter(this, void 0, void 0, function* () {
|
|
12967
|
-
if (!this.
|
|
12907
|
+
if (!this._data.pubkey)
|
|
12968
12908
|
throw new Error('User not logged');
|
|
12969
12909
|
if (!counter)
|
|
12970
12910
|
console.info('[account] Authentication on pod...');
|
|
@@ -12990,7 +12930,7 @@ class AccountService extends BaseGraphqlService {
|
|
|
12990
12930
|
if (data && data.authenticate) {
|
|
12991
12931
|
// Store the token
|
|
12992
12932
|
this.onAuthTokenChange.next(token);
|
|
12993
|
-
console.info(`[account] Authentication on pod [OK] {pubkey: '${this.
|
|
12933
|
+
console.info(`[account] Authentication on pod [OK] {pubkey: '${this._data.pubkey.substr(0, 8)}'}`);
|
|
12994
12934
|
return token; // return the token
|
|
12995
12935
|
}
|
|
12996
12936
|
// Continue (will retry with another challenge)
|
|
@@ -13016,8 +12956,8 @@ class AccountService extends BaseGraphqlService {
|
|
|
13016
12956
|
}
|
|
13017
12957
|
// TODO: check server pubkey as a valid certificate
|
|
13018
12958
|
// Do the challenge
|
|
13019
|
-
const signature = yield this.cryptoService.sign(data.authChallenge.challenge, this.
|
|
13020
|
-
const newToken = `${this.
|
|
12959
|
+
const signature = yield this.cryptoService.sign(data.authChallenge.challenge, this._data.keypair);
|
|
12960
|
+
const newToken = `${this._data.pubkey}:${data.authChallenge.challenge}|${signature}`;
|
|
13021
12961
|
// iterate with the new token
|
|
13022
12962
|
return yield this.authenticateAndGetToken(newToken, (counter || 1) + 1 /* increment */);
|
|
13023
12963
|
});
|
|
@@ -13376,9 +13316,8 @@ class ConfigService extends BaseGraphqlService {
|
|
|
13376
13316
|
}
|
|
13377
13317
|
get config() {
|
|
13378
13318
|
// If first call: start loading
|
|
13379
|
-
if (!this._started)
|
|
13319
|
+
if (!this._started)
|
|
13380
13320
|
this.start();
|
|
13381
|
-
}
|
|
13382
13321
|
return this.$data.pipe(filter(isNotNil));
|
|
13383
13322
|
}
|
|
13384
13323
|
start() {
|
|
@@ -13389,30 +13328,38 @@ class ConfigService extends BaseGraphqlService {
|
|
|
13389
13328
|
console.info('[config] Starting configuration...');
|
|
13390
13329
|
this._startPromise = this.graphql.ready()
|
|
13391
13330
|
.then(() => this.loadOrRestoreLocally())
|
|
13392
|
-
.then(() => {
|
|
13331
|
+
.then((data) => {
|
|
13393
13332
|
this._started = true;
|
|
13394
13333
|
this._startPromise = undefined;
|
|
13334
|
+
this.$data.next(data);
|
|
13335
|
+
return data;
|
|
13395
13336
|
})
|
|
13396
13337
|
.catch((err) => {
|
|
13397
13338
|
console.error(err && err.message || err, err);
|
|
13339
|
+
this._started = false;
|
|
13398
13340
|
this._startPromise = undefined;
|
|
13341
|
+
return null;
|
|
13399
13342
|
});
|
|
13400
13343
|
return this._startPromise;
|
|
13401
13344
|
}
|
|
13402
13345
|
stop() {
|
|
13403
|
-
this
|
|
13404
|
-
|
|
13405
|
-
|
|
13406
|
-
|
|
13346
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
13347
|
+
this._subscription.unsubscribe();
|
|
13348
|
+
this._subscription = new Subscription();
|
|
13349
|
+
this._started = false;
|
|
13350
|
+
this._startPromise = undefined;
|
|
13351
|
+
});
|
|
13407
13352
|
}
|
|
13408
13353
|
restart() {
|
|
13409
|
-
|
|
13410
|
-
this.
|
|
13411
|
-
|
|
13354
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
13355
|
+
if (this.started)
|
|
13356
|
+
yield this.stop();
|
|
13357
|
+
return this.start();
|
|
13358
|
+
});
|
|
13412
13359
|
}
|
|
13413
13360
|
ready() {
|
|
13414
13361
|
if (this._started)
|
|
13415
|
-
return Promise.resolve();
|
|
13362
|
+
return Promise.resolve(this.$data.value);
|
|
13416
13363
|
if (this._startPromise)
|
|
13417
13364
|
return this._startPromise;
|
|
13418
13365
|
return this.start();
|
|
@@ -13467,7 +13414,7 @@ class ConfigService extends BaseGraphqlService {
|
|
|
13467
13414
|
console.debug('[config] Pod configuration saved!');
|
|
13468
13415
|
const reloadedConfig = yield this.loadDefault({ fetchPolicy: 'network-only' });
|
|
13469
13416
|
// If this is the default config
|
|
13470
|
-
const defaultConfig = this.$data.
|
|
13417
|
+
const defaultConfig = this.$data.value;
|
|
13471
13418
|
if (isNotNil(defaultConfig) && reloadedConfig.label === defaultConfig.label) {
|
|
13472
13419
|
// Emit update event when is default config
|
|
13473
13420
|
this.$data.next(reloadedConfig);
|
|
@@ -13544,7 +13491,7 @@ class ConfigService extends BaseGraphqlService {
|
|
|
13544
13491
|
if (wasJustLoaded) {
|
|
13545
13492
|
// TODO
|
|
13546
13493
|
}
|
|
13547
|
-
|
|
13494
|
+
return data;
|
|
13548
13495
|
});
|
|
13549
13496
|
}
|
|
13550
13497
|
restoreLocally() {
|
|
@@ -13723,6 +13670,9 @@ class PlatformService {
|
|
|
13723
13670
|
isAndroidCordova() {
|
|
13724
13671
|
return this._android && this._cordova;
|
|
13725
13672
|
}
|
|
13673
|
+
get canDownload() {
|
|
13674
|
+
return this._android && !!this.downloader;
|
|
13675
|
+
}
|
|
13726
13676
|
width() {
|
|
13727
13677
|
return this.platform.width();
|
|
13728
13678
|
}
|
|
@@ -13764,7 +13714,7 @@ class PlatformService {
|
|
|
13764
13714
|
.then(() => {
|
|
13765
13715
|
this._started = true;
|
|
13766
13716
|
this._startPromise = undefined;
|
|
13767
|
-
console.info(`[platform] Starting platform [OK] {mobile: ${this._mobile}, touchUi: ${this.touchUi}} in ${Date.now() - now}ms`);
|
|
13717
|
+
console.info(`[platform] Starting platform [OK] {mobile: ${this._mobile}, touchUi: ${this.touchUi}, canDownload: ${this.canDownload}} in ${Date.now() - now}ms`);
|
|
13768
13718
|
// Update cache configuration when network changed
|
|
13769
13719
|
this.networkService.onNetworkStatusChanges.subscribe((type) => this.configureCache(type !== 'none'));
|
|
13770
13720
|
// Update authentication type
|
|
@@ -13812,18 +13762,31 @@ class PlatformService {
|
|
|
13812
13762
|
}
|
|
13813
13763
|
}
|
|
13814
13764
|
download(request) {
|
|
13815
|
-
|
|
13816
|
-
|
|
13817
|
-
|
|
13818
|
-
|
|
13819
|
-
|
|
13820
|
-
|
|
13821
|
-
|
|
13822
|
-
|
|
13823
|
-
|
|
13824
|
-
|
|
13825
|
-
|
|
13826
|
-
|
|
13765
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
13766
|
+
if (!request || !request.uri)
|
|
13767
|
+
throw new Error('Missing argument \'request\' or \'request.uri\'');
|
|
13768
|
+
if (this._android && this.downloader) {
|
|
13769
|
+
request = Object.assign({ visibleInDownloadsUi: true, notificationVisibility: NotificationVisibility.VisibleNotifyCompleted }, request);
|
|
13770
|
+
try {
|
|
13771
|
+
console.debug('[platform] Downloading, using request: ' + JSON.stringify(request));
|
|
13772
|
+
const location = yield this.downloader.download(request);
|
|
13773
|
+
console.info('[platform] File successfully downloaded at:' + location);
|
|
13774
|
+
return location;
|
|
13775
|
+
}
|
|
13776
|
+
catch (err) {
|
|
13777
|
+
console.error('[platform] Unable to download: ' + (err && err.message || err));
|
|
13778
|
+
throw err;
|
|
13779
|
+
}
|
|
13780
|
+
}
|
|
13781
|
+
// Fallback (no Cordova)
|
|
13782
|
+
// Web mode: open URI using the browser
|
|
13783
|
+
// TODO: find a way to download under iOS (see https://www.c-sharpcorner.com/article/how-to-download-a-file-using-file-transfer-plugin-in-ionic-3/)
|
|
13784
|
+
else {
|
|
13785
|
+
console.warn('[platform] Cannot use Cordova downloader: using browser open()');
|
|
13786
|
+
this.open(request.uri, '_system', 'location=no', true);
|
|
13787
|
+
return undefined;
|
|
13788
|
+
}
|
|
13789
|
+
});
|
|
13827
13790
|
}
|
|
13828
13791
|
/* -- protected methods -- */
|
|
13829
13792
|
configureCordovaPlugins(mobile) {
|
|
@@ -13920,13 +13883,14 @@ class PlatformService {
|
|
|
13920
13883
|
storeName: forage.config().storeName,
|
|
13921
13884
|
driver: [forage.INDEXEDDB, forage.WEBSQL]
|
|
13922
13885
|
});
|
|
13923
|
-
// IF data stored in the OLD storage: start migration
|
|
13924
13886
|
const keys = yield oldForage.keys();
|
|
13887
|
+
// No data in the odl storage: skip migration
|
|
13925
13888
|
if (isEmptyArray(keys)) {
|
|
13926
|
-
// Drop the old instance
|
|
13927
|
-
console.
|
|
13889
|
+
// Drop the old instance (not need anymore)
|
|
13890
|
+
console.debug(`[platform] Old storage is empty: dropping unused instance {name: '${forage.config().name}', driver: '${oldForage.driver()}'}`);
|
|
13928
13891
|
yield oldForage.dropInstance();
|
|
13929
13892
|
}
|
|
13893
|
+
// IF some data stored in the OLD storage: start migration
|
|
13930
13894
|
else {
|
|
13931
13895
|
const now = Date.now();
|
|
13932
13896
|
console.info(`[platform] Starting storage migration...`);
|
|
@@ -18486,22 +18450,24 @@ const MimeTypes = Object.freeze({
|
|
|
18486
18450
|
ANDROID_APK: 'application/vnd.android.package-archive'
|
|
18487
18451
|
});
|
|
18488
18452
|
class AppInstallUpgradeCard {
|
|
18489
|
-
constructor(modalCtrl, configService,
|
|
18453
|
+
constructor(modalCtrl, configService, toastController, translate, cd, platform, network, environment) {
|
|
18490
18454
|
this.modalCtrl = modalCtrl;
|
|
18491
18455
|
this.configService = configService;
|
|
18492
|
-
this.
|
|
18456
|
+
this.toastController = toastController;
|
|
18457
|
+
this.translate = translate;
|
|
18493
18458
|
this.cd = cd;
|
|
18459
|
+
this.platform = platform;
|
|
18494
18460
|
this.network = network;
|
|
18495
18461
|
this.environment = environment;
|
|
18496
18462
|
this._subscription = new Subscription();
|
|
18497
18463
|
this._showUpdateOfflineFeature = false;
|
|
18498
18464
|
this.loading = true;
|
|
18465
|
+
this.downloading = false;
|
|
18499
18466
|
this.waitingNetwork = false;
|
|
18500
18467
|
this.showUpgradeWarning = true;
|
|
18501
18468
|
this.showOfflineWarning = true;
|
|
18502
18469
|
this.showInstallButton = false;
|
|
18503
18470
|
this.onUpdateOfflineModeClick = new EventEmitter();
|
|
18504
|
-
this.isAndroidCordova = platform.isAndroidCordova();
|
|
18505
18471
|
}
|
|
18506
18472
|
set showUpdateOfflineFeature(value) {
|
|
18507
18473
|
if (value === this._showUpdateOfflineFeature)
|
|
@@ -18518,18 +18484,8 @@ class AppInstallUpgradeCard {
|
|
|
18518
18484
|
this.offline = this.network.offline;
|
|
18519
18485
|
// Listen pod config
|
|
18520
18486
|
this._subscription.add(this.configService.config
|
|
18521
|
-
.
|
|
18522
|
-
|
|
18523
|
-
const installLinks = this.getAllInstallLinks(config);
|
|
18524
|
-
// Check for upgrade
|
|
18525
|
-
this.updateLinks = this.getCompatibleUpgradeLinks(installLinks, config);
|
|
18526
|
-
// Check for install links (if no upgrade need)
|
|
18527
|
-
this.installLinks = !this.updateLinks && this.getCompatibleInstallLinks(installLinks);
|
|
18528
|
-
setTimeout(() => {
|
|
18529
|
-
this.loading = false;
|
|
18530
|
-
this.markForCheck();
|
|
18531
|
-
}, 2000); // Add a delay, for animation
|
|
18532
|
-
}));
|
|
18487
|
+
.pipe(debounceTime(1000))
|
|
18488
|
+
.subscribe(config => this.checkNeedInstallOrUpdate(config)));
|
|
18533
18489
|
// Listen network changes
|
|
18534
18490
|
this._subscription.add(this.network.onNetworkStatusChanges
|
|
18535
18491
|
.pipe(
|
|
@@ -18537,34 +18493,70 @@ class AppInstallUpgradeCard {
|
|
|
18537
18493
|
//tap(() => this.waitingNetwork = false),
|
|
18538
18494
|
map(connectionType => connectionType === 'none'), distinctUntilChanged())
|
|
18539
18495
|
.subscribe(offline => {
|
|
18540
|
-
this.offline
|
|
18541
|
-
|
|
18496
|
+
if (this.offline !== offline) {
|
|
18497
|
+
this.offline = offline;
|
|
18498
|
+
this.markForCheck();
|
|
18499
|
+
}
|
|
18542
18500
|
}));
|
|
18543
18501
|
});
|
|
18544
18502
|
}
|
|
18545
18503
|
ngOnDestroy() {
|
|
18546
18504
|
this._subscription.unsubscribe();
|
|
18547
18505
|
}
|
|
18548
|
-
|
|
18549
|
-
|
|
18550
|
-
|
|
18551
|
-
|
|
18552
|
-
|
|
18553
|
-
|
|
18554
|
-
|
|
18506
|
+
download(event, link) {
|
|
18507
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18508
|
+
if (!link || !link.url) {
|
|
18509
|
+
console.error('[install-upgrade-card] Missing required argument \'link.url\'');
|
|
18510
|
+
return; // Skip
|
|
18511
|
+
}
|
|
18512
|
+
this.downloading = true;
|
|
18513
|
+
try {
|
|
18514
|
+
console.info('[install-upgrade-card] User ask download of: ' + link.url);
|
|
18515
|
+
const location = this.platform.download({
|
|
18516
|
+
uri: link.url,
|
|
18517
|
+
title: link.name,
|
|
18518
|
+
filename: link.downloadFilename,
|
|
18519
|
+
mimeType: link.mimeType
|
|
18520
|
+
});
|
|
18521
|
+
console.info('[install-upgrade-card] File downloaded at: ' + location);
|
|
18522
|
+
}
|
|
18523
|
+
// Failed: display a toast
|
|
18524
|
+
catch (err) {
|
|
18525
|
+
console.error('[install-upgrade] Download failed: ' + (err && err.message || err));
|
|
18526
|
+
this.showToast({
|
|
18527
|
+
message: 'ERROR.DOWNLOAD_FAILED',
|
|
18528
|
+
messageParams: { error: err },
|
|
18529
|
+
type: 'error',
|
|
18530
|
+
showCloseButton: true
|
|
18531
|
+
});
|
|
18532
|
+
return; // Stop here
|
|
18533
|
+
}
|
|
18534
|
+
finally {
|
|
18535
|
+
this.downloading = false;
|
|
18536
|
+
this.markForCheck();
|
|
18537
|
+
}
|
|
18538
|
+
this.showToast({
|
|
18539
|
+
message: 'INFO.DOWNLOAD_UPDATE_SUCCEED',
|
|
18540
|
+
type: 'info',
|
|
18541
|
+
showCloseButton: true
|
|
18542
|
+
});
|
|
18555
18543
|
});
|
|
18556
18544
|
}
|
|
18557
18545
|
tryOnline() {
|
|
18558
|
-
this
|
|
18559
|
-
|
|
18560
|
-
this.network.tryOnline({
|
|
18561
|
-
showLoadingToast: false,
|
|
18562
|
-
showOnlineToast: true,
|
|
18563
|
-
showOfflineToast: false
|
|
18564
|
-
})
|
|
18565
|
-
.then(() => {
|
|
18566
|
-
this.waitingNetwork = false;
|
|
18546
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18547
|
+
this.waitingNetwork = true;
|
|
18567
18548
|
this.markForCheck();
|
|
18549
|
+
try {
|
|
18550
|
+
yield this.network.tryOnline({
|
|
18551
|
+
showLoadingToast: false,
|
|
18552
|
+
showOnlineToast: true,
|
|
18553
|
+
showOfflineToast: false
|
|
18554
|
+
});
|
|
18555
|
+
}
|
|
18556
|
+
finally {
|
|
18557
|
+
this.waitingNetwork = false;
|
|
18558
|
+
this.markForCheck();
|
|
18559
|
+
}
|
|
18568
18560
|
});
|
|
18569
18561
|
}
|
|
18570
18562
|
getPlatformName(platform) {
|
|
@@ -18581,6 +18573,23 @@ class AppInstallUpgradeCard {
|
|
|
18581
18573
|
return value;
|
|
18582
18574
|
}
|
|
18583
18575
|
/* -- private method -- */
|
|
18576
|
+
checkNeedInstallOrUpdate(config) {
|
|
18577
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18578
|
+
console.info('[install] Check if need to install or upgrade...');
|
|
18579
|
+
try {
|
|
18580
|
+
const installLinks = this.getAllInstallLinks(config);
|
|
18581
|
+
// Check for upgrade
|
|
18582
|
+
this.updateLinks = this.getCompatibleUpgradeLinks(installLinks, config);
|
|
18583
|
+
// Check for install links (if no upgrade need)
|
|
18584
|
+
this.installLinks = !this.updateLinks && this.getCompatibleInstallLinks(installLinks);
|
|
18585
|
+
yield sleep(500); // Add a delay, for animation
|
|
18586
|
+
}
|
|
18587
|
+
finally {
|
|
18588
|
+
this.loading = false;
|
|
18589
|
+
this.markForCheck();
|
|
18590
|
+
}
|
|
18591
|
+
});
|
|
18592
|
+
}
|
|
18584
18593
|
getCompatibleInstallLinks(installLinks) {
|
|
18585
18594
|
// Cordova already running: not need to install
|
|
18586
18595
|
if (this.platform.is('cordova'))
|
|
@@ -18592,15 +18601,20 @@ class AppInstallUpgradeCard {
|
|
|
18592
18601
|
return undefined;
|
|
18593
18602
|
}
|
|
18594
18603
|
getCompatibleUpgradeLinks(installLinks, config) {
|
|
18595
|
-
const
|
|
18596
|
-
const
|
|
18604
|
+
const appVersion = this.environment.version;
|
|
18605
|
+
const appMinVersionFromPod = config.getProperty(CORE_CONFIG_OPTIONS.APP_MIN_VERSION);
|
|
18606
|
+
if (!appVersion) {
|
|
18607
|
+
console.error('Missing value for \'environment.version\': cannot check app compatibility!');
|
|
18608
|
+
return undefined;
|
|
18609
|
+
}
|
|
18610
|
+
const needUpgrade = appMinVersionFromPod && !VersionUtils.isCompatible(appMinVersionFromPod, appVersion);
|
|
18597
18611
|
if (!needUpgrade)
|
|
18598
18612
|
return undefined;
|
|
18599
18613
|
const upgradeLinks = installLinks
|
|
18600
18614
|
.filter(link => this.platform.is('mobileweb') || (link.platform && this.platform.is(link.platform)));
|
|
18601
18615
|
// Use min version as default version
|
|
18602
18616
|
upgradeLinks.forEach(link => {
|
|
18603
|
-
link.version = link.version ||
|
|
18617
|
+
link.version = link.version || appMinVersionFromPod;
|
|
18604
18618
|
});
|
|
18605
18619
|
return isNotEmptyArray(upgradeLinks) ? upgradeLinks : undefined;
|
|
18606
18620
|
}
|
|
@@ -18612,6 +18626,7 @@ class AppInstallUpgradeCard {
|
|
|
18612
18626
|
let url = config.getProperty(CORE_CONFIG_OPTIONS.ANDROID_INSTALL_URL);
|
|
18613
18627
|
if (isNilOrBlank(url))
|
|
18614
18628
|
url = this.environment.defaultAndroidInstallUrl || null;
|
|
18629
|
+
const minVersion = config.getProperty(CORE_CONFIG_OPTIONS.APP_MIN_VERSION);
|
|
18615
18630
|
// Compute App name
|
|
18616
18631
|
const name = isNotNilOrBlank(url) && config.label || this.environment.defaultAppName || 'SUMARiS';
|
|
18617
18632
|
if (url) {
|
|
@@ -18620,7 +18635,7 @@ class AppInstallUpgradeCard {
|
|
|
18620
18635
|
let mimeType;
|
|
18621
18636
|
// Get file name
|
|
18622
18637
|
const filename = this.getFilename(url);
|
|
18623
|
-
version =
|
|
18638
|
+
version = minVersion || 'latest';
|
|
18624
18639
|
// OK, this is a downloadable APK file (e.g. NOT a link to a playstore)
|
|
18625
18640
|
if (filename === null || filename === void 0 ? void 0 : filename.endsWith('.apk')) {
|
|
18626
18641
|
// Define mime type
|
|
@@ -18630,13 +18645,12 @@ class AppInstallUpgradeCard {
|
|
|
18630
18645
|
version = versionMatches && versionMatches[1] || version;
|
|
18631
18646
|
// Compute a new file name, with the version
|
|
18632
18647
|
if (isNotNilOrBlank(name)) {
|
|
18633
|
-
downloadFilename = `${name}
|
|
18648
|
+
downloadFilename = `${name}-v${version}.apk`.toLowerCase();
|
|
18634
18649
|
}
|
|
18635
18650
|
else {
|
|
18636
18651
|
downloadFilename = filename;
|
|
18637
|
-
// Replace 'latest' with the app
|
|
18652
|
+
// Replace 'latest' with the app version, if present
|
|
18638
18653
|
if (downloadFilename.indexOf('latest')) {
|
|
18639
|
-
version = config.getProperty(CORE_CONFIG_OPTIONS.APP_MIN_VERSION);
|
|
18640
18654
|
downloadFilename = downloadFilename.replace('latest', version);
|
|
18641
18655
|
}
|
|
18642
18656
|
}
|
|
@@ -18665,11 +18679,16 @@ class AppInstallUpgradeCard {
|
|
|
18665
18679
|
markForCheck() {
|
|
18666
18680
|
this.cd.markForCheck();
|
|
18667
18681
|
}
|
|
18682
|
+
showToast(opts) {
|
|
18683
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18684
|
+
yield Toasts.show(this.toastController, this.translate, opts);
|
|
18685
|
+
});
|
|
18686
|
+
}
|
|
18668
18687
|
}
|
|
18669
18688
|
AppInstallUpgradeCard.decorators = [
|
|
18670
18689
|
{ type: Component, args: [{
|
|
18671
18690
|
selector: 'app-install-upgrade-card',
|
|
18672
|
-
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
|
|
18691
|
+
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 <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 <!-- Download using platform -->\n <ion-button *ngIf=\"platform.canDownload; else webDownloadButton\"\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\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",
|
|
18673
18692
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
18674
18693
|
animations: [slideUpDownAnimation],
|
|
18675
18694
|
styles: ["ion-text small{font-size:85%}"]
|
|
@@ -18678,8 +18697,10 @@ AppInstallUpgradeCard.decorators = [
|
|
|
18678
18697
|
AppInstallUpgradeCard.ctorParameters = () => [
|
|
18679
18698
|
{ type: ModalController },
|
|
18680
18699
|
{ type: ConfigService },
|
|
18681
|
-
{ type:
|
|
18700
|
+
{ type: ToastController },
|
|
18701
|
+
{ type: TranslateService },
|
|
18682
18702
|
{ type: ChangeDetectorRef },
|
|
18703
|
+
{ type: PlatformService },
|
|
18683
18704
|
{ type: NetworkService },
|
|
18684
18705
|
{ type: undefined, decorators: [{ type: Inject, args: [ENVIRONMENT,] }] }
|
|
18685
18706
|
];
|
|
@@ -23531,6 +23552,7 @@ class UsersPage extends AppTable {
|
|
|
23531
23552
|
this.filterCriteriaCount = 0;
|
|
23532
23553
|
this.statusList = StatusList;
|
|
23533
23554
|
this.statusById = StatusById;
|
|
23555
|
+
this.useSticky = false;
|
|
23534
23556
|
this.referentialToString = referentialToString;
|
|
23535
23557
|
this.inlineEdition = accountService.isAdmin(); // Allow inline edition only if admin
|
|
23536
23558
|
this.canEdit = accountService.isAdmin();
|
|
@@ -23634,13 +23656,13 @@ class UsersPage extends AppTable {
|
|
|
23634
23656
|
UsersPage.decorators = [
|
|
23635
23657
|
{ type: Component, args: [{
|
|
23636
23658
|
selector: 'app-users-table',
|
|
23637
|
-
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 >=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 >=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",
|
|
23659
|
+
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 >=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 >=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",
|
|
23638
23660
|
providers: [
|
|
23639
23661
|
{ provide: ValidatorService, useExisting: PersonValidatorService }
|
|
23640
23662
|
],
|
|
23641
23663
|
animations: [slideUpDownAnimation],
|
|
23642
23664
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
23643
|
-
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:
|
|
23665
|
+
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}"]
|
|
23644
23666
|
},] }
|
|
23645
23667
|
];
|
|
23646
23668
|
UsersPage.ctorParameters = () => [
|
|
@@ -23660,6 +23682,7 @@ UsersPage.ctorParameters = () => [
|
|
|
23660
23682
|
{ type: undefined, decorators: [{ type: Inject, args: [ENVIRONMENT,] }] }
|
|
23661
23683
|
];
|
|
23662
23684
|
UsersPage.propDecorators = {
|
|
23685
|
+
useSticky: [{ type: Input }],
|
|
23663
23686
|
filterExpansionPanel: [{ type: ViewChild, args: [MatExpansionPanel, { static: true },] }]
|
|
23664
23687
|
};
|
|
23665
23688
|
|
|
@@ -23725,5 +23748,5 @@ const ErrorCodes = {
|
|
|
23725
23748
|
* Generated bundle index. Do not edit.
|
|
23726
23749
|
*/
|
|
23727
23750
|
|
|
23728
|
-
export { APP_ABOUT_DEVELOPERS, APP_ABOUT_PARTNERS, APP_CONFIG_OPTIONS, APP_GRAPHQL_TYPE_POLICIES, APP_HOME_BUTTONS, APP_LOCALES, APP_LOCAL_SETTINGS, APP_LOCAL_SETTINGS_OPTIONS, APP_LOCAL_STORAGE_TYPE_POLICIES, APP_MENU_ITEMS, APP_TESTING_PAGES, AboutModal, Account, AccountPage, AccountService, AccountToStringPipe, ActionsColumnComponent, AdminModule, AdminRoutingModule, Alerts, AnimationState, AppEditorOptions, AppEntityEditor, AppForm, AppFormField, AppFormProvider, AppFormUtils, AppGestureConfig, AppGraphQLModule, AppHelpModal, AppInMemoryTable, AppInstallUpgradeCard, AppListForm, AppLoadingSpinner, AppMenuModule, AppNullForm, AppPropertiesForm, AppTabEditor, AppTabEditorOptions, AppTable, AppTableDataSourceOptions, AppTableUtils, AppValidatorService, AppendToInputDirective, ArrayFilterPipe, ArrayFirstPipe, ArrayIncludesPipe, ArrayLengthPipe, ArrayPluckPipe, AudioProvider, AuthForm, AuthGuardService, AuthModal, AutocompleteTestPage, AutofocusDirective, Base58, BaseEntityService, BaseGraphqlService, BaseGraphqlServiceOptions, BaseReferential, Beans, CORE_CONFIG_OPTIONS, CellValueChangeListener, ChipsTestPage, Color, ColorScale, ComponentDirtyGuard, ConfigService, Configuration, CoreModule, CryptoService, DATE_ISO_PATTERN, DATE_UNIX_MS_TIMESTAMP, DATE_UNIX_TIMESTAMP, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLACEHOLDER_CHAR, DEFAULT_REQUIRED_COLUMNS, DateDiffDurationPipe, DateFormatPipe, DateFromNowPipe, DateTimeTestPage, DateUtils, Department, DepartmentToStringPipe, DurationPipe, ENTITIES_STORAGE_KEY_PREFIX, ENVIRONMENT, EmptyArrayPipe, EntitiesService, EntitiesStorage, EntitiesTableDataSource, Entity, EntityClass, EntityClasses, EntityFilter, EntityFilterUtils, EntityMetadataComponent, EntityStore, EntityUtils, Environment, ErrorCodes, EvenPipe, FileService, FileSizePipe, FormArrayHelper, FormButtonsBarComponent, FormButtonsBarToken, FormFieldValuesHolder, Fragments, GraphqlService, HAMMER_PRESS_TIME, HAMMER_TAP_TIME, HighlightPipe, HomePage, Hotkeys, HotkeysDialogComponent, InMemoryEntitiesService, IsLoginAccountPipe, IsNilOrBlankPipe, IsNotNilOrBlankPipe, IsNotOnFieldModePipe, IsOnFieldModePipe, JobUtils, KEYBOARD_HIDE_DELAY_MS, LAT_LONG_DEFAULT_MAX_DECIMALS, LAT_LONG_PATTERNS, LatLongFormatPipe, LatLongTestPage, LatitudeFormatPipe, LocalSettingsService, LongitudeFormatPipe, MINIFY_ENTITY_FOR_LOCAL_STORAGE, MINIFY_ENTITY_FOR_POD, MapGetPipe, MapKeysPipe, MapValuesPipe, MatAutocompleteConfigHolder, MatAutocompleteField, MatBooleanField, MatChipsField, MatDate, MatDateShort, MatDateTime, MatDuration, MatLatLongField, MatNumpadComponent, MatNumpadContainerComponent, MatNumpadContent, MatPaginatorI18n, MatStepperI18n, MatSwipeField, MaterialChipsModule, MaterialTestingModule, MaterialTestingPage, MathAbsPipe, MenuComponent, MenuItems, MenuService, ModalToolbarComponent, NetworkService, NgInitDirective, NotEmptyArrayPipe, NumberFormatPipe, NumpadDirective, OddPipe, PRIORITIZED_AUTHORITIES, PUBKEY_REGEXP, Peer, Person, PersonFilter, PersonFragments, PersonService, PersonToStringPipe, PersonUtils, PersonValidatorService, PlatformService, ProgressBarService, ProgressInterceptor, PropertyGetPipe, RESERVED_END_COLUMNS, RESERVED_START_COLUMNS, Referential, ReferentialRef, ReferentialUtils, RegisterConfirmPage, SETTINGS_DISPLAY_COLUMNS, SETTINGS_FILTER, SETTINGS_PAGE_SIZE, SETTINGS_SORTED_COLUMN, SETTINGS_STORAGE_KEY, SETTINGS_TRANSIENT_PROPERTIES, SPACE_PLACEHOLDER_CHAR, SelectPeerModal, SettingsPage, SharedDirectivesModule, SharedFormArrayValidators, SharedFormGroupValidators, SharedHotkeysModule, SharedMatAutocompleteModule, SharedMatBooleanModule, SharedMatDateTimeModule, SharedMatDurationModule, SharedMatLatLongModule, SharedMatNumpadModule, SharedMatSwipeModule, SharedMaterialModule, SharedModule, SharedPipesModule, SharedRoutingModule, SharedTestingModule, SharedValidators, SocialModule, Software, StatusById, StatusIds, StatusList, StrLengthPipe, SwipeTestPage, TableSelectColumnsComponent, ToStringPipe, Toasts, ToolbarComponent, ToolbarToken, TranslatablePipe, TranslateContextPipe, TranslateContextService, UserEvent, UserEventFilter, UserEventService, UserEventTypes, UserEventsTable, UserSettings, UsersPage, accountToString, adaptValueToControl, addValueInArray, arrayDistinct, arrayGroupBy, arraySize, asInputElement, canHaveFocus, capitalizeFirstLetter, chainPromises, changeCaseToUnderscore, clearValueInArray, copyEntity2Form, createPromiseEvent, createPromiseEventEmitter, departmentToString, departmentsToString, disableControls, emitPromiseEvent, entityToString, equalsOrNil, fadeInAnimation, fadeInOutAnimation, filterFalse, filterNotNil, filterNumberInput, filterTrue, firstArrayValue, firstFalsePromise, firstNotNil, firstNotNilPromise, firstTruePromise, focusInput, focusNextInput, focusPreviousInput, formatLatitude, formatLongitude, fromDateISOString, fromScrollEndEvent, fromUnixMsTimestamp, fromUnixTimestamp, getCaretPosition, getColorContrast, getColorShade, getColorTint, getConnectionType, getControlFromPath, getFocusableInputElements, getFormErrors, getFormValueFromEntity, getProperty, getPropertyByPath, getPropertyByPathAsString, getRandomImage, hexToRgb, hexToRgbArray, isControlHasInput, isEmptyArray, isInputElement, isInstanceOf, isInt, isNil, isNilOrBlank, isNilOrNaN, isNotEmptyArray, isNotNil, isNotNilOrBlank, isNotNilOrNaN, isNotNilString, isNumber, isNumberRange, joinProperties, joinPropertiesPath, logFormErrors, markAllAsTouched, markAsUntouched, markControlAsTouched, markFormGroupAsTouched, matchUpperCase, mergeLoadResult, mixHex, moveInputCaretToSeparator, noTrailingSlash, notNilOrDefault, nullIfUndefined, parseLatitudeOrLongitude, propertiesPathComparator, propertyComparator, propertyPathComparator, referentialToString, referentialsToString, remove, removeAll, removeDiacritics, removeDuplicatesFromArray, removeEnd, removeValueInArray, replaceAll, resetCalculatedValue, resizeArray, rgbArrayToHex, rgbToHex, round, scrollFactory, selectInputContent, selectInputRange, setCalculatedValue, setTabIndex, sleep, slideInOutAnimation, slideUpDownAnimation, sort, splitById, splitByProperty, startsWithUpperCase, suggestFromArray, suggestFromStringArray, tabindexComparator, toBoolean, toDateISOString, toDuration, toFloat, toInt, toNotNil, toNumber, trimEmptyToNull, uncapitalizeFirstLetter, updateValueAndValidity, waitFor, waitIdle, waitWhilePending, ɵ0$5 as ɵ0, CustomReuseStrategy as ɵa, MatNumpadDomService as ɵb, isFocusableElement as ɵc, RegisterForm as ɵd, AccountValidatorService as ɵe, RegisterModal as ɵf, UserSettingsValidatorService as ɵg, LocalSettingsValidatorService as ɵh };
|
|
23729
|
-
//# sourceMappingURL=ngx-
|
|
23751
|
+
export { APP_ABOUT_DEVELOPERS, APP_ABOUT_PARTNERS, APP_CONFIG_OPTIONS, APP_GRAPHQL_TYPE_POLICIES, APP_HOME_BUTTONS, APP_LOCALES, APP_LOCAL_SETTINGS, APP_LOCAL_SETTINGS_OPTIONS, APP_LOCAL_STORAGE_TYPE_POLICIES, APP_MENU_ITEMS, APP_TESTING_PAGES, AboutModal, Account, AccountPage, AccountService, AccountToStringPipe, ActionsColumnComponent, AdminModule, AdminRoutingModule, Alerts, AnimationState, AppEditorOptions, AppEntityEditor, AppForm, AppFormField, AppFormProvider, AppFormUtils, AppGestureConfig, AppGraphQLModule, AppHelpModal, AppInMemoryTable, AppInstallUpgradeCard, AppListForm, AppLoadingSpinner, AppMenuModule, AppNullForm, AppPropertiesForm, AppTabEditor, AppTabEditorOptions, AppTable, AppTableDataSourceOptions, AppTableUtils, AppValidatorService, AppendToInputDirective, ArrayFilterPipe, ArrayFirstPipe, ArrayIncludesPipe, ArrayLengthPipe, ArrayPluckPipe, AudioProvider, AuthForm, AuthGuardService, AuthModal, AutocompleteTestPage, AutofocusDirective, Base58, BaseEntityService, BaseGraphqlService, BaseGraphqlServiceOptions, BaseReferential, Beans, CORE_CONFIG_OPTIONS, CellValueChangeListener, ChipsTestPage, Color, ColorScale, ComponentDirtyGuard, ConfigService, Configuration, CoreModule, CryptoService, DATE_ISO_PATTERN, DATE_UNIX_MS_TIMESTAMP, DATE_UNIX_TIMESTAMP, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLACEHOLDER_CHAR, DEFAULT_REQUIRED_COLUMNS, DateDiffDurationPipe, DateFormatPipe, DateFromNowPipe, DateTimeTestPage, DateUtils, Department, DepartmentToStringPipe, DurationPipe, ENTITIES_STORAGE_KEY_PREFIX, ENVIRONMENT, EmptyArrayPipe, EntitiesService, EntitiesStorage, EntitiesTableDataSource, Entity, EntityClass, EntityClasses, EntityFilter, EntityFilterUtils, EntityMetadataComponent, EntityStore, EntityUtils, Environment, ErrorCodes, EvenPipe, FileService, FileSizePipe, FormArrayHelper, FormButtonsBarComponent, FormButtonsBarToken, FormFieldValuesHolder, Fragments, GraphqlService, HAMMER_PRESS_TIME, HAMMER_TAP_TIME, HighlightPipe, HomePage, Hotkeys, HotkeysDialogComponent, InMemoryEntitiesService, IsLoginAccountPipe, IsNilOrBlankPipe, IsNotNilOrBlankPipe, IsNotOnFieldModePipe, IsOnFieldModePipe, JobUtils, KEYBOARD_HIDE_DELAY_MS, LAT_LONG_DEFAULT_MAX_DECIMALS, LAT_LONG_PATTERNS, LatLongFormatPipe, LatLongTestPage, LatitudeFormatPipe, LocalSettingsService, LongitudeFormatPipe, MINIFY_ENTITY_FOR_LOCAL_STORAGE, MINIFY_ENTITY_FOR_POD, MapGetPipe, MapKeysPipe, MapValuesPipe, MatAutocompleteConfigHolder, MatAutocompleteField, MatBooleanField, MatChipsField, MatDate, MatDateShort, MatDateTime, MatDuration, MatLatLongField, MatNumpadComponent, MatNumpadContainerComponent, MatNumpadContent, MatPaginatorI18n, MatStepperI18n, MatSwipeField, MaterialChipsModule, MaterialTestingModule, MaterialTestingPage, MathAbsPipe, MenuComponent, MenuItems, MenuService, ModalToolbarComponent, NetworkService, NgInitDirective, NotEmptyArrayPipe, NumberFormatPipe, NumpadDirective, OddPipe, PRIORITIZED_AUTHORITIES, PUBKEY_REGEXP, Peer, Person, PersonFilter, PersonFragments, PersonService, PersonToStringPipe, PersonUtils, PersonValidatorService, PlatformService, ProgressBarService, ProgressInterceptor, PropertyGetPipe, RESERVED_END_COLUMNS, RESERVED_START_COLUMNS, Referential, ReferentialRef, ReferentialUtils, RegisterConfirmPage, SETTINGS_DISPLAY_COLUMNS, SETTINGS_FILTER, SETTINGS_PAGE_SIZE, SETTINGS_SORTED_COLUMN, SETTINGS_STORAGE_KEY, SETTINGS_TRANSIENT_PROPERTIES, SPACE_PLACEHOLDER_CHAR, SelectPeerModal, SettingsPage, SharedDirectivesModule, SharedFormArrayValidators, SharedFormGroupValidators, SharedHotkeysModule, SharedMatAutocompleteModule, SharedMatBooleanModule, SharedMatDateTimeModule, SharedMatDurationModule, SharedMatLatLongModule, SharedMatNumpadModule, SharedMatSwipeModule, SharedMaterialModule, SharedModule, SharedPipesModule, SharedRoutingModule, SharedTestingModule, SharedValidators, SocialModule, Software, StartableService, StatusById, StatusIds, StatusList, StrLengthPipe, SwipeTestPage, TableSelectColumnsComponent, ToStringPipe, Toasts, ToolbarComponent, ToolbarToken, TranslatablePipe, TranslateContextPipe, TranslateContextService, UserEvent, UserEventFilter, UserEventService, UserEventTypes, UserEventsTable, UserSettings, UsersPage, accountToString, adaptValueToControl, addValueInArray, arrayDistinct, arrayGroupBy, arraySize, asInputElement, canHaveFocus, capitalizeFirstLetter, chainPromises, changeCaseToUnderscore, clearValueInArray, copyEntity2Form, createPromiseEvent, createPromiseEventEmitter, departmentToString, departmentsToString, disableControls, emitPromiseEvent, entityToString, equalsOrNil, fadeInAnimation, fadeInOutAnimation, filterFalse, filterNotNil, filterNumberInput, filterTrue, firstArrayValue, firstFalsePromise, firstNotNil, firstNotNilPromise, firstTruePromise, focusInput, focusNextInput, focusPreviousInput, formatLatitude, formatLongitude, fromDateISOString, fromScrollEndEvent, fromUnixMsTimestamp, fromUnixTimestamp, getCaretPosition, getColorContrast, getColorShade, getColorTint, getConnectionType, getControlFromPath, getFocusableInputElements, getFormErrors, getFormValueFromEntity, getProperty, getPropertyByPath, getPropertyByPathAsString, getRandomImage, hexToRgb, hexToRgbArray, isControlHasInput, isEmptyArray, isInputElement, isInstanceOf, isInt, isNil, isNilOrBlank, isNilOrNaN, isNotEmptyArray, isNotNil, isNotNilOrBlank, isNotNilOrNaN, isNotNilString, isNumber, isNumberRange, joinProperties, joinPropertiesPath, logFormErrors, markAllAsTouched, markAsUntouched, markControlAsTouched, markFormGroupAsTouched, matchUpperCase, mergeLoadResult, mixHex, moveInputCaretToSeparator, noTrailingSlash, notNilOrDefault, nullIfUndefined, parseLatitudeOrLongitude, propertiesPathComparator, propertyComparator, propertyPathComparator, referentialToString, referentialsToString, remove, removeAll, removeDiacritics, removeDuplicatesFromArray, removeEnd, removeValueInArray, replaceAll, resetCalculatedValue, resizeArray, rgbArrayToHex, rgbToHex, round, scrollFactory, selectInputContent, selectInputRange, setCalculatedValue, setTabIndex, sleep, slideInOutAnimation, slideUpDownAnimation, sort, splitById, splitByProperty, startsWithUpperCase, suggestFromArray, suggestFromStringArray, tabindexComparator, toBoolean, toDateISOString, toDuration, toFloat, toInt, toNotNil, toNumber, trimEmptyToNull, uncapitalizeFirstLetter, updateValueAndValidity, waitFor, waitIdle, waitWhilePending, ɵ0$5 as ɵ0, CustomReuseStrategy as ɵa, MatNumpadDomService as ɵb, isFocusableElement as ɵc, RegisterForm as ɵd, AccountValidatorService as ɵe, RegisterModal as ɵf, UserSettingsValidatorService as ɵg, LocalSettingsValidatorService as ɵh };
|
|
23752
|
+
//# sourceMappingURL=sumaris-net.ngx-components.js.map
|