@sumaris-net/ngx-components 1.7.3 → 1.8.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.
@@ -1636,7 +1636,7 @@ MatAutocompleteField.decorators = [
1636
1636
  { type: Component, args: [{
1637
1637
  // eslint-disable-next-line @angular-eslint/component-selector
1638
1638
  selector: 'mat-autocomplete-field',
1639
- template: "<ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col class=\"ion-no-padding\">\n <!-- readonly -->\n <mat-form-field *ngIf=\"_readonly; else writable\"\n [floatLabel]=\"floatLabel\"\n [title]=\"displayValue\"\n class=\"mat-form-field-disabled\">\n\n <div matPrefix>\n <ng-container *ngTemplateOutlet=\"matPrefixTemplate\"></ng-container>\n </div>\n\n <input matInput hidden type=\"text\"\n [placeholder]=\"placeholder\"\n [formControl]=\"formControl\" readonly>\n <ion-text>{{displayWith(value)}}</ion-text>\n\n <div matSuffix>\n <ng-container *ngTemplateOutlet=\"matSuffixTemplate\"></ng-container>\n </div>\n\n </mat-form-field>\n\n <ng-template #writable>\n <mat-form-field [floatLabel]=\"floatLabel\"\n [class.mat-form-field-disabled]=\"disabled\"\n [title]=\"displayValue\">\n\n <div matPrefix>\n <ng-container *ngTemplateOutlet=\"matPrefixTemplate\"></ng-container>\n </div>\n\n <!-- Basic select -->\n <mat-select #matSelect\n *ngIf=\"!searchable; else searchableTemplate\"\n [placeholder]=\"placeholder\"\n [formControl]=\"formControl\"\n [tabindex]=\"_tabindex\"\n [panelClass]=\"classList\"\n [style.width]=\"panelWidth\"\n (focus)=\"filterMatSelectFocusEvent($event)\"\n (blur)=\"filterMatSelectBlurEvent($event)\"\n (keydown.escape)=\"keydownEscape.emit($event)\"\n (keyup.enter)=\"keyupEnter.emit($event)\"\n [compareWith]=\"equals\"\n [multiple]=\"multiple\"\n [required]=\"required\">\n <mat-select-trigger>{{ displayWith(value) }}</mat-select-trigger>\n\n <!-- header -->\n <ion-row class=\"mat-autocomplete-header ion-no-padding\">\n <ion-col *ngFor=\"let attr of displayAttributes; index as i; last as isLast\" [size]=\"displayColumnSizes[i]\">\n <ion-label [innerHTML]=\"displayColumnNames[i] | translate\"></ion-label>\n </ion-col>\n </ion-row>\n\n <!-- None option -->\n <mat-option *ngIf=\"!required\" class=\"ion-no-padding\">\n <ion-row class=\"ion-no-padding\">\n <ion-col class=\"ion-no-padding\">\n <ion-label><i translate>COMMON.EMPTY_OPTION</i></ion-label>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <ng-container *ngIf=\"$filteredItems | async; let items\">\n <!-- No item option -->\n <mat-option *ngIf=\"items | isEmptyArray\" class=\"ion-padding text-italic\" disabled>\n <ion-label>{{noResultMessage | translate}}</ion-label>\n </mat-option>\n\n <!-- options -->\n <mat-option *ngFor=\"let item of items\" [value]=\"item\" class=\"ion-no-padding\">\n <ion-row [classList]=\"item.classList\">\n <ion-col *ngFor=\"let path of displayAttributes; index as i;\"\n [size]=\"displayColumnSizes[i]\">\n <ion-text>{{item | propertyGet: path }}</ion-text>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <!-- More item -->\n <mat-option *ngIf=\"_moreItemsCount\" (ngInit)=\"_initMatSelectInfiniteScroll()\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-container>\n </mat-select>\n\n\n <!--\n NOTE :\n - \"selectInputContent($event) || onFocus.emit($event)\" : call onFocus only when to the input is empty (nothing to select)\n -->\n <ng-template #searchableTemplate>\n <input matInput #matInputText type=\"text\"\n [matAutocomplete]=\"autocomplete\"\n [matAutocompletePosition]=\"matAutocompletePosition\"\n [formControl]=\"formControl\"\n [placeholder]=\"placeholder\"\n [appAutofocus]=\"autofocus\"\n [tabindex]=\"_tabindex\"\n [required]=\"required\"\n (click)=\"clicked.emit($event)\"\n (focus)=\"filterInputTextFocusEvent($event)\"\n (blur)=\"filterInputTextBlurEvent($event)\"\n (keydown.escape)=\"keydownEscape.emit($event)\"\n (keyup.enter)=\"keyupEnter.emit($event)\"\n (keyup.arrowdown)=\"dropButtonClick.emit($event)\">\n\n <!-- autocomplete -->\n <mat-autocomplete #autocomplete=\"matAutocomplete\"\n autoActiveFirstOption\n [displayWith]=\"displayWith\"\n [class]=\"classList\"\n [panelWidth]=\"panelWidth\">\n\n <ion-row class=\"mat-autocomplete-header ion-no-padding\">\n <ion-col *ngFor=\"let attr of displayAttributes; index as i; last as last\" [size]=\"displayColumnSizes[i]\">\n <ion-label [innerHTML]=\"displayColumnNames[i] | translate\"></ion-label>\n <ion-text color=\"light\" *ngIf=\"last && itemCount; let count\" class=\"ion-float-end ion-padding-end\"><i>{{'COMMON.RESULT_COUNT'|translate: {count: count} }}</i></ion-text>\n </ion-col>\n </ion-row>\n\n <ng-container *ngIf=\"$filteredItems | async; else loadingTemplate; let items\">\n <!-- No item option -->\n <mat-option *ngIf=\"items | isEmptyArray\" class=\"ion-padding text-italic\" disabled>\n <ion-label><i>{{noResultMessage | translate}}</i></ion-label>\n </mat-option>\n\n <mat-option *ngFor=\"let item of items\" [value]=\"item\" class=\"ion-no-padding\">\n <ion-row [classList]=\"item.classList\">\n <ion-col *ngFor=\"let path of displayAttributes; index as i;\"\n [size]=\"displayColumnSizes[i]\">\n <ion-text [innerHTML]=\"item | propertyGet: path | highlight: { search: formControl.value, withAccent: highlightAccent }\"></ion-text>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <!-- More item -->\n <mat-option *ngIf=\"_moreItemsCount\" (ngInit)=\"_initAutocompleteInfiniteScroll()\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-container>\n\n <!-- Loading spinner -->\n <ng-template #loadingTemplate>\n <mat-option *ngIf=\"(matInputText.value | strLength) >= suggestLengthThreshold; else waitMoreCharacterTemplate\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-template>\n\n <!-- Need more characters -->\n <ng-template #waitMoreCharacterTemplate>\n <mat-option class=\"ion-padding text-italic\" disabled>\n <ion-label>\n {{'INFO.PLEASE_TYPE_MORE_CHARACTERS'|translate:{minLength: suggestLengthThreshold} }}\n </ion-label>\n </mat-option>\n </ng-template>\n </mat-autocomplete>\n\n </ng-template>\n\n <div matSuffix>\n <ng-container *ngTemplateOutlet=\"matSuffixTemplate\"></ng-container>\n </div>\n\n <mat-error *ngIf=\"formControl.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"formControl.hasError('entity')\" translate>ERROR.FIELD_INVALID</mat-error>\n <ng-content select=\"[matError]\"></ng-content>\n\n </mat-form-field>\n </ng-template>\n </ion-col>\n <ion-col size=\"auto\" class=\"ion-align-self-center\">\n <ng-content select=\"[matAfter]\"></ng-content>\n </ion-col>\n </ion-row>\n</ion-grid>\n\n<ng-template #matPrefixTemplate>\n <ng-content select=\"[matPrefix]\"></ng-content>\n</ng-template>\n\n\n<ng-template #matSuffixTemplate>\n <button matSuffix mat-icon-button tabindex=\"-1\"\n type=\"button\"\n *ngIf=\"searchable\"\n (click)=\"dropButtonClick.emit($event)\"\n [hidden]=\"disabled\">\n <mat-icon class=\"large select-arrow\">arrow_drop_down</mat-icon>\n </button>\n <button matSuffix type=\"button\" mat-icon-button tabindex=\"-1\"\n *ngIf=\"enabled && mobile && !searchable && !multiple\"\n [title]=\"'COMMON.BTN_SEARCH'|translate\"\n (click)=\"toggleSearch($event)\">\n <mat-icon>search</mat-icon>\n </button>\n <button matSuffix mat-icon-button tabindex=\"-1\"\n type=\"button\"\n *ngIf=\"clearable\"\n (click)=\"clearValue($event)\"\n [hidden]=\"disabled || !formControl.value\">\n <mat-icon>close</mat-icon>\n </button>\n\n <ng-content select=\"[matSuffix]\"></ng-content>\n</ng-template>\n",
1639
+ template: "<ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col class=\"ion-no-padding\">\n <!-- readonly -->\n <mat-form-field *ngIf=\"_readonly; else writable\"\n [floatLabel]=\"floatLabel\"\n [title]=\"displayValue\"\n class=\"mat-form-field-disabled\">\n\n <div matPrefix>\n <ng-container *ngTemplateOutlet=\"matPrefixTemplate\"></ng-container>\n </div>\n\n <input matInput hidden type=\"text\"\n [placeholder]=\"placeholder\"\n [formControl]=\"formControl\" readonly>\n <ion-text>{{displayWith(value)}}</ion-text>\n\n <div matSuffix>\n <ng-container *ngTemplateOutlet=\"matSuffixTemplate\"></ng-container>\n </div>\n\n </mat-form-field>\n\n <ng-template #writable>\n <mat-form-field [floatLabel]=\"floatLabel\"\n [class.mat-form-field-disabled]=\"disabled\"\n [title]=\"displayValue\">\n\n <div matPrefix>\n <ng-container *ngTemplateOutlet=\"matPrefixTemplate\"></ng-container>\n </div>\n\n <!-- Basic select -->\n <mat-select #matSelect\n *ngIf=\"!searchable; else searchableTemplate\"\n [placeholder]=\"placeholder\"\n [formControl]=\"formControl\"\n [tabindex]=\"_tabindex\"\n [panelClass]=\"classList\"\n [style.width]=\"panelWidth\"\n (focus)=\"filterMatSelectFocusEvent($event)\"\n (blur)=\"filterMatSelectBlurEvent($event)\"\n (keydown.escape)=\"keydownEscape.emit($event)\"\n (keyup.enter)=\"keyupEnter.emit($event)\"\n [compareWith]=\"equals\"\n [multiple]=\"multiple\"\n [required]=\"required\">\n <mat-select-trigger>{{ displayWith(value) }}</mat-select-trigger>\n\n <!-- header -->\n <ion-row class=\"mat-autocomplete-header ion-no-padding\">\n <ion-col *ngFor=\"let attr of displayAttributes; index as i; last as isLast\" [size]=\"displayColumnSizes[i]\">\n <ion-label [innerHTML]=\"displayColumnNames[i] | translate\"></ion-label>\n </ion-col>\n </ion-row>\n\n <!-- None option -->\n <mat-option *ngIf=\"!required\" class=\"ion-no-padding\">\n <ion-row class=\"ion-no-padding\">\n <ion-col class=\"ion-no-padding\">\n <ion-label><i translate>COMMON.EMPTY_OPTION</i></ion-label>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <ng-container *ngIf=\"$filteredItems | async; let items\">\n <!-- No item option -->\n <mat-option *ngIf=\"items | isEmptyArray\" class=\"ion-padding text-italic\" disabled>\n <ion-label>{{noResultMessage | translate}}</ion-label>\n </mat-option>\n\n <!-- options -->\n <mat-option *ngFor=\"let item of items\" [value]=\"item\" class=\"ion-no-padding\">\n <ion-row [classList]=\"item.classList\">\n <ion-col *ngFor=\"let path of displayAttributes; index as i;\"\n [size]=\"displayColumnSizes[i]\">\n <ion-text>{{item | propertyGet: path }}</ion-text>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <!-- More item -->\n <mat-option *ngIf=\"_moreItemsCount\" (ngInit)=\"_initMatSelectInfiniteScroll()\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-container>\n </mat-select>\n\n\n <!--\n NOTE :\n - \"selectInputContent($event) || onFocus.emit($event)\" : call onFocus only when to the input is empty (nothing to select)\n -->\n <ng-template #searchableTemplate>\n <input matInput #matInputText type=\"text\"\n [matAutocomplete]=\"autocomplete\"\n [matAutocompletePosition]=\"matAutocompletePosition\"\n [formControl]=\"formControl\"\n [placeholder]=\"placeholder\"\n [appAutofocus]=\"autofocus\"\n [tabindex]=\"_tabindex\"\n [required]=\"required\"\n (click)=\"clicked.emit($event)\"\n (focus)=\"filterInputTextFocusEvent($event)\"\n (blur)=\"filterInputTextBlurEvent($event)\"\n (keydown.escape)=\"keydownEscape.emit($event)\"\n (keyup.enter)=\"keyupEnter.emit($event)\"\n (keyup.arrowdown)=\"!autocomplete.showPanel && dropButtonClick.emit($event)\">\n\n <!-- autocomplete -->\n <mat-autocomplete #autocomplete=\"matAutocomplete\"\n autoActiveFirstOption\n [displayWith]=\"displayWith\"\n [class]=\"classList\"\n [panelWidth]=\"panelWidth\">\n\n <ion-row class=\"mat-autocomplete-header ion-no-padding\">\n <ion-col *ngFor=\"let attr of displayAttributes; index as i; last as last\" [size]=\"displayColumnSizes[i]\">\n <ion-label [innerHTML]=\"displayColumnNames[i] | translate\"></ion-label>\n <ion-text color=\"light\" *ngIf=\"last && itemCount; let count\" class=\"ion-float-end ion-padding-end\"><i>{{'COMMON.RESULT_COUNT'|translate: {count: count} }}</i></ion-text>\n </ion-col>\n </ion-row>\n\n <ng-container *ngIf=\"$filteredItems | async; else loadingTemplate; let items\">\n <!-- No item option -->\n <mat-option *ngIf=\"items | isEmptyArray\" class=\"ion-padding text-italic\" disabled>\n <ion-label><i>{{noResultMessage | translate}}</i></ion-label>\n </mat-option>\n\n <mat-option *ngFor=\"let item of items\" [value]=\"item\" class=\"ion-no-padding\">\n <ion-row [classList]=\"item.classList\">\n <ion-col *ngFor=\"let path of displayAttributes; index as i;\"\n [size]=\"displayColumnSizes[i]\">\n <ion-text [innerHTML]=\"item | propertyGet: path | highlight: { search: formControl.value, withAccent: highlightAccent }\"></ion-text>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <!-- More item -->\n <mat-option *ngIf=\"_moreItemsCount\" (ngInit)=\"_initAutocompleteInfiniteScroll()\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-container>\n\n <!-- Loading spinner -->\n <ng-template #loadingTemplate>\n <mat-option *ngIf=\"(matInputText.value | strLength) >= suggestLengthThreshold; else waitMoreCharacterTemplate\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-template>\n\n <!-- Need more characters -->\n <ng-template #waitMoreCharacterTemplate>\n <mat-option class=\"ion-padding text-italic\" disabled>\n <ion-label>\n {{'INFO.PLEASE_TYPE_MORE_CHARACTERS'|translate:{minLength: suggestLengthThreshold} }}\n </ion-label>\n </mat-option>\n </ng-template>\n </mat-autocomplete>\n\n </ng-template>\n\n <div matSuffix>\n <ng-container *ngTemplateOutlet=\"matSuffixTemplate\"></ng-container>\n </div>\n\n <mat-error *ngIf=\"formControl.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"formControl.hasError('entity')\" translate>ERROR.FIELD_INVALID</mat-error>\n <ng-content select=\"[matError]\"></ng-content>\n\n </mat-form-field>\n </ng-template>\n </ion-col>\n <ion-col size=\"auto\" class=\"ion-align-self-center\">\n <ng-content select=\"[matAfter]\"></ng-content>\n </ion-col>\n </ion-row>\n</ion-grid>\n\n<ng-template #matPrefixTemplate>\n <ng-content select=\"[matPrefix]\"></ng-content>\n</ng-template>\n\n\n<ng-template #matSuffixTemplate>\n <button matSuffix mat-icon-button tabindex=\"-1\"\n type=\"button\"\n *ngIf=\"searchable\"\n (click)=\"dropButtonClick.emit($event)\"\n [hidden]=\"disabled\">\n <mat-icon class=\"large select-arrow\">arrow_drop_down</mat-icon>\n </button>\n <button matSuffix type=\"button\" mat-icon-button tabindex=\"-1\"\n *ngIf=\"enabled && mobile && !searchable && !multiple\"\n [title]=\"'COMMON.BTN_SEARCH'|translate\"\n (click)=\"toggleSearch($event)\">\n <mat-icon>search</mat-icon>\n </button>\n <button matSuffix mat-icon-button tabindex=\"-1\"\n type=\"button\"\n *ngIf=\"clearable\"\n (click)=\"clearValue($event)\"\n [hidden]=\"disabled || !formControl.value\">\n <mat-icon>close</mat-icon>\n </button>\n\n <ng-content select=\"[matSuffix]\"></ng-content>\n</ng-template>\n",
1640
1640
  providers: [{
1641
1641
  provide: NG_VALUE_ACCESSOR,
1642
1642
  multi: true,
@@ -13561,6 +13561,7 @@ class AccountService extends BaseGraphqlService {
13561
13561
  this.onChange = new Subject();
13562
13562
  this.onAuthTokenChange = new Subject();
13563
13563
  this.onAuthBasicChange = new Subject();
13564
+ this._stopWatching$ = new Subject();
13564
13565
  this._cache = {
13565
13566
  loaded: false,
13566
13567
  keypair: null,
@@ -13963,6 +13964,7 @@ class AccountService extends BaseGraphqlService {
13963
13964
  if (hadAuthBasic)
13964
13965
  this.onAuthBasicChange.next(undefined);
13965
13966
  this.onChange.next(undefined);
13967
+ this._stopWatching$.next();
13966
13968
  });
13967
13969
  }
13968
13970
  /**
@@ -14079,12 +14081,17 @@ class AccountService extends BaseGraphqlService {
14079
14081
  return res && res.confirmAccountEmail;
14080
14082
  });
14081
14083
  }
14082
- listenChanges() {
14084
+ watch() {
14083
14085
  if (!this._cache.pubkey)
14084
- return Subscription.EMPTY;
14085
- const self = this;
14086
- console.debug('[account] [WS] Listening changes');
14087
- const subscription = this.graphql.subscribe({
14086
+ throw new Error('Not logged in');
14087
+ if (!this.started) {
14088
+ // Wait service ready, then loop
14089
+ return from(this.ready())
14090
+ .pipe(switchMap(() => this.watch()));
14091
+ }
14092
+ this._stopWatching$.next(); // Stop previous listening
14093
+ console.debug('[account] [WS] Watching changes');
14094
+ return this.graphql.subscribe({
14088
14095
  query: AccountSubscriptions.listenChanges,
14089
14096
  variables: {
14090
14097
  interval: 10
@@ -14093,35 +14100,39 @@ class AccountService extends BaseGraphqlService {
14093
14100
  code: ErrorCodes$2.SUBSCRIBE_ACCOUNT_ERROR,
14094
14101
  message: 'ERROR.ACCOUNT.SUBSCRIBE_ACCOUNT_ERROR'
14095
14102
  }
14096
- }).subscribe({
14097
- next: ({ data }) => __awaiter(this, void 0, void 0, function* () {
14098
- var _a;
14099
- if (!data)
14100
- return;
14101
- const existingUpdateDate = toDateISOString((_a = self._data) === null || _a === void 0 ? void 0 : _a.updateDate);
14102
- if (existingUpdateDate !== data.updateDate) {
14103
- console.debug(`[account] [WS] Detected update on {${data.updateDate}}`);
14104
- yield self.refresh();
14105
- }
14106
- }),
14107
- error: (err) => __awaiter(this, void 0, void 0, function* () {
14103
+ })
14104
+ .pipe(
14105
+ // Stop this pipe next time we call watch()
14106
+ takeUntil(this._stopWatching$), map(({ data }) => {
14107
+ var _a;
14108
+ if (!data)
14109
+ return;
14110
+ const existingUpdateDate = toDateISOString((_a = this._data) === null || _a === void 0 ? void 0 : _a.updateDate);
14111
+ if (existingUpdateDate === data.updateDate)
14112
+ return;
14113
+ console.debug(`[account] [WS] Detected update on {${data.updateDate}}`);
14114
+ return Account.fromObject(data);
14115
+ }), filter(isNotNil));
14116
+ }
14117
+ listenChanges() {
14118
+ const self = this;
14119
+ const subscription = this.watch()
14120
+ .subscribe({
14121
+ next: (data) => self.refresh(),
14122
+ error: (err) => {
14108
14123
  if (err && +err.code === ServerErrorCodes.NOT_FOUND) {
14109
14124
  console.info('[account] Account not exists anymore: force user to logout...', err);
14110
- yield self.logout();
14125
+ self.logout();
14111
14126
  }
14112
14127
  else if (err && +err.code === ServerErrorCodes.UNAUTHORIZED) {
14113
14128
  console.info('[account] Account not authorized: force user to logout...', err);
14114
- yield self.logout();
14129
+ self.logout();
14115
14130
  }
14116
14131
  else {
14117
14132
  console.warn('[account] [WS] Received error:', err);
14118
14133
  }
14119
- }),
14120
- complete: () => {
14121
- console.debug('[account] [WS] Completed');
14122
14134
  }
14123
14135
  });
14124
- // Add log when closing WS
14125
14136
  subscription.add(() => console.debug('[account] [WS] Stop listening changes'));
14126
14137
  return subscription;
14127
14138
  }
@@ -14801,7 +14812,7 @@ const CacheStatistics = gql `
14801
14812
  `;
14802
14813
  const APP_CONFIG_OPTIONS = new InjectionToken('defaultOptions');
14803
14814
  class ConfigService extends BaseGraphqlService {
14804
- constructor(platform, graphql, storage, network, file, toastController, translate, environment, defaultOptionsMap) {
14815
+ constructor(platform, graphql, storage, network, file, toastController, translate, accountService, environment, defaultOptionsMap) {
14805
14816
  super(graphql, environment);
14806
14817
  this.platform = platform;
14807
14818
  this.graphql = graphql;
@@ -14810,6 +14821,7 @@ class ConfigService extends BaseGraphqlService {
14810
14821
  this.file = file;
14811
14822
  this.toastController = toastController;
14812
14823
  this.translate = translate;
14824
+ this.accountService = accountService;
14813
14825
  this.environment = environment;
14814
14826
  this.$data = new BehaviorSubject(null);
14815
14827
  this._debug = !environment.production;
@@ -14856,6 +14868,9 @@ class ConfigService extends BaseGraphqlService {
14856
14868
  return this.loadDefault();
14857
14869
  });
14858
14870
  }
14871
+ canUserWrite(data, opts) {
14872
+ return this.accountService.isAdmin();
14873
+ }
14859
14874
  /**
14860
14875
  * Save a configuration
14861
14876
  *
@@ -15073,7 +15088,7 @@ class ConfigService extends BaseGraphqlService {
15073
15088
  });
15074
15089
  }
15075
15090
  }
15076
- ConfigService.ɵprov = i0.ɵɵdefineInjectable({ factory: function ConfigService_Factory() { return new ConfigService(i0.ɵɵinject(i1$4.Platform), i0.ɵɵinject(GraphqlService), i0.ɵɵinject(i3$1.Storage), i0.ɵɵinject(NetworkService), i0.ɵɵinject(FileService), i0.ɵɵinject(i1$4.ToastController), i0.ɵɵinject(i1$1.TranslateService), i0.ɵɵinject(ENVIRONMENT), i0.ɵɵinject(APP_CONFIG_OPTIONS, 8)); }, token: ConfigService, providedIn: "root" });
15091
+ ConfigService.ɵprov = i0.ɵɵdefineInjectable({ factory: function ConfigService_Factory() { return new ConfigService(i0.ɵɵinject(i1$4.Platform), i0.ɵɵinject(GraphqlService), i0.ɵɵinject(i3$1.Storage), i0.ɵɵinject(NetworkService), i0.ɵɵinject(FileService), i0.ɵɵinject(i1$4.ToastController), i0.ɵɵinject(i1$1.TranslateService), i0.ɵɵinject(AccountService), i0.ɵɵinject(ENVIRONMENT), i0.ɵɵinject(APP_CONFIG_OPTIONS, 8)); }, token: ConfigService, providedIn: "root" });
15077
15092
  ConfigService.decorators = [
15078
15093
  { type: Injectable, args: [{
15079
15094
  providedIn: 'root',
@@ -15088,6 +15103,7 @@ ConfigService.ctorParameters = () => [
15088
15103
  { type: FileService },
15089
15104
  { type: ToastController },
15090
15105
  { type: TranslateService },
15106
+ { type: AccountService },
15091
15107
  { type: undefined, decorators: [{ type: Inject, args: [ENVIRONMENT,] }] },
15092
15108
  { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [APP_CONFIG_OPTIONS,] }] }
15093
15109
  ];
@@ -20633,6 +20649,9 @@ class BaseEntityService extends BaseGraphqlService {
20633
20649
  return res.total;
20634
20650
  });
20635
20651
  }
20652
+ canUserWrite(data, opts) {
20653
+ return !!data;
20654
+ }
20636
20655
  saveAll(entities, opts) {
20637
20656
  return __awaiter(this, void 0, void 0, function* () {
20638
20657
  if (isEmptyArray(entities))
@@ -23859,6 +23878,10 @@ class AppEntityEditor extends AppTabEditor {
23859
23878
  if (!opts || opts.emitEvent !== false)
23860
23879
  this.markForCheck();
23861
23880
  }
23881
+ /* -- public abstract methods (to override) -- */
23882
+ canUserWrite(data, opts) {
23883
+ return this.dataService.canUserWrite(data, opts);
23884
+ }
23862
23885
  /* -- protected methods -- */
23863
23886
  /**
23864
23887
  * Return undefined to keep same tab