@sumaris-net/ngx-components 1.20.35 → 1.21.0-beta10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/bundles/sumaris-net.ngx-components.umd.js +1103 -321
  2. package/bundles/sumaris-net.ngx-components.umd.js.map +1 -1
  3. package/bundles/sumaris-net.ngx-components.umd.min.js +2 -2
  4. package/bundles/sumaris-net.ngx-components.umd.min.js.map +1 -1
  5. package/doc/changelog.md +0 -9
  6. package/esm2015/public_api.js +8 -3
  7. package/esm2015/src/app/core/icon/icon.component.js +5 -2
  8. package/esm2015/src/app/core/menu/menu.component.js +2 -2
  9. package/esm2015/src/app/core/table/table.class.js +11 -11
  10. package/esm2015/src/app/shared/functions.js +60 -1
  11. package/esm2015/src/app/shared/material/autocomplete/material.autocomplete.js +4 -5
  12. package/esm2015/src/app/shared/material/autocomplete/testing/autocomplete.test.js +7 -10
  13. package/esm2015/src/app/shared/material/chips/material.chips.js +2 -3
  14. package/esm2015/src/app/shared/material/chips/testing/chips.test.js +2 -3
  15. package/esm2015/src/app/shared/material/datetime/material.date.js +2 -3
  16. package/esm2015/src/app/shared/material/datetime/material.datetime.js +2 -3
  17. package/esm2015/src/app/shared/pipes/arrays.pipe.js +1 -1
  18. package/esm2015/src/app/shared/pipes/form.pipes.js +4 -2
  19. package/esm2015/src/app/shared/pipes/highlight.pipe.js +4 -4
  20. package/esm2015/src/app/shared/pipes/string.pipes.js +18 -7
  21. package/esm2015/src/app/shared/services/memory-entity-service.class.js +1 -13
  22. package/esm2015/src/app/shared/types.js +1 -1
  23. package/esm2015/src/app/social/job/job.model.js +24 -0
  24. package/esm2015/src/app/social/job/job.module.js +25 -0
  25. package/esm2015/src/app/social/job/job.service.js +53 -0
  26. package/esm2015/src/app/social/job/progression/job-progression.component.js +192 -0
  27. package/esm2015/src/app/social/job/progression/job-progression.list.js +20 -0
  28. package/esm2015/src/app/social/message/message.module.js +25 -0
  29. package/esm2015/src/app/social/social.errors.js +5 -2
  30. package/esm2015/src/app/social/social.module.js +10 -15
  31. package/esm2015/src/app/social/user-event/notification/user-event-notification.component.js +99 -0
  32. package/esm2015/src/app/social/user-event/notification/user-event-notification.list.js +39 -0
  33. package/esm2015/src/app/social/user-event/user-event.model.js +58 -2
  34. package/esm2015/src/app/social/user-event/user-event.module.js +28 -0
  35. package/esm2015/src/app/social/user-event/user-event.service.js +299 -168
  36. package/esm2015/src/app/social/user-event/user-events.table.js +17 -12
  37. package/esm2015/sumaris-net.ngx-components.js +8 -6
  38. package/fesm2015/sumaris-net.ngx-components.js +930 -284
  39. package/fesm2015/sumaris-net.ngx-components.js.map +1 -1
  40. package/package.json +2 -3
  41. package/public_api.d.ts +7 -2
  42. package/src/app/core/icon/icon.component.d.ts +4 -4
  43. package/src/app/core/table/table.class.d.ts +6 -8
  44. package/src/app/shared/functions.d.ts +9 -0
  45. package/src/app/shared/material/autocomplete/testing/autocomplete.test.d.ts +2 -1
  46. package/src/app/shared/material/badge/badge-icon.test.d.ts +1 -1
  47. package/src/app/shared/pipes/form.pipes.d.ts +2 -1
  48. package/src/app/shared/services/memory-entity-service.class.d.ts +0 -6
  49. package/src/app/shared/types.d.ts +4 -0
  50. package/src/app/social/job/job.model.d.ts +32 -0
  51. package/src/app/social/job/job.module.d.ts +2 -0
  52. package/src/app/social/job/job.service.d.ts +23 -0
  53. package/src/app/social/job/progression/job-progression.component.d.ts +43 -0
  54. package/src/app/social/job/progression/job-progression.list.d.ts +12 -0
  55. package/src/app/social/message/message.module.d.ts +2 -0
  56. package/src/app/social/social.errors.d.ts +3 -0
  57. package/src/app/social/social.module.d.ts +8 -0
  58. package/src/app/social/user-event/notification/user-event-notification.component.d.ts +25 -0
  59. package/src/app/social/user-event/notification/user-event-notification.list.d.ts +20 -0
  60. package/src/app/social/user-event/user-event.model.d.ts +51 -6
  61. package/src/app/social/user-event/user-event.module.d.ts +2 -0
  62. package/src/app/social/user-event/user-event.service.d.ts +79 -51
  63. package/src/app/social/user-event/user-events.table.d.ts +6 -6
  64. package/src/assets/i18n/en-US.json +15 -2
  65. package/src/assets/i18n/en.json +15 -2
  66. package/src/assets/i18n/fr.json +14 -1
  67. package/src/theme/_ngx-components.scss +7 -0
  68. package/sumaris-net.ngx-components.d.ts +7 -5
  69. package/sumaris-net.ngx-components.metadata.json +1 -1
  70. package/esm2015/src/app/shared/services.js +0 -61
  71. package/src/app/shared/services.d.ts +0 -9
@@ -241,6 +241,64 @@ function changeCaseToUnderscore(value) {
241
241
  function removeDiacritics(text) {
242
242
  return text.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
243
243
  }
244
+ function suggestFromArray(items, value, opts) {
245
+ if (isNotNil(value) && typeof value === 'object')
246
+ return { data: [value] };
247
+ value = (typeof value === 'string' && value !== '*') && value.toUpperCase() || undefined;
248
+ const keys = opts && (opts.searchAttribute && [opts.searchAttribute] || opts.searchAttributes) || ['label'];
249
+ if (!items) {
250
+ return { data: [], total: 0 };
251
+ }
252
+ // Filter items
253
+ let filteredItems = items;
254
+ if (isNotNilOrBlank(value)) {
255
+ // If wildcard, search using regexp
256
+ if (value.indexOf('*') !== -1) {
257
+ value = value.replace(/[*]+/g, '.*');
258
+ filteredItems = filteredItems.filter(v => keys.findIndex(key => matchUpperCase(getPropertyByPathAsString(v, key), value)) !== -1);
259
+ }
260
+ // Else (no wildcard), search using startsWith
261
+ else {
262
+ filteredItems = filteredItems.filter(v => keys.findIndex(key => startsWithUpperCase(getPropertyByPathAsString(v, key), value)) !== -1);
263
+ }
264
+ }
265
+ // Exclude ids
266
+ if (isNotEmptyArray(opts === null || opts === void 0 ? void 0 : opts.excludedIds)) {
267
+ filteredItems = filteredItems.filter(v => !opts.excludedIds.includes(+v['id']));
268
+ }
269
+ // Sort
270
+ if (!opts || opts.skipSort !== true) {
271
+ // Make to copy input items, before sorting (if not yet already done)
272
+ if (filteredItems === items)
273
+ filteredItems = filteredItems.slice();
274
+ // Sort by keys
275
+ filteredItems.sort(propertiesPathComparator(keys));
276
+ }
277
+ const total = filteredItems.length || 0;
278
+ // Pagination (truncate if need)
279
+ let pageData = filteredItems;
280
+ if (opts) {
281
+ if ((opts.offset || 0) > 0) {
282
+ pageData = pageData.slice(opts.offset);
283
+ }
284
+ if (isNotNil(opts.size) && pageData.length > opts.size) {
285
+ pageData = pageData.slice(0, opts.size);
286
+ }
287
+ }
288
+ const result = {
289
+ data: pageData,
290
+ total
291
+ };
292
+ // Add fetch more capability, if total was fetched
293
+ const nextOffset = (opts && opts.offset || 0) + pageData.length;
294
+ if (nextOffset < total) {
295
+ result.fetchMore = (_) => __awaiter(this, void 0, void 0, function* () {
296
+ return suggestFromArray(filteredItems, undefined /*OPTIMISATION: already applied*/, Object.assign(Object.assign({}, opts), { offset: nextOffset, skipSort: true, excludedIds: undefined // OPTIMISATION: already filtered
297
+ }));
298
+ });
299
+ }
300
+ return result;
301
+ }
244
302
  function suggestFromStringArray(values, value, options) {
245
303
  value = (typeof value === 'string' && value !== '*') && value.toUpperCase() || undefined;
246
304
  if (isNilOrBlank(value))
@@ -1042,65 +1100,6 @@ const isMobile = (win) => {
1042
1100
  return _cache.mobile;
1043
1101
  };
1044
1102
 
1045
- function suggestFromArray(items, value, opts) {
1046
- if (isNotNil(value) && typeof value === 'object')
1047
- return { data: [value] };
1048
- value = (typeof value === 'string' && value !== '*') && value.toUpperCase() || undefined;
1049
- const keys = opts && (opts.searchAttribute && [opts.searchAttribute] || opts.searchAttributes) || ['label'];
1050
- if (!items) {
1051
- return { data: [], total: 0 };
1052
- }
1053
- // Filter items
1054
- let filteredItems = items;
1055
- if (isNotNilOrBlank(value)) {
1056
- // If wildcard, search using regexp
1057
- if (value.indexOf('*') !== -1) {
1058
- value = value.replace(/[*]+/g, '.*');
1059
- filteredItems = filteredItems.filter(v => keys.findIndex(key => matchUpperCase(getPropertyByPathAsString(v, key), value)) !== -1);
1060
- }
1061
- // Else (no wildcard), search using startsWith
1062
- else {
1063
- filteredItems = filteredItems.filter(v => keys.findIndex(key => startsWithUpperCase(getPropertyByPathAsString(v, key), value)) !== -1);
1064
- }
1065
- }
1066
- // Exclude ids
1067
- if (isNotEmptyArray(opts === null || opts === void 0 ? void 0 : opts.excludedIds)) {
1068
- filteredItems = filteredItems.filter(v => !opts.excludedIds.includes(+v['id']));
1069
- }
1070
- // Sort
1071
- if (!opts || opts.skipSort !== true) {
1072
- // Make to copy input items, before sorting (if not yet already done)
1073
- if (filteredItems === items)
1074
- filteredItems = filteredItems.slice();
1075
- // Sort by keys
1076
- filteredItems.sort(propertiesPathComparator(keys));
1077
- }
1078
- const total = filteredItems.length || 0;
1079
- // Pagination (truncate if need)
1080
- let pageData = filteredItems;
1081
- if (opts) {
1082
- if ((opts.offset || 0) > 0) {
1083
- pageData = pageData.slice(opts.offset);
1084
- }
1085
- if (isNotNil(opts.size) && pageData.length > opts.size) {
1086
- pageData = pageData.slice(0, opts.size);
1087
- }
1088
- }
1089
- const result = {
1090
- data: pageData,
1091
- total
1092
- };
1093
- // Add fetch more capability, if total was fetched
1094
- const nextOffset = (opts && opts.offset || 0) + pageData.length;
1095
- if (nextOffset < total) {
1096
- result.fetchMore = (_) => __awaiter(this, void 0, void 0, function* () {
1097
- return suggestFromArray(filteredItems, undefined /*OPTIMISATION: already applied*/, Object.assign(Object.assign({}, opts), { offset: nextOffset, skipSort: true, excludedIds: undefined // OPTIMISATION: already filtered
1098
- }));
1099
- });
1100
- }
1101
- return result;
1102
- }
1103
-
1104
1103
  class MatAutocompleteConfigHolder {
1105
1104
  constructor(options) {
1106
1105
  this.options = options;
@@ -1361,11 +1360,11 @@ class MatAutocompleteField {
1361
1360
  // Search text (no distinctUntilChanged() because of the debounceTime() pipe)
1362
1361
  this.formControl.valueChanges
1363
1362
  .pipe(filter(value => this.enabled && isNotNilString(value)),
1364
- // Mark as loading (to show loading spinner) - as soon as possible, and before debounceTime
1363
+ // Mark as loading (to show loading spinner) - earlier as possible, so before the debounce time
1365
1364
  tap(_ => this.showLoadingSpinner && this.markAsLoading()),
1366
1365
  // Filter if not enough character
1367
1366
  filter(value => {
1368
- if (((value === null || value === void 0 ? void 0 : value.length) || 0) < this.suggestLengthThreshold) {
1367
+ if (value.length <= this.suggestLengthThreshold) {
1369
1368
  // Update display value, to enable the message "not enough characters"
1370
1369
  this.displayValue = value;
1371
1370
  return false;
@@ -2329,9 +2328,9 @@ class HighlightPipe {
2329
2328
  const cleanedSearchText = removeDiacritics(searchRegexp).toUpperCase();
2330
2329
  const index = removeDiacritics(value.toUpperCase()).indexOf(cleanedSearchText);
2331
2330
  if (index !== -1) {
2332
- return value.substring(0, index - 1) +
2333
- '<b>' + value.substring(index, cleanedSearchText.length) + '</b>' +
2334
- value.substring(index + cleanedSearchText.length);
2331
+ return value.substr(0, index - 1) +
2332
+ '<b>' + value.substr(index, cleanedSearchText.length) + '</b>' +
2333
+ value.substr(index + cleanedSearchText.length);
2335
2334
  }
2336
2335
  return value;
2337
2336
  }
@@ -2729,50 +2728,60 @@ class IsNotNilOrBlankPipe {
2729
2728
  return isNotNilOrBlank(value);
2730
2729
  }
2731
2730
  }
2731
+ IsNotNilOrBlankPipe.ɵprov = ɵɵdefineInjectable({ factory: function IsNotNilOrBlankPipe_Factory() { return new IsNotNilOrBlankPipe(); }, token: IsNotNilOrBlankPipe, providedIn: "root" });
2732
2732
  IsNotNilOrBlankPipe.decorators = [
2733
2733
  { type: Pipe, args: [{
2734
2734
  name: 'isNotNilOrBlank'
2735
- },] }
2735
+ },] },
2736
+ { type: Injectable, args: [{ providedIn: 'root' },] }
2736
2737
  ];
2737
2738
  class IsNilOrBlankPipe {
2738
2739
  transform(value) {
2739
2740
  return isNilOrBlank(value);
2740
2741
  }
2741
2742
  }
2743
+ IsNilOrBlankPipe.ɵprov = ɵɵdefineInjectable({ factory: function IsNilOrBlankPipe_Factory() { return new IsNilOrBlankPipe(); }, token: IsNilOrBlankPipe, providedIn: "root" });
2742
2744
  IsNilOrBlankPipe.decorators = [
2743
2745
  { type: Pipe, args: [{
2744
2746
  name: 'isNilOrBlank'
2745
- },] }
2747
+ },] },
2748
+ { type: Injectable, args: [{ providedIn: 'root' },] }
2746
2749
  ];
2747
2750
  class ToStringPipe {
2748
2751
  transform(value) {
2749
2752
  return (value !== null && value !== undefined) ? value.toString() : '';
2750
2753
  }
2751
2754
  }
2755
+ ToStringPipe.ɵprov = ɵɵdefineInjectable({ factory: function ToStringPipe_Factory() { return new ToStringPipe(); }, token: ToStringPipe, providedIn: "root" });
2752
2756
  ToStringPipe.decorators = [
2753
2757
  { type: Pipe, args: [{
2754
2758
  name: 'toString'
2755
- },] }
2759
+ },] },
2760
+ { type: Injectable, args: [{ providedIn: 'root' },] }
2756
2761
  ];
2757
2762
  class StrLengthPipe {
2758
2763
  transform(value) {
2759
2764
  return value && value.length || 0;
2760
2765
  }
2761
2766
  }
2767
+ StrLengthPipe.ɵprov = ɵɵdefineInjectable({ factory: function StrLengthPipe_Factory() { return new StrLengthPipe(); }, token: StrLengthPipe, providedIn: "root" });
2762
2768
  StrLengthPipe.decorators = [
2763
2769
  { type: Pipe, args: [{
2764
2770
  name: 'strLength'
2765
- },] }
2771
+ },] },
2772
+ { type: Injectable, args: [{ providedIn: 'root' },] }
2766
2773
  ];
2767
2774
  class StrIncludesPipe {
2768
2775
  transform(value, searchString, position) {
2769
2776
  return (value === null || value === void 0 ? void 0 : value.includes(searchString, position)) || false;
2770
2777
  }
2771
2778
  }
2779
+ StrIncludesPipe.ɵprov = ɵɵdefineInjectable({ factory: function StrIncludesPipe_Factory() { return new StrIncludesPipe(); }, token: StrIncludesPipe, providedIn: "root" });
2772
2780
  StrIncludesPipe.decorators = [
2773
2781
  { type: Pipe, args: [{
2774
2782
  name: 'strIncludes'
2775
- },] }
2783
+ },] },
2784
+ { type: Injectable, args: [{ providedIn: 'root' },] }
2776
2785
  ];
2777
2786
 
2778
2787
  class TranslateContextService {
@@ -4223,7 +4232,8 @@ FormGetGroupPipe.decorators = [
4223
4232
  },] }
4224
4233
  ];
4225
4234
  class FormGetValuePipe {
4226
- constructor(_ref) {
4235
+ constructor(adapter, _ref) {
4236
+ this.adapter = adapter;
4227
4237
  this._ref = _ref;
4228
4238
  this.value = undefined;
4229
4239
  this._lastControl = null;
@@ -4288,6 +4298,7 @@ FormGetValuePipe.decorators = [
4288
4298
  },] }
4289
4299
  ];
4290
4300
  FormGetValuePipe.ctorParameters = () => [
4301
+ { type: FormErrorTranslator },
4291
4302
  { type: ChangeDetectorRef }
4292
4303
  ];
4293
4304
 
@@ -5158,8 +5169,7 @@ class MatDate {
5158
5169
  set tabindex(value) {
5159
5170
  if (this._tabindex !== value) {
5160
5171
  this._tabindex = value;
5161
- if (!this._writing)
5162
- this.updateTabIndex();
5172
+ setTimeout(() => this.updateTabIndex());
5163
5173
  }
5164
5174
  }
5165
5175
  get tabindex() {
@@ -5518,8 +5528,7 @@ class MatDateTime {
5518
5528
  set tabindex(value) {
5519
5529
  if (this._tabindex !== value) {
5520
5530
  this._tabindex = value;
5521
- if (!this._writing)
5522
- this.updateTabIndex();
5531
+ setTimeout(() => this.updateTabIndex());
5523
5532
  }
5524
5533
  }
5525
5534
  get tabindex() {
@@ -18204,18 +18213,6 @@ class InMemoryEntitiesService extends EntitiesService {
18204
18213
  return this._equalsFn(d1, d2);
18205
18214
  return d1 && d1.equals ? d1.equals(d2) : EntityUtils.equals(d1, d2, 'id');
18206
18215
  }
18207
- /**
18208
- * Count data stored by the service (without hidden data).
18209
- * @param opts
18210
- */
18211
- get count() {
18212
- var _a;
18213
- return (((_a = this.data) === null || _a === void 0 ? void 0 : _a.length) || 0);
18214
- }
18215
- get hiddenCount() {
18216
- var _a;
18217
- return ((_a = this._hiddenData) === null || _a === void 0 ? void 0 : _a.length) || 0;
18218
- }
18219
18216
  /* -- protected methods -- */
18220
18217
  filter(data, _filter, hiddenData) {
18221
18218
  // if filter is DataFilter instance, use its test function
@@ -21434,7 +21431,7 @@ class MenuComponent {
21434
21431
  MenuComponent.decorators = [
21435
21432
  { type: Component, args: [{
21436
21433
  selector: 'app-menu',
21437
- template: "<ion-split-pane #splitPane [contentId]=\"contentId\" (swiperight)=\"onSwipeRight($event)\">\n\n <ion-menu [id]=\"id\" [menuId]=\"menuId\" contentId=\"menu-content\">\n <ion-header>\n\n <ion-toolbar @fadeInAnimation *ngIf=\"isLogin; else notLogin\" class=\"ion-toolbar-top\">\n <ion-grid>\n <ion-row>\n <ion-col size=\"4\">\n <button type=\"button\" mat-flat-button\n class=\"user-avatar\" [class.primary]=\"!accountAvatar\"\n [style.background-image]=\"'url('+(accountAvatar||'./assets/img/person.png')+')'\"\n [routerLink]=\"['/account']\"\n routerDirection=\"root\"\n routerLinkActive=\"ion-color-primary\"\n (click)=\"close()\">\n </button>\n </ion-col>\n <ion-col size=\"8\" class=\"user-logo\">\n <img *ngIf=\"logo\" src=\"{{logo}}\" [title]=\"'APP_NAME'|translate: {appName: appName}\" width=\"108px;\"/>\n <span *ngIf=\"!logo\" width=\"108px;\">{{appName}}</span>\n </ion-col>\n </ion-row>\n <ion-row class=\"ion-no-padding\">\n\n <button mat-button type=\"button\"\n [routerLink]=\"['/account']\"\n routerDirection=\"root\"\n routerLinkActive=\"ion-color-primary\"\n (click)=\"close()\">\n <ion-label color=\"primary\" class=\"ion-text-wrap ion-text-start\">\n <h3 class=\"no-margin username\">\n <b>{{accountName}}</b>\n </h3>\n <h4>{{accountEmail}}</h4>\n </ion-label>\n </button>\n\n </ion-row>\n </ion-grid>\n\n </ion-toolbar>\n\n <!-- User not logged -->\n <ng-template #notLogin>\n <mat-toolbar class=\"ion-padding\" @fadeInAnimation\n style=\"height: unset; display: block; margin: auto; text-align: center;\">\n <img *ngIf=\"logo\" src=\"{{logo}}\" [title]=\"'APP_NAME'|translate: {appName: appName}\" width=\"150px;\">\n <span *ngIf=\"!logo\" style=\"width: 150px\">{{appName}}</span>\n </mat-toolbar>\n </ng-template>\n </ion-header>\n\n <ion-content [class.has-user-header]=\"isLogin\">\n\n <ion-list lines=\"none\">\n <ion-menu-toggle auto-hide=\"false\"\n [class.flex-spacer]=\"item.cssClass == 'flex-spacer'\"\n *ngFor=\"let item of $items | async\">\n\n <!-- link -->\n <ion-item *ngIf=\"!loading && item.path\"\n @fadeInAnimation\n class=\"{{item.cssClass}} {{item.color}} text-1x\"\n tappable\n [routerLink]=\"item.path\"\n routerDirection=\"root\"\n routerLinkActive=\"selected\"\n [routerLinkActiveOptions]=\"{exact: (item.path === '/')}\">\n <ion-icon slot=\"start\" *ngIf=\"item.icon\" [name]=\"item.icon\"></ion-icon>\n <mat-icon slot=\"start\" *ngIf=\"item.matIcon\">{{item.matIcon}}</mat-icon>\n <ion-label translate>{{item.title}}</ion-label>\n </ion-item>\n\n <!-- action -->\n <ion-item *ngIf=\"!loading && item.action\"\n @fadeInAnimation\n class=\"{{item.cssClass}} {{item.color}} text-1x\"\n tappable\n (click)=\"doAction(item.action, $event)\">\n <ion-icon slot=\"start\" *ngIf=\"item.icon\" [name]=\"item.icon\"></ion-icon>\n <mat-icon slot=\"start\" *ngIf=\"item.matIcon\">{{item.matIcon}}</mat-icon>\n <ion-label translate>{{item.title}}</ion-label>\n </ion-item>\n\n <!-- divider -->\n <ion-item-divider @fadeInAnimation\n class=\"{{item.cssClass}} {{item.color}}\"\n *ngIf=\"!loading && !item.path && !item.action\">\n <ion-label translate>{{item.title}}</ion-label>\n </ion-item-divider>\n\n </ion-menu-toggle>\n </ion-list>\n </ion-content>\n\n <ion-footer class=\"hidden-xs hidden-sm\">\n <ion-toolbar>\n\n <ion-buttons slot=\"start\">\n <ion-button mat-icon-button color=\"accent\" (click)=\"openAboutModal($event)\">\n <mat-icon slot=\"icon-only\">help_outline</mat-icon>\n </ion-button>\n </ion-buttons>\n\n <ion-title (click)=\"openAboutModal($event)\" color=\"medium\">\n {{'MENU.FOOTER_VERSION_ABOUT'| translate: {version: appVersion} }}\n </ion-title>\n\n <ion-buttons slot=\"end\">\n <button mat-icon-button color=\"accent\" (click)=\"toggleSplitPaneShow($event)\"\n class=\"hidden-xs hidden-sm hidden-md\"\n [title]=\"(splitPane.when ? 'COMMON.BTN_HIDE_MENU' : 'COMMON.BTN_SHOW_MENU') |translate\">\n <mat-icon><span>{{splitPane.when ? '&#xab;' : '&#xbb;'}}</span></mat-icon>\n </button>\n </ion-buttons>\n </ion-toolbar>\n </ion-footer>\n\n </ion-menu>\n\n <ng-content></ng-content>\n\n</ion-split-pane>\n",
21434
+ template: "<ion-split-pane #splitPane [contentId]=\"contentId\" (swiperight)=\"onSwipeRight($event)\">\n\n <ion-menu [id]=\"id\" [menuId]=\"menuId\" contentId=\"menu-content\">\n <ion-header>\n\n <ion-toolbar @fadeInAnimation *ngIf=\"isLogin; else notLogin\" class=\"ion-toolbar-top\">\n <ion-grid>\n <ion-row>\n <ion-col size=\"4\">\n <button type=\"button\" mat-flat-button\n class=\"user-avatar\" [class.primary]=\"!accountAvatar\"\n [style.background-image]=\"'url('+(accountAvatar||'./assets/img/person.png')+')'\"\n [routerLink]=\"['/account']\"\n routerDirection=\"root\"\n routerLinkActive=\"ion-color-primary\"\n (click)=\"close()\">\n </button>\n </ion-col>\n <ion-col size=\"8\" class=\"user-logo\">\n <img *ngIf=\"logo\" src=\"{{logo}}\" [title]=\"'APP_NAME'|translate: {appName: appName}\" width=\"108px;\"/>\n <span *ngIf=\"!logo\" width=\"108px;\">{{appName}}</span>\n </ion-col>\n </ion-row>\n <ion-row class=\"ion-no-padding\">\n\n <button mat-button type=\"button\"\n [routerLink]=\"['/account']\"\n routerDirection=\"root\"\n routerLinkActive=\"ion-color-primary\"\n (click)=\"close()\">\n <ion-label color=\"primary\" class=\"ion-text-wrap ion-text-start\">\n <h3 class=\"no-margin username\">\n <b>{{accountName}}</b>\n </h3>\n <h4>{{accountEmail}}</h4>\n </ion-label>\n </button>\n\n<!-- todo ajouter ici les 2 composants du module social -->\n\n </ion-row>\n </ion-grid>\n\n </ion-toolbar>\n\n <!-- User not logged -->\n <ng-template #notLogin>\n <mat-toolbar class=\"ion-padding\" @fadeInAnimation\n style=\"height: unset; display: block; margin: auto; text-align: center;\">\n <img *ngIf=\"logo\" src=\"{{logo}}\" [title]=\"'APP_NAME'|translate: {appName: appName}\" width=\"150px;\">\n <span *ngIf=\"!logo\" style=\"width: 150px\">{{appName}}</span>\n </mat-toolbar>\n </ng-template>\n </ion-header>\n\n <ion-content [class.has-user-header]=\"isLogin\">\n\n <ion-list lines=\"none\">\n <ion-menu-toggle auto-hide=\"false\"\n [class.flex-spacer]=\"item.cssClass == 'flex-spacer'\"\n *ngFor=\"let item of $items | async\">\n\n <!-- link -->\n <ion-item *ngIf=\"!loading && item.path\"\n @fadeInAnimation\n class=\"{{item.cssClass}} {{item.color}} text-1x\"\n tappable\n [routerLink]=\"item.path\"\n routerDirection=\"root\"\n routerLinkActive=\"selected\"\n [routerLinkActiveOptions]=\"{exact: (item.path === '/')}\">\n <ion-icon slot=\"start\" *ngIf=\"item.icon\" [name]=\"item.icon\"></ion-icon>\n <mat-icon slot=\"start\" *ngIf=\"item.matIcon\">{{item.matIcon}}</mat-icon>\n <ion-label translate>{{item.title}}</ion-label>\n </ion-item>\n\n <!-- action -->\n <ion-item *ngIf=\"!loading && item.action\"\n @fadeInAnimation\n class=\"{{item.cssClass}} {{item.color}} text-1x\"\n tappable\n (click)=\"doAction(item.action, $event)\">\n <ion-icon slot=\"start\" *ngIf=\"item.icon\" [name]=\"item.icon\"></ion-icon>\n <mat-icon slot=\"start\" *ngIf=\"item.matIcon\">{{item.matIcon}}</mat-icon>\n <ion-label translate>{{item.title}}</ion-label>\n </ion-item>\n\n <!-- divider -->\n <ion-item-divider @fadeInAnimation\n class=\"{{item.cssClass}} {{item.color}}\"\n *ngIf=\"!loading && !item.path && !item.action\">\n <ion-label translate>{{item.title}}</ion-label>\n </ion-item-divider>\n\n </ion-menu-toggle>\n </ion-list>\n </ion-content>\n\n <ion-footer class=\"hidden-xs hidden-sm\">\n <ion-toolbar>\n\n <ion-buttons slot=\"start\">\n <ion-button mat-icon-button color=\"accent\" (click)=\"openAboutModal($event)\">\n <mat-icon slot=\"icon-only\">help_outline</mat-icon>\n </ion-button>\n </ion-buttons>\n\n <ion-title (click)=\"openAboutModal($event)\" color=\"medium\">\n {{'MENU.FOOTER_VERSION_ABOUT'| translate: {version: appVersion} }}\n </ion-title>\n\n <ion-buttons slot=\"end\">\n <button mat-icon-button color=\"accent\" (click)=\"toggleSplitPaneShow($event)\"\n class=\"hidden-xs hidden-sm hidden-md\"\n [title]=\"(splitPane.when ? 'COMMON.BTN_HIDE_MENU' : 'COMMON.BTN_SHOW_MENU') |translate\">\n <mat-icon><span>{{splitPane.when ? '&#xab;' : '&#xbb;'}}</span></mat-icon>\n </button>\n </ion-buttons>\n </ion-toolbar>\n </ion-footer>\n\n </ion-menu>\n\n <ng-content></ng-content>\n\n</ion-split-pane>\n",
21438
21435
  animations: [fadeInAnimation],
21439
21436
  changeDetection: ChangeDetectionStrategy.OnPush,
21440
21437
  styles: ["ion-menu{--ion-item-background:transparent;--ion-item-divider-background:transparent;--ion-item-icon-color:var(--ion-color-primary-tint);--ion-item-text-color:var(--ion-color-primary-tint);--ion-item-background-selected:var(--ion-color-secondary100);--ion-item-text-color-selected:var(--ion-color-primary);--ion-item-icon-color-selected:var(--ion-color-primary);--ion-item-text-color-disable:var(--ion-color-medium);--ion-item-icon-color-disable:var(--ion-color-medium)}ion-menu ion-header ion-text{color:var(--ion-color-primary)}ion-menu ion-header .user-avatar{background-size:cover;background-repeat:no-repeat;background-position:50%;background-color:var(--ion-color-secondary);border:1px solid var(--ion-color-primary);overflow:hidden!important;font-size:var(--avatar-size,60px)!important;line-height:var(--avatar-size,60px);height:var(--avatar-size,60px)!important;width:var(--avatar-size,60px)!important;border-radius:50%;display:inline-block}ion-menu ion-header .user-logo{text-align:right}ion-menu ion-header .user-logo img{max-width:120px;max-height:var(--avatar-size,60px);width:auto}ion-menu ion-header .username{padding-top:0;margin-top:0;margin-bottom:0;width:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}ion-menu ion-header button[mat-button]{padding:0;text-align:start!important;width:100%}ion-menu .scroll-content{margin-top:79px!important}ion-menu .has-user-header .scroll-content{margin-top:188px!important}ion-menu ion-content.has-profile-header{--offset-top:188px}ion-menu ion-content.no-profile-header{--offset-top:79px}ion-menu ion-content ion-list{min-height:100%;display:flex;flex-direction:column;justify-content:flex-start}ion-menu ion-content ion-list ion-menu-toggle.flex-spacer{flex:1 1 auto;display:flex;flex-direction:column;justify-content:flex-end}ion-menu ion-content ion-list ion-item.primary{--ion-item-icon-color:var(--ion-color-primary-tint);--ion-item-text-color:var(--ion-color-primary-tint)}ion-menu ion-content ion-list ion-item.secondary{--ion-item-icon-color:var(--ion-color-secondary-tint);--ion-item-text-color:var(--ion-color-secondary-tint)}ion-menu ion-content ion-list ion-item.tertiary{--ion-item-icon-color:var(--ion-color-tertiary-tint);--ion-item-text-color:var(--ion-color-tertiary-tint)}ion-menu ion-content ion-list ion-item.danger{--ion-item-icon-color:var(--ion-color-danger-tint);--ion-item-text-color:var(--ion-color-danger-tint)}ion-menu ion-content ion-list ion-item.medium{--ion-item-icon-color:var(--ion-color-medium-shade);--ion-item-text-color:var(--ion-color-medium-shade)}ion-menu ion-content ion-list ion-item.dark{--ion-item-icon-color:var(--ion-color-dark-tint);--ion-item-text-color:var(--ion-color-dark-tint)}ion-menu ion-content ion-list ion-item ion-icon,ion-menu ion-content ion-list ion-item mat-icon{color:var(--ion-item-icon-color)!important;fill:currentColor;stroke:currentColor}ion-menu ion-content ion-list ion-item ion-label,ion-menu ion-content ion-list ion-item ion-text{color:var(--ion-item-text-color)!important}ion-menu ion-content ion-list ion-item.selected{background-color:var(--ion-item-background-selected)!important;--color-hover:var(--ion-item-background-selected)!important}ion-menu ion-content ion-list ion-item.selected ion-icon,ion-menu ion-content ion-list ion-item.selected mat-icon{color:var(--ion-item-icon-color-selected)!important;fill:currentColor;stroke:currentColor}ion-menu ion-content ion-list ion-item.selected ion-label,ion-menu ion-content ion-list ion-item.selected ion-text{color:var(--ion-item-text-color-selected)!important}ion-menu ion-content ion-list ion-item.selected:hover{--color-hover:var(--ion-item-background-selected)!important;--ion-item-icon-color-selected:var(--ion-item-icon-color)!important;--ion-item-text-color-selected:var(--ion-item-text-color)!important}ion-menu ion-footer{display:block!important}ion-menu ion-footer ion-toolbar ion-title{cursor:pointer;font-size:12pt;font-weight:400;text-align:center;padding:0 8px}ion-menu ion-footer ion-toolbar ion-button{--width:40px}@media screen and (max-width:767px){ion-menu ion-footer{display:none!important;visibility:hidden!important}}@media screen and (min-width:768px){ion-menu ion-scroll{overflow-y:auto!important}ion-menu .user-avatar{font-size:var(--avatar-size,80px)!important;line-height:var(--avatar-size,80px);height:var(--avatar-size,80px)!important;width:var(--avatar-size,80px)!important}ion-menu .user-logo img{max-height:var(--avatar-size,80px)}}"]
@@ -21498,13 +21495,14 @@ class AppIconComponent {
21498
21495
  this.icon = value.icon;
21499
21496
  this.matIcon = value.matIcon;
21500
21497
  this.matSvgIcon = value.matSvgIcon;
21498
+ this.color = value.color;
21501
21499
  }
21502
21500
  ;
21503
21501
  }
21504
21502
  AppIconComponent.decorators = [
21505
21503
  { type: Component, args: [{
21506
21504
  selector: 'app-icon',
21507
- template: "<ion-icon *ngIf=\"icon; else matIconTemplate\" slot=\"icon-only\"\n [color]=\"color\"\n [name]=\"icon\"\n></ion-icon>\n<ng-template #matIconTemplate>\n <mat-icon [svgIcon]=\"matSvgIcon\"\n [style.color]=\"'var(--ion-color-'+color+')'\">{{matIcon}}</mat-icon>\n</ng-template>\n\n"
21505
+ template: "<ion-icon *ngIf=\"icon; else matIconTemplate\" slot=\"icon-only\"\n [color]=\"color\"\n [name]=\"icon\"\n [style.height.px]=\"height\"\n [style.width.px]=\"width\"\n></ion-icon>\n<ng-template #matIconTemplate>\n <mat-icon [svgIcon]=\"matSvgIcon\"\n [style.color]=\"'var(--ion-color-'+color+')'\"\n [style.height.px]=\"height\"\n [style.width.px]=\"width\"\n [style.font-size.px]=\"height || width\"\n >{{matIcon}}</mat-icon>\n</ng-template>\n\n"
21508
21506
  },] }
21509
21507
  ];
21510
21508
  AppIconComponent.propDecorators = {
@@ -21512,6 +21510,8 @@ AppIconComponent.propDecorators = {
21512
21510
  matIcon: [{ type: Input }],
21513
21511
  matSvgIcon: [{ type: Input }],
21514
21512
  color: [{ type: Input }],
21513
+ height: [{ type: Input }],
21514
+ width: [{ type: Input }],
21515
21515
  ref: [{ type: Input }]
21516
21516
  };
21517
21517
 
@@ -23013,7 +23013,7 @@ class AppTable {
23013
23013
  .then(() => this.editRowById(event, row.id, { focusColumn: this.lastUserColumn }));
23014
23014
  return true;
23015
23015
  }
23016
- // Edit previous row
23016
+ // Edit next row
23017
23017
  this.editRow(event, row, { focusColumn: this.lastUserColumn });
23018
23018
  return true;
23019
23019
  }
@@ -23036,7 +23036,8 @@ class AppTable {
23036
23036
  if (id < 0)
23037
23037
  return;
23038
23038
  if (id >= this.visibleRowCount) {
23039
- this.addRow(event, undefined, opts);
23039
+ this.focusColumn = opts && opts.focusColumn || this.firstUserColumn;
23040
+ this.addRow(event);
23040
23041
  }
23041
23042
  else {
23042
23043
  const row = yield this.dataSource.getRow(id);
@@ -23109,7 +23110,7 @@ class AppTable {
23109
23110
  this.cancelExistingRow(event, row, opts);
23110
23111
  }
23111
23112
  }
23112
- addRow(event, insertAt, opts) {
23113
+ addRow(event, insertAt) {
23113
23114
  if (this.debug)
23114
23115
  console.debug('[table] Asking for new row...');
23115
23116
  if (!this._enabled)
@@ -23124,7 +23125,7 @@ class AppTable {
23124
23125
  return false;
23125
23126
  }
23126
23127
  // Add new row
23127
- this.addRowToTable(insertAt, opts);
23128
+ this.addRowToTable(insertAt);
23128
23129
  return true;
23129
23130
  }
23130
23131
  save(opts) {
@@ -23239,7 +23240,7 @@ class AppTable {
23239
23240
  * @param event
23240
23241
  * @param row
23241
23242
  * @param opts Use interactive=false to avoid user interaction (e.g. user confirmation)
23242
- * And to force deletion even if table is busy
23243
+ * Use skipIfLoading=false to force deletion even if table is busy
23243
23244
  */
23244
23245
  deleteRow(event, row, opts) {
23245
23246
  return __awaiter(this, void 0, void 0, function* () {
@@ -23252,7 +23253,7 @@ class AppTable {
23252
23253
  * @param event
23253
23254
  * @param rows
23254
23255
  * @param opts Use interactive=false to avoid user interaction (e.g. user confirmation)
23255
- * And to force deletion even if table is busy
23256
+ * Use skipIfLoading=false to force deletion even if table is busy
23256
23257
  */
23257
23258
  deleteRows(event, rows, opts) {
23258
23259
  var _a;
@@ -23265,8 +23266,8 @@ class AppTable {
23265
23266
  event === null || event === void 0 ? void 0 : event.preventDefault();
23266
23267
  if (!this._enabled || isEmptyArray(rows))
23267
23268
  return 0; // Skip is disabled, or no rows to delete
23268
- if (this.loading && (!opts || opts.interactive !== false)) {
23269
- console.warn('[app-table] Skip deleteRows() because table is busy (loading). Use opts.interactive = false to force deletion');
23269
+ if (this.loading && (!opts || opts.skipIfLoading !== false)) {
23270
+ console.warn('[app-table] Skip deleteRows() because table is busy (loading). Use opts.skipLoading = false to force deletion');
23270
23271
  return 0; // Skip if loading
23271
23272
  }
23272
23273
  // Make sure to keep newly created row
@@ -23789,10 +23790,9 @@ class AppTable {
23789
23790
  //if (this.debug) console.debug("[table] id = " + id);
23790
23791
  return id;
23791
23792
  }
23792
- addRowToTable(insertAt, opts) {
23793
+ addRowToTable(insertAt) {
23793
23794
  return __awaiter(this, void 0, void 0, function* () {
23794
23795
  this.focusFirstColumn = true;
23795
- this.focusColumn = (opts === null || opts === void 0 ? void 0 : opts.focusColumn) || this.firstUserColumn;
23796
23796
  yield this._dataSource.asyncCreateNew(insertAt);
23797
23797
  this.editedRow = this._dataSource.getRow(-1);
23798
23798
  // Emit start editing event
@@ -25823,7 +25823,10 @@ AppInMemoryTable.propDecorators = {
25823
25823
  const SocialErrorCodes = {
25824
25824
  LOAD_USER_EVENTS_ERROR: 50000,
25825
25825
  SAVE_USER_EVENT_ERROR: 50001,
25826
- SEND_MESSAGE_ERROR: 50003
25826
+ COUNT_USER_EVENT_ERROR: 50002,
25827
+ SEND_MESSAGE_ERROR: 50003,
25828
+ SUBSCRIBE_USER_EVENTS_ERROR: 50005,
25829
+ SUBSCRIBE_JOB_PROGRESSION_ERROR: 50010
25827
25830
  };
25828
25831
 
25829
25832
  var UserEvent_1;
@@ -25838,16 +25841,23 @@ let UserEvent = UserEvent_1 = class UserEvent extends Entity {
25838
25841
  }
25839
25842
  asObject(opts) {
25840
25843
  const target = super.asObject(opts);
25844
+ target.creationDate = toDateISOString(this.creationDate);
25845
+ target.readDate = toDateISOString(this.readDate);
25841
25846
  // Serialize content
25842
25847
  if (typeof target.content === 'object') {
25843
25848
  target.content = JSON.stringify(target.content);
25844
25849
  }
25850
+ delete target.avatar;
25851
+ delete target.avatarIcon;
25852
+ delete target.icon;
25853
+ delete target.actions;
25845
25854
  return target;
25846
25855
  }
25847
25856
  fromObject(source) {
25848
25857
  Object.assign(this, source); // Copy all properties
25849
25858
  super.fromObject(source);
25850
25859
  this.creationDate = fromDateISOString(source.creationDate);
25860
+ this.readDate = fromDateISOString(source.readDate);
25851
25861
  try {
25852
25862
  // Deserialize content
25853
25863
  if (typeof source.content === 'string' && source.content.startsWith('{')) {
@@ -25866,6 +25876,52 @@ let UserEvent = UserEvent_1 = class UserEvent extends Entity {
25866
25876
  UserEvent = UserEvent_1 = __decorate([
25867
25877
  EntityClass({ typename: 'UserEventVO' })
25868
25878
  ], UserEvent);
25879
+ // @dynamic
25880
+ let UserEventFilter = class UserEventFilter extends EntityFilter {
25881
+ constructor() {
25882
+ super(...arguments);
25883
+ this.types = [];
25884
+ this.levels = [];
25885
+ this.issuers = [];
25886
+ this.recipients = [];
25887
+ this.startDate = null;
25888
+ this.excludeRead = false;
25889
+ }
25890
+ fromObject(source, opts) {
25891
+ super.fromObject(source, opts);
25892
+ this.types = source.types || [];
25893
+ this.levels = source.levels || [];
25894
+ this.issuers = source.issuers || [];
25895
+ this.recipients = source.recipients || [];
25896
+ this.startDate = fromDateISOString(source.startDate);
25897
+ this.excludeRead = source.excludeRead || false;
25898
+ }
25899
+ buildFilter() {
25900
+ const filterFns = super.buildFilter();
25901
+ if (isNotEmptyArray(this.types)) {
25902
+ filterFns.push(t => this.types.includes(t.type));
25903
+ }
25904
+ if (isNotEmptyArray(this.levels)) {
25905
+ filterFns.push(t => this.levels.includes(t.level));
25906
+ }
25907
+ if (isNotEmptyArray(this.issuers)) {
25908
+ filterFns.push(t => this.issuers.includes(t.issuer));
25909
+ }
25910
+ if (isNotEmptyArray(this.recipients)) {
25911
+ filterFns.push(t => this.recipients.includes(t.recipient));
25912
+ }
25913
+ if (isNotNil(this.startDate)) {
25914
+ filterFns.push(t => this.startDate.isSameOrBefore(t.creationDate));
25915
+ }
25916
+ if (this.excludeRead === true) {
25917
+ filterFns.push(t => isNil(t.readSignature)); // todo or t.signature ?
25918
+ }
25919
+ return filterFns;
25920
+ }
25921
+ };
25922
+ UserEventFilter = __decorate([
25923
+ EntityClass({ typename: 'UserEventFilterVO' })
25924
+ ], UserEventFilter);
25869
25925
 
25870
25926
  const UserEventFragments = {
25871
25927
  userEvent: gql `fragment UserEventFragment on UserEventVO {
@@ -25893,179 +25949,162 @@ const UserEventFragments = {
25893
25949
  }`
25894
25950
  };
25895
25951
 
25896
- // @dynamic
25897
- let UserEventFilter = class UserEventFilter extends EntityFilter {
25898
- constructor() {
25899
- super(...arguments);
25900
- this.issuer = null;
25901
- this.recipient = null;
25902
- }
25903
- fromObject(source, opts) {
25904
- super.fromObject(source, opts);
25905
- this.issuer = source.issuer;
25906
- this.recipient = source.recipient;
25907
- }
25908
- buildFilter() {
25909
- const filterFns = super.buildFilter();
25910
- // issuer
25911
- if (this.issuer) {
25912
- filterFns.push(t => (t.issuer === this.issuer));
25913
- }
25914
- if (this.recipient) {
25915
- filterFns.push(t => (t.recipient === this.recipient));
25916
- }
25917
- return filterFns;
25918
- }
25919
- };
25920
- UserEventFilter = __decorate([
25921
- EntityClass({ typename: 'UserEventFilterVO' })
25922
- ], UserEventFilter);
25923
- const SaveQuery = gql `
25924
- mutation SaveUserEvent($data: UserEventVOInput){
25925
- data: saveUserEvent(userEvent: $data){
25926
- ...UserEventFragment
25927
- }
25928
- }
25929
- ${UserEventFragments.userEvent}
25930
- `;
25931
- const LoadAllQuery = gql `
25932
- query UserEvents($filter: UserEventFilterVOInput, $page: PageInput){
25933
- data: userEvents(filter: $filter, page: $page){
25934
- ...LightUserEventFragment
25935
- }
25936
- }
25937
- ${UserEventFragments.lightUserEvent}
25938
- `;
25939
- const DeleteByIdsMutation = gql `
25940
- mutation DeleteUserEvents($ids:[Int]){
25941
- deleteUserEvents(ids: $ids)
25942
- }
25943
- `;
25944
- const LoadAllWithContentQuery = gql `
25945
- query UserEventsWithContent($filter: UserEventFilterVOInput, $page: PageInput){
25946
- data: userEvents(filter: $filter, page: $page){
25947
- ...UserEventFragment
25948
- }
25949
- }
25950
- ${UserEventFragments.userEvent}
25951
- `;
25952
- class UserEventService extends BaseGraphqlService {
25953
- constructor(graphql, accountService, network, translate, toastController, environment) {
25952
+ const moment$6 = momentImported;
25953
+ const UserEventServiceToken = new InjectionToken('UserEventService');
25954
+ class AbstractUserEventService extends BaseGraphqlService {
25955
+ constructor(graphql, accountService, network, translate, queries, mutations, subscriptions, environment) {
25954
25956
  super(graphql, environment);
25955
25957
  this.graphql = graphql;
25956
25958
  this.accountService = accountService;
25957
25959
  this.network = network;
25958
25960
  this.translate = translate;
25959
- this.toastController = toastController;
25961
+ this.queries = queries;
25962
+ this.mutations = mutations;
25963
+ this.subscriptions = subscriptions;
25960
25964
  this.environment = environment;
25961
- this._userEventActions = [];
25965
+ this._count$ = new BehaviorSubject(0);
25966
+ this._subscriptions = new Subscription();
25967
+ this.listeners = [];
25968
+ this._logPrefix = '[user-event-service] ';
25962
25969
  // For DEV only
25963
- this._debug = environment && !environment.production;
25970
+ this._debug = !(environment === null || environment === void 0 ? void 0 : environment.production);
25971
+ // listen login/logout
25972
+ this._subscriptions.add(accountService.onLogin.subscribe(() => __awaiter(this, void 0, void 0, function* () {
25973
+ const count = yield this.count(undefined);
25974
+ if (count && this._debug) {
25975
+ console.debug(`${this._logPrefix}Received ${count} unread notifications`);
25976
+ }
25977
+ this._count$.next(count);
25978
+ })));
25979
+ }
25980
+ get userEventCount() {
25981
+ return this._count$.asObservable();
25982
+ }
25983
+ ngOnDestroy() {
25984
+ this._subscriptions.unsubscribe();
25964
25985
  }
25965
- /**
25966
- *
25967
- * @param offset
25968
- * @param size
25969
- * @param sortBy
25970
- * @param sortDirection
25971
- * @param filter
25972
- * @param options
25973
- * @deprecated use watchPage() instead
25974
- */
25975
25986
  watchAll(offset, size, sortBy, sortDirection, filter, options) {
25976
25987
  return this.watchPage({ offset, size, sortBy, sortDirection }, filter, options);
25977
25988
  }
25978
25989
  watchPage(page, filter, options) {
25990
+ var _a;
25979
25991
  let now = this._debug && Date.now();
25980
- //if (this._debug)
25981
- console.debug('[user-event-service] Loading user events...', filter);
25992
+ if (!filter) {
25993
+ filter = this.defaultFilter();
25994
+ }
25995
+ console.debug(`${this._logPrefix}Loading user events...`, filter);
25982
25996
  filter = this.asFilter(filter);
25983
25997
  // Force recipient to current issuer, if not admin and not specified
25984
- if (isNilOrBlank(filter.recipient) || !this.accountService.isAdmin()) {
25985
- const recipient = this.accountService.account.pubkey;
25986
- if (recipient !== filter.recipient) {
25998
+ if (isEmptyArray(filter === null || filter === void 0 ? void 0 : filter.recipients) || !this.accountService.isAdmin()) {
25999
+ const recipient = this.defaultRecipient();
26000
+ if (!((_a = filter === null || filter === void 0 ? void 0 : filter.recipients) === null || _a === void 0 ? void 0 : _a.includes(recipient))) {
25987
26001
  console.warn('[user-events-service] Force user event filter.recipient=' + recipient);
25988
- filter.recipient = recipient;
26002
+ filter.recipients = [recipient];
25989
26003
  }
25990
26004
  }
25991
- const withContent = options && options.withContent === true;
26005
+ const withContent = (options === null || options === void 0 ? void 0 : options.withContent) === true;
25992
26006
  return this.mutableWatchQuery({
25993
26007
  queryName: withContent ? 'LoadAllWithContent' : 'LoadAll',
25994
- query: withContent ? LoadAllWithContentQuery : LoadAllQuery,
26008
+ query: withContent ? this.queries.loadAllWithContent : this.queries.loadAll,
25995
26009
  variables: {
25996
- page: Object.assign(Object.assign({ sortBy: 'updateDate' }, page), { sortDirection: (page.sortDirection || 'DESC').toUpperCase() }),
26010
+ page: Object.assign(Object.assign({ sortBy: 'creationDate' }, page), {
26011
+ // @ts-ignore
26012
+ sortDirection: (page.sortDirection || 'desc').toUpperCase() }),
25997
26013
  filter: filter && filter.asPodObject()
25998
26014
  },
25999
26015
  arrayFieldName: 'data',
26000
26016
  error: { code: SocialErrorCodes.LOAD_USER_EVENTS_ERROR, message: 'SOCIAL.ERROR.LOAD_USER_EVENTS_ERROR' },
26001
- fetchPolicy: options && options.fetchPolicy || undefined
26017
+ fetchPolicy: (options === null || options === void 0 ? void 0 : options.fetchPolicy) || "cache-first"
26002
26018
  })
26003
26019
  .pipe(map(res => {
26004
- const data = res && (res.data || []).map(UserEvent.fromObject);
26020
+ const data = ((res === null || res === void 0 ? void 0 : res.data) || []).map(value => this.processUserEvent(value, this));
26005
26021
  if (now) {
26006
- console.debug(`[user-event-service] ${data.length} user events loaded in ${Date.now() - now}ms`);
26022
+ console.debug(`${this._logPrefix}${data.length} user events loaded in ${Date.now() - now}ms`);
26007
26023
  now = null;
26008
26024
  }
26025
+ const unreadCount = this.unreadEvents(data);
26009
26026
  return {
26010
26027
  data,
26011
- total: res && toNumber(res.total, data.length)
26028
+ total: toNumber(res === null || res === void 0 ? void 0 : res.total, unreadCount)
26012
26029
  };
26013
26030
  }));
26014
26031
  }
26015
- saveAll(data, options) {
26016
- return Promise.all(data
26017
- .map(entity => this.save(entity, options)));
26032
+ add(entity) {
26033
+ // TODO set an id !!!!!!!!!!!!!!!!!!!!!!!!!
26034
+ entity = this.processUserEvent(entity);
26035
+ const withContent = !!entity.content;
26036
+ // Add user event locally
26037
+ this.insertIntoMutableCachedQueries(this.graphql.cache, {
26038
+ query: withContent ? this.queries.loadAllWithContent : this.queries.loadAll,
26039
+ data: entity
26040
+ });
26041
+ // Update count
26042
+ this._count$.next(this._count$.value + 1);
26018
26043
  }
26019
- /**
26020
- * Save a userEvent entity
26021
- *
26022
- * @param entity
26023
- */
26024
- save(entity, options) {
26044
+ count(filter, options) {
26045
+ var _a;
26025
26046
  return __awaiter(this, void 0, void 0, function* () {
26026
- this.fillDefaultProperties(entity);
26027
- // Transform into json
26028
- const isNew = isNil(entity.id);
26029
- const json = entity.asObject();
26030
- const now = Date.now();
26047
+ if (!((_a = this.queries) === null || _a === void 0 ? void 0 : _a.count)) {
26048
+ console.warn(`${this._logPrefix}Query 'count' not provided, skip.`);
26049
+ return undefined;
26050
+ }
26051
+ if (!filter)
26052
+ filter = this.defaultFilter();
26053
+ filter = this.asFilter(filter);
26031
26054
  if (this._debug)
26032
- console.debug(`[user-event-service] Saving user event...`, json);
26033
- yield this.graphql.mutate({
26034
- mutation: SaveQuery,
26055
+ console.debug(`${this._logPrefix}Counting user events with filter:`, filter);
26056
+ const res = yield this.graphql.query({
26057
+ query: this.queries.count,
26035
26058
  variables: {
26036
- data: json
26059
+ filter: filter.asPodObject()
26037
26060
  },
26038
- error: { code: SocialErrorCodes.SAVE_USER_EVENT_ERROR, message: 'SOCIAL.ERROR.SAVE_USER_EVENT_ERROR' },
26039
- update: (proxy, { data }) => {
26040
- // Update entity
26041
- const savedEntity = data && data.data;
26042
- if (savedEntity) {
26043
- if (this._debug)
26044
- console.debug(`[user-event-service] User event saved in ${Date.now() - now}ms`, entity);
26045
- this.copyIdAndUpdateDate(savedEntity, entity);
26046
- // Add to cache
26047
- if (isNew) {
26048
- this.insertIntoMutableCachedQueries(proxy, {
26049
- query: LoadAllQuery,
26050
- data: Object.assign(Object.assign({}, savedEntity), { content: null })
26051
- });
26052
- this.insertIntoMutableCachedQueries(proxy, {
26053
- query: LoadAllWithContentQuery,
26054
- data: savedEntity
26055
- });
26056
- }
26057
- }
26058
- }
26061
+ error: { code: SocialErrorCodes.COUNT_USER_EVENT_ERROR, message: 'SOCIAL.ERROR.COUNT_USER_EVENT_ERROR' },
26062
+ fetchPolicy: (options === null || options === void 0 ? void 0 : options.fetchPolicy) || 'network-only'
26059
26063
  });
26060
- return entity;
26064
+ return res.total;
26065
+ });
26066
+ }
26067
+ listenChanges(filter, options) {
26068
+ var _a;
26069
+ if (!((_a = this.subscriptions) === null || _a === void 0 ? void 0 : _a.listenChanges)) {
26070
+ console.warn(`${this._logPrefix}Subscription query 'listenChanges' not provided, skip.`);
26071
+ return of();
26072
+ }
26073
+ if (!filter)
26074
+ filter = this.defaultFilter();
26075
+ filter = this.asFilter(filter);
26076
+ const withContent = (options === null || options === void 0 ? void 0 : options.withContent) === true;
26077
+ if (this._debug)
26078
+ console.debug(`${this._logPrefix}[WS] Listening changes for user events with filter:`, filter);
26079
+ return this.graphql.subscribe({
26080
+ query: this.subscriptions.listenChanges,
26081
+ fetchPolicy: options === null || options === void 0 ? void 0 : options.fetchPolicy,
26082
+ variables: { filter: filter.asPodObject(), interval: toNumber(options === null || options === void 0 ? void 0 : options.interval, 10) },
26083
+ error: {
26084
+ code: SocialErrorCodes.SUBSCRIBE_USER_EVENTS_ERROR,
26085
+ message: 'SOCIAL.ERROR.SUBSCRIBE_USER_EVENTS_ERROR'
26086
+ }
26087
+ }).pipe(map(({ data }) => {
26088
+ if (data && this._debug)
26089
+ console.debug(`${this._logPrefix}Received new user events:`, data);
26090
+ const updatedData = data === null || data === void 0 ? void 0 : data.map(value => this.processUserEvent(value, this));
26091
+ // Update cache
26092
+ this.insertIntoMutableCachedQueries(this.graphql.cache, {
26093
+ query: withContent ? this.queries.loadAllWithContent : this.queries.loadAll,
26094
+ data: updatedData
26095
+ });
26096
+ // update count
26097
+ this._count$.next(this.unreadEvents(updatedData));
26098
+ return updatedData;
26099
+ }));
26100
+ }
26101
+ delete(entity) {
26102
+ return __awaiter(this, void 0, void 0, function* () {
26103
+ if (!entity)
26104
+ return; // skip
26105
+ yield this.deleteAll([entity]);
26061
26106
  });
26062
26107
  }
26063
- /**
26064
- * Save many trips
26065
- *
26066
- * @param entities
26067
- * @param opts
26068
- */
26069
26108
  deleteAll(entities, opts) {
26070
26109
  return __awaiter(this, void 0, void 0, function* () {
26071
26110
  const ids = entities && entities
@@ -26074,49 +26113,206 @@ class UserEventService extends BaseGraphqlService {
26074
26113
  return; // stop, if nothing else to do
26075
26114
  const now = Date.now();
26076
26115
  if (this._debug)
26077
- console.debug('[user-event-service] Deleting events... ids:', ids);
26116
+ console.debug(`${this._logPrefix}Deleting events... ids:`, ids);
26078
26117
  yield this.graphql.mutate({
26079
- mutation: DeleteByIdsMutation,
26118
+ mutation: this.mutations.deleteByIds,
26080
26119
  variables: {
26081
26120
  ids
26082
26121
  },
26083
26122
  update: (proxy) => {
26084
26123
  // Remove from caches
26085
26124
  this.removeFromMutableCachedQueriesByIds(proxy, {
26086
- query: LoadAllQuery,
26125
+ query: this.queries.loadAll,
26087
26126
  ids
26088
26127
  });
26089
26128
  this.removeFromMutableCachedQueriesByIds(proxy, {
26090
- query: LoadAllWithContentQuery,
26129
+ query: this.queries.loadAllWithContent,
26091
26130
  ids
26092
26131
  });
26093
26132
  if (this._debug)
26094
- console.debug(`[user-event-service] Events deleted in ${Date.now() - now}ms`);
26133
+ console.debug(`${this._logPrefix}Events deleted in ${Date.now() - now}ms`);
26095
26134
  }
26096
26135
  });
26097
26136
  });
26098
26137
  }
26099
- /**
26100
- * Delete userEvent entities
26101
- */
26102
- delete(data) {
26138
+ save(entity, options) {
26103
26139
  return __awaiter(this, void 0, void 0, function* () {
26104
- if (!data)
26105
- return; // skip
26106
- yield this.deleteAll([data]);
26140
+ this.fillDefaultProperties(entity);
26141
+ // Transform into json
26142
+ const isNew = isNil(entity.id);
26143
+ const json = entity.asObject();
26144
+ const now = Date.now();
26145
+ if (this._debug)
26146
+ console.debug(`${this._logPrefix}Saving user event...`, json);
26147
+ yield this.graphql.mutate({
26148
+ mutation: this.mutations.save,
26149
+ variables: {
26150
+ data: json
26151
+ },
26152
+ error: { code: SocialErrorCodes.SAVE_USER_EVENT_ERROR, message: 'SOCIAL.ERROR.SAVE_USER_EVENT_ERROR' },
26153
+ update: (proxy, { data }) => {
26154
+ // Update entity
26155
+ const savedEntity = data && data.data;
26156
+ if (savedEntity) {
26157
+ if (this._debug)
26158
+ console.debug(`${this._logPrefix}User event saved in ${Date.now() - now}ms`, entity);
26159
+ this.copyIdAndUpdateDate(savedEntity, entity);
26160
+ // Add to cache
26161
+ if (isNew) {
26162
+ this.insertIntoMutableCachedQueries(proxy, {
26163
+ query: this.queries.loadAll,
26164
+ data: Object.assign(Object.assign({}, savedEntity), { content: null })
26165
+ });
26166
+ this.insertIntoMutableCachedQueries(proxy, {
26167
+ query: this.queries.loadAllWithContent,
26168
+ data: savedEntity
26169
+ });
26170
+ }
26171
+ }
26172
+ }
26173
+ });
26174
+ return entity;
26107
26175
  });
26108
26176
  }
26109
- listenChanges(id, options) {
26110
- // TODO
26111
- console.warn('TODO: implement listen changes on user events');
26112
- return of();
26177
+ saveAll(data, opts) {
26178
+ return Promise.all(data
26179
+ .map(entity => this.save(entity, opts)));
26113
26180
  }
26114
- registerAction(definition) {
26115
- console.info(`[user-event-service] Registering action ${definition.name} for ${definition.__typename}`);
26116
- this._userEventActions.push(definition);
26181
+ markAsRead(entity) {
26182
+ return __awaiter(this, void 0, void 0, function* () {
26183
+ // Get onRead listeners
26184
+ const onReadListeners = (this.listeners || []).filter(listener => !!listener.onRead).filter(listener => listener.accept(entity));
26185
+ if (onReadListeners.length) {
26186
+ for (const listener of onReadListeners) {
26187
+ yield listener.onRead(entity);
26188
+ }
26189
+ }
26190
+ else {
26191
+ // Use default
26192
+ yield this.defaultMarkAsRead(entity);
26193
+ }
26194
+ });
26117
26195
  }
26118
- getActionsByTypename(typename) {
26119
- return this._userEventActions.filter(def => def.__typename === typename);
26196
+ registerListener(listener) {
26197
+ if (!this.listeners)
26198
+ this.listeners = [];
26199
+ this.listeners.push(listener);
26200
+ }
26201
+ /* -- protected methods -- */
26202
+ processUserEvent(source, that) {
26203
+ that = that || this;
26204
+ // Convert json source to IUserEvent
26205
+ let target = that.fromObject(source);
26206
+ // Get listeners
26207
+ const listeners = (that.listeners || []).filter(listener => listener.onReceived).filter(listener => listener.accept(target));
26208
+ for (const listener of listeners) {
26209
+ if (target) {
26210
+ target = listener.onReceived(target);
26211
+ }
26212
+ else {
26213
+ if (that._debug)
26214
+ console.debug(`${that._logPrefix}Event has been rejected by a listener`, source);
26215
+ break;
26216
+ }
26217
+ }
26218
+ return target;
26219
+ }
26220
+ defaultMarkAsRead(userEvent) {
26221
+ return __awaiter(this, void 0, void 0, function* () {
26222
+ if (!userEvent)
26223
+ throw new Error(`${this._logPrefix}Invalid event to save`);
26224
+ // set read date
26225
+ userEvent.readDate = moment$6();
26226
+ if (!userEvent.id || userEvent.id < 0) {
26227
+ if (this._debug)
26228
+ console.debug(`${this._logPrefix}Save local user event`);
26229
+ // TODO !!!!!!!!!!!!!
26230
+ }
26231
+ else {
26232
+ yield this.save(userEvent);
26233
+ }
26234
+ });
26235
+ }
26236
+ defaultFilter() {
26237
+ // By default
26238
+ return {
26239
+ recipients: [this.defaultRecipient()]
26240
+ };
26241
+ }
26242
+ defaultRecipient() {
26243
+ return this.accountService.person.pubkey;
26244
+ }
26245
+ fillDefaultProperties(entity) {
26246
+ }
26247
+ unreadEvents(events) {
26248
+ // default calculation on unread events from a list
26249
+ if (isEmptyArray(events))
26250
+ return undefined;
26251
+ const lastReadDate = events
26252
+ .filter(event => isNotNil(event.readDate))
26253
+ .reduce((lastDate, event) => DateUtils.max(lastDate, event.readDate), undefined);
26254
+ return events
26255
+ .filter(event => event.creationDate.isSameOrAfter(lastReadDate)).length;
26256
+ }
26257
+ copyIdAndUpdateDate(source, target) {
26258
+ EntityUtils.copyIdAndUpdateDate(source, target);
26259
+ }
26260
+ }
26261
+ AbstractUserEventService.decorators = [
26262
+ { type: Directive }
26263
+ ];
26264
+ AbstractUserEventService.ctorParameters = () => [
26265
+ { type: GraphqlService },
26266
+ { type: AccountService },
26267
+ { type: NetworkService },
26268
+ { type: TranslateService },
26269
+ { type: undefined },
26270
+ { type: undefined },
26271
+ { type: undefined },
26272
+ { type: Environment, decorators: [{ type: Optional }, { type: Inject, args: [ENVIRONMENT,] }] }
26273
+ ];
26274
+ const userEventQueries = {
26275
+ loadAll: gql `
26276
+ query UserEvents($filter: UserEventFilterVOInput, $page: PageInput){
26277
+ data: userEvents(filter: $filter, page: $page){
26278
+ ...LightUserEventFragment
26279
+ }
26280
+ }
26281
+ ${UserEventFragments.lightUserEvent}
26282
+ `,
26283
+ loadAllWithContent: gql `
26284
+ query UserEventsWithContent($filter: UserEventFilterVOInput, $page: PageInput){
26285
+ data: userEvents(filter: $filter, page: $page){
26286
+ ...UserEventFragment
26287
+ }
26288
+ }
26289
+ ${UserEventFragments.userEvent}
26290
+ `
26291
+ };
26292
+ const userEventMutations = {
26293
+ deleteByIds: gql `
26294
+ mutation DeleteUserEvents($ids:[Int]){
26295
+ deleteUserEvents(ids: $ids)
26296
+ }
26297
+ `,
26298
+ save: gql `
26299
+ mutation SaveUserEvent($data: UserEventVOInput){
26300
+ data: saveUserEvent(userEvent: $data){
26301
+ ...UserEventFragment
26302
+ }
26303
+ }
26304
+ ${UserEventFragments.userEvent}
26305
+ `
26306
+ };
26307
+ class UserEventService extends AbstractUserEventService {
26308
+ constructor(graphql, accountService, network, translate, toastController, environment) {
26309
+ super(graphql, accountService, network, translate, userEventQueries, userEventMutations, undefined, environment);
26310
+ this.graphql = graphql;
26311
+ this.accountService = accountService;
26312
+ this.network = network;
26313
+ this.translate = translate;
26314
+ this.toastController = toastController;
26315
+ this.environment = environment;
26120
26316
  }
26121
26317
  showToastErrorWithContext(opts) {
26122
26318
  return __awaiter(this, void 0, void 0, function* () {
@@ -26180,13 +26376,16 @@ class UserEventService extends BaseGraphqlService {
26180
26376
  }
26181
26377
  sendDataForDebug(data) {
26182
26378
  const userEvent = new UserEvent();
26183
- userEvent.eventType = UserEventTypes.DEBUG_DATA;
26379
+ userEvent.type = UserEventTypes.DEBUG_DATA;
26184
26380
  userEvent.content = this.convertObjectToString(data);
26185
26381
  return this.save(userEvent);
26186
26382
  }
26187
26383
  asFilter(filter) {
26188
26384
  return UserEventFilter.fromObject(filter);
26189
26385
  }
26386
+ fromObject(source) {
26387
+ return UserEvent.fromObject(source);
26388
+ }
26190
26389
  /* -- protected methods -- */
26191
26390
  convertObjectToString(data) {
26192
26391
  if (typeof data === 'string') {
@@ -26215,13 +26414,9 @@ class UserEventService extends BaseGraphqlService {
26215
26414
  // TODO: compute sign
26216
26415
  console.warn('TODO: sign user event before sending');
26217
26416
  }
26218
- copyIdAndUpdateDate(source, target) {
26219
- EntityUtils.copyIdAndUpdateDate(source, target);
26220
- }
26221
26417
  }
26222
- UserEventService.ɵprov = ɵɵdefineInjectable({ factory: function UserEventService_Factory() { return new UserEventService(ɵɵinject(GraphqlService), ɵɵinject(AccountService), ɵɵinject(NetworkService), ɵɵinject(TranslateService), ɵɵinject(ToastController), ɵɵinject(ENVIRONMENT, 8)); }, token: UserEventService, providedIn: "root" });
26223
26418
  UserEventService.decorators = [
26224
- { type: Injectable, args: [{ providedIn: 'root' },] }
26419
+ { type: Injectable }
26225
26420
  ];
26226
26421
  UserEventService.ctorParameters = () => [
26227
26422
  { type: GraphqlService },
@@ -26234,7 +26429,7 @@ UserEventService.ctorParameters = () => [
26234
26429
 
26235
26430
  const ICONS_MAP = {
26236
26431
  DEBUG_DATA: { matIcon: 'bug_report' },
26237
- INBOX_MESSAGE: { matIcon: 'mail' },
26432
+ INBOX_MESSAGE: { matIcon: 'mail' }
26238
26433
  };
26239
26434
  // TODO: refactor with a registration done by data service:
26240
26435
  // - userEventService.registerEventAction({__typename: 'TripVO', ...})
@@ -26300,7 +26495,7 @@ class UserEventsTable extends AppTable {
26300
26495
  }
26301
26496
  }));
26302
26497
  const filter = this.filter || new UserEventFilter();
26303
- filter.recipient = this.recipient;
26498
+ filter.recipients = [this.recipient];
26304
26499
  this.setFilter(filter, { emitEvent: true });
26305
26500
  }
26306
26501
  start() {
@@ -26313,17 +26508,17 @@ class UserEventsTable extends AppTable {
26313
26508
  });
26314
26509
  }
26315
26510
  getIcon(source) {
26316
- return ICONS_MAP[source.eventType];
26511
+ return ICONS_MAP[source.type];
26317
26512
  }
26318
26513
  getDetail(source) {
26319
26514
  if (!source)
26320
26515
  return undefined;
26321
- if (source.content && source.eventType === UserEventTypes.DEBUG_DATA) {
26516
+ if (source.content && source.type === UserEventTypes.DEBUG_DATA) {
26322
26517
  const context = source.content.context;
26323
26518
  if (context && context.__typename) {
26324
- const actions = this.service.getActionsByTypename(context.__typename);
26519
+ // const actions = this.service.getActionsByTypename(context.__typename);
26325
26520
  return {
26326
- actions,
26521
+ // actions,
26327
26522
  title: source.content.error && source.content.error.message || source.content.message,
26328
26523
  description: source.content.error && source.content.error.details || undefined
26329
26524
  };
@@ -26335,11 +26530,11 @@ class UserEventsTable extends AppTable {
26335
26530
  doAction(action, row) {
26336
26531
  return __awaiter(this, void 0, void 0, function* () {
26337
26532
  const event = row.currentData;
26338
- const context = event.content && event.content.context;
26533
+ // const context = event.content && event.content.context;
26339
26534
  this.markAsLoading();
26340
26535
  if (action && typeof action.executeAction === 'function') {
26341
26536
  try {
26342
- let res = action.executeAction(event, context);
26537
+ let res = action.executeAction(event /*, context*/);
26343
26538
  res = (res instanceof Promise) ? yield res : res;
26344
26539
  }
26345
26540
  catch (err) {
@@ -26358,13 +26553,18 @@ UserEventsTable.decorators = [
26358
26553
  selector: 'app-user-events-table',
26359
26554
  template: "\n<!-- Type = options menu -->\n<mat-menu #optionsMenu=\"matMenu\" xPosition=\"after\">\n\n <!-- display columns -->\n <button mat-menu-item\n (click)=\"openSelectColumnsModal($event)\">\n <mat-icon>view_column</mat-icon>\n <ion-label translate>COMMON.DISPLAYED_COLUMNS_DOTS</ion-label>\n </button>\n\n</mat-menu>\n\n<!-- top header -->\n<mat-toolbar>\n\n <ng-container *ngIf=\"!selection.hasValue(); else hasSelection\">\n\n <button mat-icon-button [title]=\"'COMMON.BTN_REFRESH'|translate\" (click)=\"onRefresh.emit()\">\n <mat-icon>refresh</mat-icon>\n </button>\n\n </ng-container>\n\n <!-- if row selection -->\n <ng-template #hasSelection>\n\n <!-- delete -->\n <button mat-icon-button class=\"hidden-xs hidden-sm\" *ngIf=\"canDelete\"\n [title]=\"'COMMON.BTN_DELETE'|translate\" (click)=\"deleteSelection($event)\">\n <mat-icon>delete</mat-icon>\n </button>\n </ng-template>\n\n <!-- error -->\n <ion-item *ngIf=\"error\" hidden-xs hidden-sm hidden-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=\"toolbar-spacer\"></div>\n\n <button mat-icon-button\n [title]=\"'COMMON.BTN_OPTIONS'|translate\"\n [matMenuTriggerFor]=\"optionsMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n</mat-toolbar>\n\n\n<mat-table [dataSource]=\"dataSource\" matSort\n [matSortActive]=\"defaultSortBy\"\n [matSortDirection]=\"defaultSortDirection\"\n matSortDisableClear [trackBy]=\"trackByFn\">\n\n <!-- select -->\n <ng-container matColumnDef=\"select\">\n <mat-header-cell *matHeaderCellDef [class.cdk-visually-hidden]=\"!canEdit\">\n <mat-checkbox (change)=\"$event ? masterToggle() : null\" [checked]=\"selection.hasValue() && isAllSelected()\"\n [indeterminate]=\"selection.hasValue() && !isAllSelected()\">\n </mat-checkbox>\n </mat-header-cell>\n <mat-cell *matCellDef=\"let row\" [class.cdk-visually-hidden]=\"!canEdit\">\n <mat-checkbox (click)=\"$event.stopPropagation()\" (change)=\"$event ? selection.toggle(row) : null\" [checked]=\"selection.isSelected(row)\">\n </mat-checkbox>\n </mat-cell>\n </ng-container>\n\n <!-- id -->\n <ng-container matColumnDef=\"id\">\n <mat-header-cell *matHeaderCellDef mat-sort-header [class.cdk-visually-hidden]=\"!isAdmin\">\n <app-loading-spinner [loading]=\"loadingSubject|async\"><ion-label>#</ion-label></app-loading-spinner>\n </mat-header-cell>\n <mat-cell *matCellDef=\"let row\" [class.cdk-visually-hidden]=\"!isAdmin\">{{ row.currentData.id | mathAbs }}</mat-cell>\n </ng-container>\n\n <!-- creationDate -->\n <ng-container matColumnDef=\"creationDate\">\n <mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label translate>SOCIAL.USER_EVENT.CREATION_DATE</ion-label>\n </mat-header-cell>\n <mat-cell *matCellDef=\"let row\">\n <ion-text>\n {{row.currentData.creationDate | dateFromNow }}<br/>\n <ion-text color=\"medium\"><small>{{row.currentData.creationDate | dateFormat: {time: true} }}</small></ion-text>\n </ion-text>\n </mat-cell>\n </ng-container>\n\n <!-- icon -->\n <ng-container matColumnDef=\"icon\">\n <mat-header-cell *matHeaderCellDef>\n </mat-header-cell>\n <mat-cell *matCellDef=\"let row;\">\n <ng-container *ngIf=\"getIcon(row.currentData); let details\">\n <ion-icon slot=\"start\" *ngIf=\"details.icon\" [name]=\"details.icon\"></ion-icon>\n <mat-icon slot=\"start\" *ngIf=\"details.matIcon\">{{details.matIcon}}</mat-icon>\n </ng-container>\n </mat-cell>\n </ng-container>\n\n <!-- event type -->\n <ng-container matColumnDef=\"eventType\">\n <mat-header-cell *matHeaderCellDef mat-sort-header>\n <ion-label translate>SOCIAL.USER_EVENT.EVENT_TYPE</ion-label>\n </mat-header-cell>\n <mat-cell *matCellDef=\"let row;\">\n <span>{{'SOCIAL.USER_EVENT.TYPE_ENUM.' + row.currentData.eventType | translate}}</span>\n </mat-cell>\n </ng-container>\n\n <!-- message -->\n <ng-container matColumnDef=\"message\">\n <mat-header-cell *matHeaderCellDef>\n <ion-label translate>SOCIAL.USER_EVENT.MESSAGE</ion-label>\n </mat-header-cell>\n <mat-cell *matCellDef=\"let row;\">\n <ng-container *ngIf=\"getDetail(row.currentData); let details\">\n <ion-text [innerHTML]=\"details.title|translate\">\n </ion-text>\n </ng-container>\n </mat-cell>\n </ng-container>\n\n <!-- Actions buttons column -->\n <ng-container matColumnDef=\"actions\">\n <mat-header-cell *matHeaderCellDef>\n </mat-header-cell>\n <mat-cell *matCellDef=\"let row\">\n <ng-container *ngIf=\"getDetail(row.currentData); let details\">\n <button mat-icon-button color=\"light\"\n *ngFor=\"let action of details.actions\"\n [title]=\"action.title |translate\"\n [color]=\"action.color\"\n (click)=\"doAction(action, row)\">\n <ion-icon slot=\"start\" *ngIf=\"action.icon\" [name]=\"action.icon\"></ion-icon>\n <mat-icon slot=\"start\" *ngIf=\"action.matIcon\">{{action.matIcon}}</mat-icon>\n </button>\n </ng-container>\n </mat-cell>\n </ng-container>\n\n <mat-header-row *matHeaderRowDef=\"displayedColumns\"></mat-header-row>\n <mat-row *matRowDef=\"let row; columns: displayedColumns;\"\n class=\"mat-row-disabled\"\n (click)=\"clickRow($event, row)\"></mat-row>\n\n</mat-table>\n\n<ion-row class=\"ion-no-padding\">\n <ion-col></ion-col>\n <ion-col class=\"ion-no-padding\" size=\"auto\">\n <mat-paginator [length]=\"totalRowCount\" [pageSize]=\"defaultPageSize\" [pageSizeOptions]=\"defaultPageSizeOptions\" showFirstLastButtons>\n </mat-paginator>\n </ion-col>\n</ion-row>\n",
26360
26555
  changeDetection: ChangeDetectionStrategy.OnPush,
26556
+ providers: [
26557
+ {
26558
+ provide: UserEventServiceToken, useClass: UserEventService
26559
+ }
26560
+ ],
26361
26561
  styles: [".mat-header-row .mat-column-icon,.mat-row .mat-column-icon{max-width:40px;padding-left:0}.mat-header-row .mat-column-icon ion-icon,.mat-header-row .mat-column-icon mat-icon,.mat-row .mat-column-icon ion-icon,.mat-row .mat-column-icon mat-icon{font-size:1.2em;height:1.2em;width:1.2em}.mat-header-row .mat-column-creationDate,.mat-row .mat-column-creationDate{min-width:100px;max-width:120px}"]
26362
26562
  },] }
26363
26563
  ];
26364
26564
  UserEventsTable.ctorParameters = () => [
26365
26565
  { type: Injector },
26366
26566
  { type: AccountService },
26367
- { type: UserEventService },
26567
+ { type: undefined, decorators: [{ type: Inject, args: [UserEventServiceToken,] }] },
26368
26568
  { type: EntitiesStorage },
26369
26569
  { type: ChangeDetectorRef },
26370
26570
  { type: undefined, decorators: [{ type: Inject, args: [ENVIRONMENT,] }] }
@@ -26376,6 +26576,154 @@ UserEventsTable.propDecorators = {
26376
26576
  defaultSortDirection: [{ type: Input }]
26377
26577
  };
26378
26578
 
26579
+ class UserEventNotificationList {
26580
+ constructor(popoverController) {
26581
+ this.popoverController = popoverController;
26582
+ }
26583
+ ngOnInit() {
26584
+ }
26585
+ read($event, userEvent) {
26586
+ var _a;
26587
+ if (userEvent === null || userEvent === void 0 ? void 0 : userEvent.readDate)
26588
+ return;
26589
+ (_a = this.readEvent) === null || _a === void 0 ? void 0 : _a.emit(userEvent);
26590
+ }
26591
+ executeAction(action, userEvent) {
26592
+ // Execute then close popover
26593
+ action.executeAction(userEvent);
26594
+ this.dismiss();
26595
+ }
26596
+ dismiss() {
26597
+ this.popoverController.dismiss();
26598
+ }
26599
+ }
26600
+ UserEventNotificationList.decorators = [
26601
+ { type: Component, args: [{
26602
+ selector: 'app-user-event-notification-list',
26603
+ template: "\n<ion-list class=\"ion-list-popover\">\n\n <ion-row class=\"ion-list-header column\">\n <ion-col>\n <ion-label>{{titleI18n | translate}}</ion-label>\n </ion-col>\n </ion-row>\n\n <ion-item *ngIf=\"!(userEvents | async)?.length\">\n {{'SOCIAL.USER_EVENT.NOTIFICATION.EMPTY' | translate}}\n </ion-item>\n <ion-item\n *ngFor=\"let userEvent of userEvents | async\"\n (click)=\"read($event, userEvent)\"\n >\n\n <ion-avatar slot=\"start\">\n\n <img *ngIf=\"userEvent.avatar; else avatarIcon\" src=\"{{ userEvent.avatar }}\">\n\n <ng-template #avatarIcon>\n <app-icon\n *ngIf=\"userEvent.avatarIcon\"\n [ref]=\"userEvent.avatarIcon\"\n height=\"40\"\n width=\"40\"\n ></app-icon>\n </ng-template>\n\n </ion-avatar>\n\n <ion-grid class=\"ion-no-margin ion-no-padding\">\n <ion-row>\n <ion-col>\n <p [class.unread]=\"!userEvent.readDate\">\n {{userEvent.message}}\n </p>\n </ion-col>\n </ion-row>\n <ion-row *ngIf=\"userEvent.actions?.length\">\n <ion-col>\n <p>\n <span *ngFor=\"let action of userEvent.actions\">\n <a\n [style.margin-right.px]=\"8\"\n [style.cursor]=\"'pointer'\"\n (click)=\"executeAction(action, userEvent)\"\n >\n {{ '&#10142;&nbsp;' + action.name}}\n </a>\n </span>\n </p>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col>\n <app-icon\n *ngIf=\"userEvent.icon\"\n [ref]=\"userEvent.icon\"\n height=\"16\"\n width=\"16\"\n [style.vertical-align]=\"'sub'\"\n ></app-icon>\n <small>\n <span>{{userEvent.creationDate|dateFromNow}}</span>\n <span [style.color]=\"'gray'\">{{ ' | ' + (userEvent.creationDate|dateFormat:{time:true}) }}</span>\n </small>\n </ion-col>\n </ion-row>\n </ion-grid>\n </ion-item>\n\n</ion-list>\n",
26604
+ styles: [":host(.popover-viewport){overflow-y:auto;-webkit-user-select:none;-moz-user-select:none;user-select:none}.unread{font-weight:700}"]
26605
+ },] }
26606
+ ];
26607
+ UserEventNotificationList.ctorParameters = () => [
26608
+ { type: PopoverController }
26609
+ ];
26610
+ UserEventNotificationList.propDecorators = {
26611
+ titleI18n: [{ type: Input }],
26612
+ userEvents: [{ type: Input }],
26613
+ readEvent: [{ type: Output }]
26614
+ };
26615
+
26616
+ class UserEventNotificationComponent {
26617
+ constructor(userEventService, accountService, popoverController, cd) {
26618
+ this.userEventService = userEventService;
26619
+ this.accountService = accountService;
26620
+ this.popoverController = popoverController;
26621
+ this.cd = cd;
26622
+ this.debug = false;
26623
+ this.titleI18n = 'SOCIAL.USER_EVENT.NOTIFICATION.TITLE';
26624
+ this.disabled = false;
26625
+ this.userEventsSubject = new BehaviorSubject(undefined);
26626
+ this._subscriptions = new Subscription();
26627
+ this._logPrefix = '[user-event-notification] ';
26628
+ this._readEvent = new EventEmitter();
26629
+ }
26630
+ get count() {
26631
+ var _a;
26632
+ return (_a = this.userEventService) === null || _a === void 0 ? void 0 : _a.userEventCount.pipe(map(value => value === 0 ? undefined : value));
26633
+ }
26634
+ ngOnInit() {
26635
+ if (isNil(this.userEventService)) {
26636
+ console.warn(`${this._logPrefix}No service injected`);
26637
+ this.disabled = true;
26638
+ return;
26639
+ }
26640
+ // Watch first
26641
+ this._subscriptions.add(this.userEventService.watchAll(0, 10, 'creationDate', 'desc').subscribe(value => {
26642
+ const data = value.data;
26643
+ if (this.debug) {
26644
+ console.debug(`${this._logPrefix}Receiving user events`, data);
26645
+ }
26646
+ this.userEventsSubject.next(data);
26647
+ }));
26648
+ // Then listen changes
26649
+ this._subscriptions.add(this.userEventService.listenChanges(undefined).subscribe(data => {
26650
+ if (this.debug) {
26651
+ console.debug(`${this._logPrefix}Receiving new user events`, data);
26652
+ }
26653
+ }));
26654
+ this._readEvent.subscribe(value => {
26655
+ this.userEventService.markAsRead(value);
26656
+ });
26657
+ }
26658
+ showList(event) {
26659
+ return __awaiter(this, void 0, void 0, function* () {
26660
+ const popover = yield this.popoverController.create({
26661
+ component: UserEventNotificationList,
26662
+ componentProps: {
26663
+ titleI18n: this.titleI18n,
26664
+ userEvents: this.userEventsSubject,
26665
+ readEvent: this._readEvent
26666
+ },
26667
+ backdropDismiss: true,
26668
+ keyboardClose: true,
26669
+ event,
26670
+ translucent: true,
26671
+ cssClass: 'popover-large'
26672
+ });
26673
+ yield popover.present();
26674
+ });
26675
+ }
26676
+ markForCheck() {
26677
+ this.cd.markForCheck();
26678
+ }
26679
+ ngOnDestroy() {
26680
+ if (this.debug) {
26681
+ console.debug(`${this._logPrefix}Destroying`);
26682
+ }
26683
+ this._subscriptions.unsubscribe();
26684
+ }
26685
+ }
26686
+ UserEventNotificationComponent.decorators = [
26687
+ { type: Component, args: [{
26688
+ selector: 'app-user-event-notification-component',
26689
+ template: "<button\n #button\n mat-icon-button\n [title]=\"titleI18n | translate\"\n [disabled]=\"disabled\"\n (click)=\"showList($event)\"\n>\n <mat-icon\n [matBadge]=\"count | async\"\n matBadgeColor=\"accent\"\n matBadgeSize=\"small\"\n matBadgePosition=\"above after\"\n >notifications\n </mat-icon>\n</button>\n\n",
26690
+ changeDetection: ChangeDetectionStrategy.OnPush,
26691
+ styles: [""]
26692
+ },] }
26693
+ ];
26694
+ UserEventNotificationComponent.ctorParameters = () => [
26695
+ { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [UserEventServiceToken,] }] },
26696
+ { type: AccountService },
26697
+ { type: PopoverController },
26698
+ { type: ChangeDetectorRef }
26699
+ ];
26700
+ UserEventNotificationComponent.propDecorators = {
26701
+ debug: [{ type: Input }],
26702
+ titleI18n: [{ type: Input }],
26703
+ disabled: [{ type: Input }]
26704
+ };
26705
+
26706
+ class UserEventModule {
26707
+ }
26708
+ UserEventModule.decorators = [
26709
+ { type: NgModule, args: [{
26710
+ imports: [
26711
+ CommonModule,
26712
+ CoreModule,
26713
+ SharedModule
26714
+ ],
26715
+ declarations: [
26716
+ UserEventsTable,
26717
+ UserEventNotificationComponent,
26718
+ UserEventNotificationList,
26719
+ ],
26720
+ exports: [
26721
+ UserEventsTable,
26722
+ UserEventNotificationComponent
26723
+ ]
26724
+ },] }
26725
+ ];
26726
+
26379
26727
  const MessageTypes = {
26380
26728
  INBOX_MESSAGE: 'INBOX_MESSAGE',
26381
26729
  EMAIL: 'EMAIL',
@@ -26587,9 +26935,9 @@ MessageForm.propDecorators = {
26587
26935
  canSelectType: [{ type: Input }]
26588
26936
  };
26589
26937
 
26590
- class SocialModule {
26938
+ class MessageModule {
26591
26939
  }
26592
- SocialModule.decorators = [
26940
+ MessageModule.decorators = [
26593
26941
  { type: NgModule, args: [{
26594
26942
  imports: [
26595
26943
  CommonModule,
@@ -26597,17 +26945,317 @@ SocialModule.decorators = [
26597
26945
  SharedModule
26598
26946
  ],
26599
26947
  declarations: [
26600
- UserEventsTable,
26601
26948
  MessageModal,
26602
26949
  MessageForm
26603
26950
  ],
26604
26951
  exports: [
26605
- UserEventsTable,
26606
26952
  MessageModal
26607
26953
  ]
26608
26954
  },] }
26609
26955
  ];
26610
26956
 
26957
+ var JobProgression_1;
26958
+ // todo use entity
26959
+ class Job {
26960
+ }
26961
+ // @dynamic
26962
+ let JobProgression = JobProgression_1 = class JobProgression extends Entity {
26963
+ constructor() {
26964
+ super(JobProgression_1.TYPENAME);
26965
+ }
26966
+ fromObject(source) {
26967
+ super.fromObject(source);
26968
+ this.message = source.message;
26969
+ this.current = source.current;
26970
+ this.total = source.total;
26971
+ }
26972
+ };
26973
+ JobProgression = JobProgression_1 = __decorate([
26974
+ EntityClass({ typename: 'JobProgressionVO' })
26975
+ ], JobProgression);
26976
+
26977
+ const JobProgressionServiceToken = new InjectionToken('JobProgressionService');
26978
+ const jobProgressionSubscription = gql `subscription UpdateJobProgression($id: Int!, $interval: Int){
26979
+ data: updateJobProgression(id: $id, interval: $interval) {
26980
+ id
26981
+ message
26982
+ current
26983
+ total
26984
+ }
26985
+ }`;
26986
+ class JobProgressionService extends BaseGraphqlService {
26987
+ constructor(graphql, environment) {
26988
+ super(graphql, environment);
26989
+ this.graphql = graphql;
26990
+ this.environment = environment;
26991
+ this._logPrefix = '[job-progression-service] ';
26992
+ // For DEV only
26993
+ this._debug = !(environment === null || environment === void 0 ? void 0 : environment.production);
26994
+ }
26995
+ listenChanges(id, options) {
26996
+ if (isNil(id))
26997
+ throw new Error(`${this._logPrefix}Missing argument 'id'`);
26998
+ if (this._debug)
26999
+ console.debug(`${this._logPrefix}[WS] Listening changes for job progression {${id}}...`);
27000
+ return this.graphql.subscribe({
27001
+ query: jobProgressionSubscription,
27002
+ fetchPolicy: options === null || options === void 0 ? void 0 : options.fetchPolicy,
27003
+ variables: { id, interval: toNumber(options === null || options === void 0 ? void 0 : options.interval, 10) },
27004
+ error: { code: SocialErrorCodes.SUBSCRIBE_JOB_PROGRESSION_ERROR, message: 'SOCIAL.ERROR.SUBSCRIBE_JOB_PROGRESSION_ERROR' }
27005
+ }).pipe(map(({ data }) => {
27006
+ const progression = data && JobProgression.fromObject(data);
27007
+ if (progression && this._debug)
27008
+ console.debug(`${this._logPrefix}Job progression ${id} updated on server`, progression);
27009
+ return progression;
27010
+ }));
27011
+ }
27012
+ }
27013
+ JobProgressionService.decorators = [
27014
+ { type: Injectable }
27015
+ ];
27016
+ JobProgressionService.ctorParameters = () => [
27017
+ { type: GraphqlService },
27018
+ { type: Environment, decorators: [{ type: Optional }, { type: Inject, args: [ENVIRONMENT,] }] }
27019
+ ];
27020
+
27021
+ class JobProgressionList {
27022
+ constructor() {
27023
+ }
27024
+ ngOnInit() {
27025
+ }
27026
+ }
27027
+ JobProgressionList.decorators = [
27028
+ { type: Component, args: [{
27029
+ selector: 'app-job-progression-list',
27030
+ template: "<ion-list class=\"ion-list-popover\">\n\n <ion-row class=\"ion-list-header column\">\n <ion-col>\n <ion-label>{{titleI18n | translate}}</ion-label>\n </ion-col>\n </ion-row>\n\n <ion-item *ngIf=\"!jobProgressions?.length\">\n {{'SOCIAL.JOB.PROGRESSION.EMPTY' | translate}}\n </ion-item>\n <ion-item *ngFor=\"let jobProgression of jobProgressions\">\n <ion-grid class=\"ion-no-margin\">\n <ion-row>\n <ion-col>\n {{jobProgression.message}}\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col>\n <ion-progress-bar\n [value]=\"jobProgression.total > 0 ? jobProgression.current / jobProgression.total : undefined\"\n [type]=\"jobProgression.total > 0 ? 'determinate' : 'indeterminate'\">\n </ion-progress-bar>\n </ion-col>\n </ion-row>\n </ion-grid>\n </ion-item>\n\n</ion-list>\n",
27031
+ styles: [":host(.popover-viewport){overflow-y:auto;-webkit-user-select:none;-moz-user-select:none;user-select:none}"]
27032
+ },] }
27033
+ ];
27034
+ JobProgressionList.ctorParameters = () => [];
27035
+ JobProgressionList.propDecorators = {
27036
+ titleI18n: [{ type: Input }],
27037
+ jobProgressions: [{ type: Input }]
27038
+ };
27039
+
27040
+ class JobProgressionComponent {
27041
+ constructor(jobProgressionService, popoverController, cd) {
27042
+ this.jobProgressionService = jobProgressionService;
27043
+ this.popoverController = popoverController;
27044
+ this.cd = cd;
27045
+ this.debug = false;
27046
+ this.titleI18n = 'SOCIAL.JOB.PROGRESSION.TITLE';
27047
+ this.jobFinished = new EventEmitter();
27048
+ this.disabled = true;
27049
+ this.color = 'accent';
27050
+ this.mode = 'determinate';
27051
+ this.value = 0;
27052
+ this.jobProgressions = [];
27053
+ this._subscriptions = new Subscription();
27054
+ this._subscriptionsById = {};
27055
+ this._logPrefix = '[job-progression] ';
27056
+ }
27057
+ ngOnInit() {
27058
+ var _a, _b, _c, _d;
27059
+ if (isNil(this.jobProgressionService)) {
27060
+ console.warn(`${this._logPrefix}No service injected`);
27061
+ }
27062
+ // parse options
27063
+ this.autoHide = toBoolean((_a = this.options) === null || _a === void 0 ? void 0 : _a.autoHide, false);
27064
+ this.visible = !this.autoHide;
27065
+ this.autoHideDelay = toNumber((_b = this.options) === null || _b === void 0 ? void 0 : _b.autoHideDelay, 1000);
27066
+ this.autoRemove = toBoolean((_c = this.options) === null || _c === void 0 ? void 0 : _c.autoRemove, true);
27067
+ this.autoRemoveDelay = toNumber((_d = this.options) === null || _d === void 0 ? void 0 : _d.autoRemoveDelay, 1000);
27068
+ }
27069
+ addJob(id) {
27070
+ if (isNil(this.jobProgressionService)) {
27071
+ console.warn(`${this._logPrefix}No service injected. Can't add a job`);
27072
+ return;
27073
+ }
27074
+ if (this.debug) {
27075
+ console.debug(`${this._logPrefix}Add job id=${id}`);
27076
+ }
27077
+ if (!!this.getProgression(id)) {
27078
+ console.warn(`${this._logPrefix}Job (id=${id}) already present`);
27079
+ return;
27080
+ }
27081
+ this.disabled = false;
27082
+ const sub = this.jobProgressionService.listenChanges(id)
27083
+ .pipe(filter(progression => {
27084
+ if (id !== progression.id) {
27085
+ console.error(`${this._logPrefix}Job progression (id=${progression.id}) doesn't match expected job (id=${id})`);
27086
+ return false;
27087
+ }
27088
+ return true;
27089
+ }), map(progression => {
27090
+ // Ensure visible
27091
+ this.visible = true;
27092
+ // Get current progression object
27093
+ const currentProgression = this.getProgression(id);
27094
+ if (!currentProgression) {
27095
+ // add it
27096
+ this.jobProgressions.push(progression);
27097
+ if (this.debug) {
27098
+ console.debug(`${this._logPrefix}Progression added id=${id}`, progression);
27099
+ }
27100
+ }
27101
+ else {
27102
+ // update current
27103
+ Object.assign(currentProgression, progression);
27104
+ if (this.debug) {
27105
+ console.debug(`${this._logPrefix}Progression updated id=${id}`, progression);
27106
+ }
27107
+ }
27108
+ return progression;
27109
+ }))
27110
+ .subscribe(progression => {
27111
+ // update value
27112
+ this.updateValue();
27113
+ // job finished
27114
+ if (progression.total > 0 && progression.current == progression.total) {
27115
+ if (this.debug) {
27116
+ console.debug(`${this._logPrefix}Finished job id=${progression.id}`);
27117
+ }
27118
+ // emit event
27119
+ this.jobFinished.emit(progression.id);
27120
+ if (this.autoRemove) {
27121
+ setTimeout(() => {
27122
+ var _a;
27123
+ this.removeJob(progression.id);
27124
+ // If last job
27125
+ if (this.autoHide && !((_a = this.jobProgressions) === null || _a === void 0 ? void 0 : _a.length)) {
27126
+ setTimeout(() => {
27127
+ this.visible = false;
27128
+ this.markForCheck();
27129
+ }, this.autoHideDelay);
27130
+ }
27131
+ }, this.autoRemoveDelay);
27132
+ }
27133
+ }
27134
+ });
27135
+ this._subscriptionsById[id] = sub;
27136
+ this._subscriptions.add(sub);
27137
+ }
27138
+ removeJob(id) {
27139
+ if (this.debug) {
27140
+ console.debug(`${this._logPrefix}Remove job id=${id}`);
27141
+ }
27142
+ const sub = this._subscriptionsById[id];
27143
+ if (sub) {
27144
+ this._subscriptions.remove(sub);
27145
+ sub.unsubscribe();
27146
+ }
27147
+ const progression = this.getProgression(id);
27148
+ if (progression) {
27149
+ this.jobProgressions.splice(this.jobProgressions.indexOf(progression), 1);
27150
+ }
27151
+ this.updateValue();
27152
+ }
27153
+ getProgression(id) {
27154
+ return this.jobProgressions.find(progression => progression.id === id);
27155
+ }
27156
+ updateValue() {
27157
+ if (this.debug) {
27158
+ console.debug(`${this._logPrefix}Updating value`);
27159
+ }
27160
+ let value = 0;
27161
+ const nbProgression = arraySize(this.jobProgressions);
27162
+ let allIndeterminate = nbProgression > 0;
27163
+ this.jobProgressions.forEach(progression => {
27164
+ const indeterminate = progression.total === 0;
27165
+ allIndeterminate = allIndeterminate && indeterminate;
27166
+ if (!indeterminate) {
27167
+ value = value + progression.current * 100 / progression.total;
27168
+ }
27169
+ });
27170
+ this.value = nbProgression > 0 ? value / nbProgression : 0;
27171
+ this.mode = allIndeterminate ? 'indeterminate' : 'determinate';
27172
+ if (this.debug) {
27173
+ console.debug(`${this._logPrefix}Setting value=${this.value}, mode=${this.mode}`);
27174
+ }
27175
+ this.markForCheck();
27176
+ }
27177
+ showList(event) {
27178
+ return __awaiter(this, void 0, void 0, function* () {
27179
+ const popover = yield this.popoverController.create({
27180
+ component: JobProgressionList,
27181
+ componentProps: {
27182
+ titleI18n: this.titleI18n,
27183
+ jobProgressions: this.jobProgressions
27184
+ },
27185
+ backdropDismiss: true,
27186
+ keyboardClose: true,
27187
+ event,
27188
+ translucent: true,
27189
+ cssClass: 'popover-large'
27190
+ });
27191
+ yield popover.present();
27192
+ });
27193
+ }
27194
+ markForCheck() {
27195
+ this.cd.markForCheck();
27196
+ }
27197
+ ngOnDestroy() {
27198
+ if (this.debug) {
27199
+ console.debug(`${this._logPrefix}Destroying`);
27200
+ }
27201
+ this._subscriptions.unsubscribe();
27202
+ }
27203
+ }
27204
+ JobProgressionComponent.decorators = [
27205
+ { type: Component, args: [{
27206
+ selector: 'app-job-progression',
27207
+ template: "<button\n #button\n mat-icon-button\n [title]=\"titleI18n | translate\"\n [disabled]=\"disabled\"\n *ngIf=\"visible\"\n (click)=\"showList($event)\"\n>\n <mat-icon\n [matBadge]=\"jobProgressions?.length\"\n [matBadgeHidden]=\"!jobProgressions?.length\"\n matBadgeColor=\"accent\"\n matBadgeSize=\"small\"\n matBadgePosition=\"above after\"\n >{{ disabled || jobProgressions?.length ? 'schedule' : 'task_alt' }}\n </mat-icon>\n <mat-spinner\n #spinner\n *ngIf=\"jobProgressions?.length\"\n class=\"floating-spinner\"\n [color]=\"color\"\n [mode]=\"mode\"\n [value]=\"value\"\n diameter=\"30\"\n strokeWidth=\"3\"\n ></mat-spinner>\n</button>\n\n",
27208
+ changeDetection: ChangeDetectionStrategy.OnPush,
27209
+ styles: [".floating-spinner{position:absolute;top:6px;left:5px}"]
27210
+ },] }
27211
+ ];
27212
+ JobProgressionComponent.ctorParameters = () => [
27213
+ { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [JobProgressionServiceToken,] }] },
27214
+ { type: PopoverController },
27215
+ { type: ChangeDetectorRef }
27216
+ ];
27217
+ JobProgressionComponent.propDecorators = {
27218
+ debug: [{ type: Input }],
27219
+ titleI18n: [{ type: Input }],
27220
+ options: [{ type: Input }],
27221
+ jobFinished: [{ type: Output }]
27222
+ };
27223
+
27224
+ class JobModule {
27225
+ }
27226
+ JobModule.decorators = [
27227
+ { type: NgModule, args: [{
27228
+ imports: [
27229
+ CommonModule,
27230
+ SharedModule,
27231
+ TranslateModule.forChild(),
27232
+ ],
27233
+ declarations: [
27234
+ JobProgressionComponent,
27235
+ JobProgressionList
27236
+ ],
27237
+ exports: [
27238
+ JobProgressionComponent
27239
+ ]
27240
+ },] }
27241
+ ];
27242
+
27243
+ const SocialModuleOptionsToken = new InjectionToken("SocialModuleOptions");
27244
+ class SocialModule {
27245
+ }
27246
+ SocialModule.decorators = [
27247
+ { type: NgModule, args: [{
27248
+ imports: [
27249
+ CommonModule,
27250
+ CoreModule,
27251
+ SharedModule,
27252
+ UserEventModule,
27253
+ JobModule,
27254
+ MessageModule,
27255
+ ],
27256
+ },] }
27257
+ ];
27258
+
26611
27259
  const Mutations = {
26612
27260
  send: gql `mutation SendMessage($data: MessageVOInput){
26613
27261
  done: sendMessage(message: $data)
@@ -27253,12 +27901,10 @@ class AutocompleteTestPage {
27253
27901
  });
27254
27902
  }
27255
27903
  ngAfterViewInit() {
27256
- if (this.farEntityField) {
27257
- setTimeout(() => {
27258
- this.farEntityField.suggestLengthThreshold = 2;
27259
- this.farEntityField.reloadItems();
27260
- }, 2000);
27261
- }
27904
+ setTimeout(() => {
27905
+ this.farEntityField.suggestLengthThreshold = 2;
27906
+ this.farEntityField.reloadItems();
27907
+ }, 2000);
27262
27908
  setTimeout(() => this.loadData(), 1000);
27263
27909
  }
27264
27910
  // Load the form with data
@@ -27346,7 +27992,7 @@ class AutocompleteTestPage {
27346
27992
  AutocompleteTestPage.decorators = [
27347
27993
  { type: Component, args: [{
27348
27994
  selector: 'app-autocomplete-test',
27349
- template: "<ion-header>\n <ion-toolbar color=\"primary\">\n\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>Autocomplete field test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content>\n\n <!-- Tab nav - mobile/desktop mode -->\n <nav mat-tab-nav-bar>\n <a mat-tab-link\n [active]=\"mode==='mobile'\"\n (click)=\"toggleMode('mobile')\">\n <mat-label>Mobile</mat-label>\n </a>\n <a mat-tab-link [active]=\"mode==='desktop'\"\n (click)=\"toggleMode('desktop')\">\n <mat-label>Desktop</mat-label>\n </a>\n <a mat-tab-link [active]=\"mode==='memory'\"\n (click)=\"toggleMode('memory')\">\n <mat-label>Memory leak debug</mat-label>\n </a>\n <a mat-tab-link\n [active]=\"mode==='temp'\"\n (click)=\"toggleMode('temp')\">\n <mat-label>Temporary</mat-label>\n </a>\n </nav>\n\n <form class=\"form-container\" [formGroup]=\"form\" (ngSubmit)=\"doSubmit($event)\">\n\n <!--<ion-grid *ngIf=\"mode === 'temp'\">\n <ion-row>\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an Observable\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>items: Observable&lt;any[]&gt;</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [logPrefix]=\"'[combo-desktop-2]'\"\n [compareWith]=\"compareWithFn\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n\n </ion-card>\n </ion-col>\n </ion-row>\n\n\n </ion-grid>-->\n\n\n <ion-grid *ngIf=\"mode === 'memory'\">\n\n <!-- debugging memory leak -->\n <ion-row>\n <ion-col>\n <ion-text><h4>Debug memory leak</h4></ion-text>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col size=\"2\">\n <mat-form-field floatLabel=\"always\">\n <input matInput type=\"text\" hidden placeholder=\"Items type\">\n <mat-select (selectionChange)=\"memoryAutocompleteFieldName=$event.value\" [value]=\"memoryAutocompleteFieldName\">\n <mat-option value=\"entity-$items\">Observable</mat-option>\n <mat-option value=\"entity-suggestFn\">Suggest function</mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n <ion-col size=\"1\" class=\"ion-no-padding\">\n <mat-form-field floatLabel=\"always\">\n <input matInput type=\"text\" hidden placeholder=\"Mobile ?\">\n <mat-checkbox (change)=\"memoryMobile=$event.checked\" [checked]=\"memoryMobile\">\n </mat-checkbox>\n </mat-form-field>\n </ion-col>\n\n <ion-col size=\"2\">\n <ion-button *ngIf=\"!memoryTimer\" (click)=\"startMemoryTimer()\" color=\"tertiary\">Start</ion-button>\n <ion-button *ngIf=\"memoryTimer\" (click)=\"stopMemoryTimer()\" color=\"tertiary\">Stop</ion-button>\n </ion-col>\n </ion-row>\n\n <ion-row>\n <ion-col>\n <mat-autocomplete-field formControlName=\"entity\"\n *ngIf=\"!memoryHide && memoryAutocompleteFieldName\"\n [config]=\"autocompleteFields.get(memoryAutocompleteFieldName)\"\n [mobile]=\"memoryMobile\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-memory-leak]'\">\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n <!-- Mobile mode -->\n <ion-grid *ngIf=\"mode === 'mobile'\">\n\n <ion-row>\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Entity far in the list (not in first page)\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre style=\"white-space: normal !important;\">value: {{stringify(form.controls.farEntity.value)}}</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"farEntity\" #farEntityField\n [config]=\"autocompleteFields.get('entity-far')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-far]'\"\n [debug]=\"true\"\n [required]=\"true\"\n [clearable]=\"true\"\n placeholder=\"Far entity (in the list)\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Suggest() function\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>mobile: true, suggestFn: (searchText, filter) =&gt; any[]</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-1]'\"\n [required]=\"true\"\n placeholder=\"Suggest field\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Items is an Observable\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>mobile: true, items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-observable]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Control value if missing in items\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>mobile: true, items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"missingEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [clearable]=\"true\"\n [logPrefix]=\"'[combo-mobile-2]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n search with a suggestLengthThreshold\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>suggest: (value, filter) => LoadResult&lt;any&gt;\nsuggestLengthThreshold: '3'</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n suggestLengthThreshold=\"3\"\n [logPrefix]=\"'[combo-mobile-suggestLengthThreshold]'\"\n required\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Multiple value\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>multiple: true</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entities\"\n [config]=\"autocompleteFields.get('entities-items-filter')\"\n [placeholder]=\"'Multiple'\"\n mobile=\"true\"\n multiple=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-multiple]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"disableEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-2]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n </ion-row>\n\n <ion-row>\n <ion-col size=\"9\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Change filter\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre></pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col>\n <ion-button (click)=\"updateFilter(filterChangeField, 'entity-items-filter')\">Filter on: {{autocompleteFields.get('entity-items-filter').filter?.searchAttribute }}</ion-button>\n </ion-col>\n <ion-col>\n <mat-autocomplete-field formControlName=\"entity\" #filterChangeField\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-filter]'\"\n ></mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"3\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Large combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class=\"min-width-large\"</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"true\"\n class=\"min-width-large\"\n panelWidth=\"400px\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-large]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Readonly toggle\n </ion-text>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid>\n <ion-row>\n <ion-col size=\"3\">\n <mat-checkbox (change)=\"readonlyField.readonly=$event.checked\" [checked]=\"readonlyField.readonly\">\n Readonly ?\n </mat-checkbox>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col>\n <mat-autocomplete-field #readonlyField formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-readonly]'\"\n [readonly]=\"true\"\n [clearable]=\"!readonlyField.readonly\">\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Fullscreen\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class: 'mat-autocomplete-panel-full-size'</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [class]=\"'mat-autocomplete-panel-full-size'\"\n [matAutocompletePosition]=\"'above'\"\n [logPrefix]=\"'[combo-mobile-full-size]'\"\n [debug]=\"true\"\n [showAllOnFocus]=\"true\"\n [showPanelOnFocus]=\"true\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n <!-- Desktop mode -->\n <ion-grid *ngIf=\"mode === 'desktop'\">\n <ion-row>\n <ion-col>\n <ion-text><h4>Desktop mode</h4></ion-text>\n </ion-col>\n </ion-row>\n <ion-row>\n\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n items from suggest function\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>suggest: (value, filter) => LoadResult&lt;any&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"false\"\n [equals]=\"equals\"\n [clearable]=\"true\"\n [logPrefix]=\"'[combo-desktop-suggest]'\"\n [debug]=\"true\">\n </mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n search with a suggestLengthThreshold\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>suggest: (value, filter) => LoadResult&lt;any&gt;\nsuggestLengthThreshold: '3'</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"false\"\n [equals]=\"equals\"\n suggestLengthThreshold=\"3\"\n [logPrefix]=\"'[combo-desktop-suggestLengthThreshold]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an array\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>items: [], value: {{stringify(form.controls.entity.value)}}</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items')\"\n [logPrefix]=\"'[combo-desktop-array-1]'\"\n [equals]=\"equals\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an Observable\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [logPrefix]=\"'[combo-desktop-2]'\"\n [equals]=\"equals\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Multiple value\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>multiple: true</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entities\"\n [config]=\"autocompleteFields.get('entities-items-filter')\"\n [placeholder]=\"'Multiple'\"\n mobile=\"false\"\n multiple=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-multiple]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Control value if missing in items\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"missingEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-missing]'\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Full size panel\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class: 'mat-autocomplete-panel-full-size'</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [class]=\"'mat-autocomplete-panel-full-size'\"\n [matAutocompletePosition]=\"'above'\"\n [logPrefix]=\"'[combo-desktop-full-size]'\"\n [debug]=\"true\"\n [showAllOnFocus]=\"true\"\n [showPanelOnFocus]=\"true\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"disableEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-disable]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Readonly control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-readonly]'\"\n [readonly]=\"true\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n\n <ion-row>\n <ion-col size=\"9\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Full size combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class=\"mat-autocomplete-panel-full-size\"</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"false\"\n class=\"mat-autocomplete-panel-full-size\"\n panelWidth=\"100vw\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-full-size]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"3\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Large combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>panelWidth: string</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"false\"\n panelWidth=\"400px\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-large]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Readonly toggle\n </ion-text>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid>\n <ion-row>\n <ion-col size=\"3\">\n <mat-checkbox (change)=\"readonlyField.readonly=$event.checked\" [checked]=\"readonlyField.readonly\">\n Readonly ?\n </mat-checkbox>\n </ion-col>\n <ion-col size=\"3\">\n <mat-checkbox #showIcons [checked]=\"false\">\n Icons ?\n </mat-checkbox>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col>\n <mat-autocomplete-field #readonlyField formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"false\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-readonly]'\"\n [readonly]=\"true\"\n [clearable]=\"!readonlyField.readonly\">\n <mat-icon matPrefix *ngIf=\"showIcons.checked\">keyboard_arrow_right</mat-icon>\n <mat-icon matSuffix *ngIf=\"showIcons.checked\">keyboard_arrow_left</mat-icon>\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n </form>\n\n</ion-content>\n"
27995
+ template: "<ion-header>\n <ion-toolbar color=\"primary\">\n\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>Autocomplete field test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content>\n\n <!-- Tab nav - mobile/desktop mode -->\n <nav mat-tab-nav-bar>\n <a mat-tab-link\n [active]=\"mode==='mobile'\"\n (click)=\"toggleMode('mobile')\">\n <mat-label>Mobile</mat-label>\n </a>\n <a mat-tab-link [active]=\"mode==='desktop'\"\n (click)=\"toggleMode('desktop')\">\n <mat-label>Desktop</mat-label>\n </a>\n <a mat-tab-link [active]=\"mode==='memory'\"\n (click)=\"toggleMode('memory')\">\n <mat-label>Memory leak debug</mat-label>\n </a>\n <a mat-tab-link\n [active]=\"mode==='temp'\"\n (click)=\"toggleMode('temp')\">\n <mat-label>Temporary</mat-label>\n </a>\n </nav>\n\n <form class=\"form-container\" [formGroup]=\"form\" (ngSubmit)=\"doSubmit($event)\">\n\n <!--<ion-grid *ngIf=\"mode === 'temp'\">\n <ion-row>\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an Observable\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>items: Observable&lt;any[]&gt;</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [logPrefix]=\"'[combo-desktop-2]'\"\n [compareWith]=\"compareWithFn\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n\n </ion-card>\n </ion-col>\n </ion-row>\n\n\n </ion-grid>-->\n\n\n <ion-grid *ngIf=\"mode === 'memory'\">\n\n <!-- debugging memory leak -->\n <ion-row>\n <ion-col>\n <ion-text><h4>Debug memory leak</h4></ion-text>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col size=\"2\">\n <mat-form-field floatLabel=\"always\">\n <input matInput type=\"text\" hidden placeholder=\"Items type\">\n <mat-select (selectionChange)=\"memoryAutocompleteFieldName=$event.value\" [value]=\"memoryAutocompleteFieldName\">\n <mat-option value=\"entity-$items\">Observable</mat-option>\n <mat-option value=\"entity-suggestFn\">Suggest function</mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n <ion-col size=\"1\" class=\"ion-no-padding\">\n <mat-form-field floatLabel=\"always\">\n <input matInput type=\"text\" hidden placeholder=\"Mobile ?\">\n <mat-checkbox (change)=\"memoryMobile=$event.checked\" [checked]=\"memoryMobile\">\n </mat-checkbox>\n </mat-form-field>\n </ion-col>\n\n <ion-col size=\"2\">\n <ion-button *ngIf=\"!memoryTimer\" (click)=\"startMemoryTimer()\" color=\"tertiary\">Start</ion-button>\n <ion-button *ngIf=\"memoryTimer\" (click)=\"stopMemoryTimer()\" color=\"tertiary\">Stop</ion-button>\n </ion-col>\n </ion-row>\n\n <ion-row>\n <ion-col>\n <mat-autocomplete-field formControlName=\"entity\"\n *ngIf=\"!memoryHide && memoryAutocompleteFieldName\"\n [config]=\"autocompleteFields.get(memoryAutocompleteFieldName)\"\n [mobile]=\"memoryMobile\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-memory-leak]'\">\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n <!-- Mobile mode -->\n <ion-grid *ngIf=\"mode === 'mobile'\">\n\n <ion-row>\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Entity far in the list (not in first page)\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre style=\"white-space: normal !important;\">value: {{stringify(form.controls.farEntity.value)}}</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"farEntity\" #farEntityField\n [config]=\"autocompleteFields.get('entity-far')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-far]'\"\n [debug]=\"true\"\n [required]=\"true\"\n [clearable]=\"true\"\n placeholder=\"Far entity (in the list)\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Suggest() function\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>mobile: true, suggestFn: (searchText, filter) =&gt; any[]</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-1]'\"\n [required]=\"true\"\n placeholder=\"Suggest field\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Items is an Observable\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>mobile: true, items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-observable]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Control value if missing in items\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>mobile: true, items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"missingEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [clearable]=\"true\"\n [logPrefix]=\"'[combo-mobile-2]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n search with a suggestLengthThreshold\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>suggest: (value, filter) => LoadResult&lt;any&gt;\nsuggestLengthThreshold: '3'</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n suggestLengthThreshold=\"3\"\n [logPrefix]=\"'[combo-mobile-suggestLengthThreshold]'\"\n required\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Multiple value\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>multiple: true</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entities\"\n [config]=\"autocompleteFields.get('entities-items-filter')\"\n [placeholder]=\"'Multiple'\"\n mobile=\"true\"\n multiple=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-multiple]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"disableEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-2]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n </ion-row>\n\n <ion-row>\n <ion-col size=\"9\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Change filter\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre></pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col>\n <ion-button (click)=\"updateFilter(filterChangeField, 'entity-items-filter')\">Filter on: {{autocompleteFields.get('entity-items-filter').filter?.searchAttribute }}</ion-button>\n </ion-col>\n <ion-col>\n <mat-autocomplete-field formControlName=\"entity\" #filterChangeField\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-filter]'\"\n ></mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"3\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Large combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class=\"min-width-large\"</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"true\"\n class=\"min-width-large\"\n panelWidth=\"400px\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-large]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Readonly toggle\n </ion-text>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid>\n <ion-row>\n <ion-col size=\"3\">\n <mat-checkbox (change)=\"readonlyField.readonly=$event.checked\" [checked]=\"readonlyField.readonly\">\n Readonly ?\n </mat-checkbox>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col>\n <mat-autocomplete-field #readonlyField formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-mobile-readonly]'\"\n [readonly]=\"true\"\n [clearable]=\"!readonlyField.readonly\">\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Fullscreen\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class: 'mat-autocomplete-panel-full-size'</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [class]=\"'mat-autocomplete-panel-full-size'\"\n [matAutocompletePosition]=\"'above'\"\n [logPrefix]=\"'[combo-mobile-full-size]'\"\n [debug]=\"true\"\n [showAllOnFocus]=\"true\"\n [showPanelOnFocus]=\"true\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n <!-- Desktop mode -->\n <ion-grid *ngIf=\"mode === 'desktop'\">\n <ion-row>\n <ion-col>\n <ion-text><h4>Desktop mode</h4></ion-text>\n </ion-col>\n </ion-row>\n <ion-row>\n\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n items from suggest function\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>suggest: (value, filter) => LoadResult&lt;any&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"false\"\n [equals]=\"equals\"\n [clearable]=\"true\"\n [logPrefix]=\"'[combo-desktop-suggest]'\">\n </mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n search with a suggestLengthThreshold\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>suggest: (value, filter) => LoadResult&lt;any&gt;\nsuggestLengthThreshold: '3'</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"false\"\n [equals]=\"equals\"\n suggestLengthThreshold=\"3\"\n [logPrefix]=\"'[combo-desktop-suggestLengthThreshold]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an array\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>items: [], value: {{stringify(form.controls.entity.value)}}</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items')\"\n [logPrefix]=\"'[combo-desktop-array-1]'\"\n [equals]=\"equals\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an Observable\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [logPrefix]=\"'[combo-desktop-2]'\"\n [equals]=\"equals\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Multiple value\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>multiple: true</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entities\"\n [config]=\"autocompleteFields.get('entities-items-filter')\"\n [placeholder]=\"'Multiple'\"\n mobile=\"false\"\n multiple=\"true\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-multiple]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Control value if missing in items\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"missingEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-missing]'\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Full size panel\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class: 'mat-autocomplete-panel-full-size'</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [class]=\"'mat-autocomplete-panel-full-size'\"\n [matAutocompletePosition]=\"'above'\"\n [logPrefix]=\"'[combo-desktop-full-size]'\"\n [debug]=\"true\"\n [showAllOnFocus]=\"true\"\n [showPanelOnFocus]=\"true\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"disableEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-disable]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Readonly control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-readonly]'\"\n [readonly]=\"true\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n\n <ion-row>\n <ion-col size=\"9\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Full size combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class=\"mat-autocomplete-panel-full-size\"</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"false\"\n class=\"mat-autocomplete-panel-full-size\"\n panelWidth=\"100vw\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-full-size]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"3\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Large combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>panelWidth: string</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"false\"\n panelWidth=\"400px\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-large]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Readonly toggle\n </ion-text>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid>\n <ion-row>\n <ion-col size=\"3\">\n <mat-checkbox (change)=\"readonlyField.readonly=$event.checked\" [checked]=\"readonlyField.readonly\">\n Readonly ?\n </mat-checkbox>\n </ion-col>\n <ion-col size=\"3\">\n <mat-checkbox #showIcons [checked]=\"false\">\n Icons ?\n </mat-checkbox>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col>\n <mat-autocomplete-field #readonlyField formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"false\"\n [equals]=\"equals\"\n [logPrefix]=\"'[combo-desktop-readonly]'\"\n [readonly]=\"true\"\n [clearable]=\"!readonlyField.readonly\">\n <mat-icon matPrefix *ngIf=\"showIcons.checked\">keyboard_arrow_right</mat-icon>\n <mat-icon matSuffix *ngIf=\"showIcons.checked\">keyboard_arrow_left</mat-icon>\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n </form>\n\n</ion-content>\n"
27350
27996
  },] }
27351
27997
  ];
27352
27998
  AutocompleteTestPage.ctorParameters = () => [
@@ -27422,13 +28068,13 @@ LatLongTestPage.ctorParameters = () => [
27422
28068
  { type: FormBuilder }
27423
28069
  ];
27424
28070
 
27425
- const moment$6 = momentImported;
28071
+ const moment$7 = momentImported;
27426
28072
  class SwipeTestPage {
27427
28073
  constructor(formBuilder, dateFormatPipe) {
27428
28074
  this.formBuilder = formBuilder;
27429
28075
  this.dateFormatPipe = dateFormatPipe;
27430
28076
  this.$dates = new BehaviorSubject(undefined);
27431
- this._today = moment$6().startOf('day');
28077
+ this._today = moment$7().startOf('day');
27432
28078
  this.form = formBuilder.group({
27433
28079
  empty: [null, Validators.required],
27434
28080
  date: [null, Validators.compose([Validators.required, SharedValidators.validDate])],
@@ -27441,7 +28087,7 @@ class SwipeTestPage {
27441
28087
  ngOnInit() {
27442
28088
  const dates = [];
27443
28089
  for (let d = 0; d < 7; d++) {
27444
- dates[d] = moment$6(this._today).add(d - 3, 'day');
28090
+ dates[d] = moment$7(this._today).add(d - 3, 'day');
27445
28091
  }
27446
28092
  this.$dates.next(dates);
27447
28093
  this.loadData();
@@ -27482,7 +28128,7 @@ SwipeTestPage.ctorParameters = () => [
27482
28128
  { type: DateFormatPipe }
27483
28129
  ];
27484
28130
 
27485
- const moment$7 = momentImported;
28131
+ const moment$8 = momentImported;
27486
28132
  class DateTimeTestPage {
27487
28133
  constructor(platform, formBuilder, cd) {
27488
28134
  this.platform = platform;
@@ -27514,7 +28160,7 @@ class DateTimeTestPage {
27514
28160
  // Load the form with data
27515
28161
  loadData() {
27516
28162
  return __awaiter(this, void 0, void 0, function* () {
27517
- const now = moment$7();
28163
+ const now = moment$8();
27518
28164
  const data = {
27519
28165
  empty: null,
27520
28166
  emptyRequired: null,
@@ -27760,7 +28406,7 @@ NumpadTestPage.ctorParameters = () => [
27760
28406
  { type: FormBuilder }
27761
28407
  ];
27762
28408
 
27763
- const moment$8 = momentImported;
28409
+ const moment$9 = momentImported;
27764
28410
  class DateTestPage {
27765
28411
  constructor(formBuilder, cd) {
27766
28412
  this.formBuilder = formBuilder;
@@ -27793,7 +28439,7 @@ class DateTestPage {
27793
28439
  // Load the form with data
27794
28440
  loadData() {
27795
28441
  return __awaiter(this, void 0, void 0, function* () {
27796
- const now = moment$8();
28442
+ const now = moment$9();
27797
28443
  const nowAtMahe = now.clone().tz(this.timezone).startOf('day');
27798
28444
  const data = {
27799
28445
  empty: null,
@@ -28539,5 +29185,5 @@ CoreTestingModule.decorators = [
28539
29185
  * Generated bundle index. Do not edit.
28540
29186
  */
28541
29187
 
28542
- export { APP_ABOUT_DEVELOPERS, APP_ABOUT_PARTNERS, APP_CONFIG_OPTIONS, APP_FORM_ERROR_I18N_KEYS, 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, AndroidOsEnvironment, AnimationState, AppEditor, AppEditorOptions, AppEntityEditor, AppEntityEditorModal, AppEntityEditorModalOptions, 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, AutoTitleDirective, AutocompleteTestPage, AutofocusDirective, Base58, BaseEntityService, BaseGraphqlService, BaseGraphqlServiceOptions, BaseReferential, Beans, CORE_CONFIG_OPTIONS, CORE_TESTING_PAGES, CellValueChangeListener, ChipsTestPage, Color, ColorScale, ComponentDirtyGuard, ConfigService, Configuration, CoreModule, CoreTestingModule, CryptoService, CsvUtils, DATE_ISO_PATTERN, DATE_UNIX_MS_TIMESTAMP, DATE_UNIX_TIMESTAMP, DEFAULT_MENU_SHOW_WHEN, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLACEHOLDER_CHAR, DEFAULT_REQUIRED_COLUMNS, DateDiffDurationPipe, DateFormatPipe, DateFromNowPipe, DateTimeTestPage, DateUtils, Department, DepartmentToStringPipe, DragAndDropDirective, DurationPipe, ENTITIES_STORAGE_KEY_PREFIX, ENVIRONMENT, EmptyArrayPipe, EntitiesService, EntitiesStorage, EntitiesTableDataSource, Entity, EntityClass, EntityClasses, EntityFilter, EntityFilterUtils, EntityMetadataComponent, EntityStore, EntityUtils, Environment, ErrorCodes, EvenPipe, FileResponse, FileService, FileSizePipe, FilesUtils, FormArrayHelper, FormButtonsBarComponent, FormButtonsBarToken, FormErrorTranslatePipe, FormErrorTranslator, FormFieldValuesHolder, FormGetArrayPipe, FormGetControlPipe, FormGetGroupPipe, FormGetPipe, FormGetValuePipe, Fragments$1 as 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, MatBadgeIconDirective, MatBooleanField, MatChipsField, MatDate, MatDateShort, MatDateTime, MatDuration, MatLatLongField, MatNumpadComponent, MatNumpadContainerComponent, MatNumpadContent, MatPaginatorI18n, MatStepperI18n, MatSwipeField, MaterialTestingModule, MathAbsPipe, MenuComponent, MenuItems, MenuService, Message, MessageFilter, MessageForm, MessageModal, MessageService, MessageTypeList, MessageTypes, MimeTypes, ModalToolbarComponent, NetworkService, NgInitDirective, NgVarDirective, NotEmptyArrayPipe, NumberFormatPipe, NumpadDirective, OddPipe, PRIORITIZED_AUTHORITIES, PUBKEY_REGEXP, Peer, Person, PersonFilter, PersonFragments, PersonService, PersonToStringPipe, PersonUtils, PersonValidatorService, PlatformService, ProgressBarService, ProgressInterceptor, PropertyFormatPipe, PropertyGetPipe, RESERVED_END_COLUMNS, RESERVED_START_COLUMNS, Referential, ReferentialFilter, ReferentialRef, ReferentialUtils, ReferentialValidatorService, RegisterConfirmPage, ResizableComponent, ResizableDirective, ResizableModule, SETTINGS_DISPLAY_COLUMNS, SETTINGS_FILTER, SETTINGS_PAGE_SIZE, SETTINGS_SORTED_COLUMN, SETTINGS_STORAGE_KEY, SETTINGS_TRANSIENT_PROPERTIES, SHARED_MATERIAL_TESTING_PAGES, SHARED_TESTING_PAGES, SPACE_PLACEHOLDER_CHAR, SelectPeerModal, ServerErrorCodes, SettingsPage, SharedAsyncValidators, SharedDirectivesModule, SharedFormArrayValidators, SharedFormGroupValidators, SharedHotkeysModule, SharedMatAutocompleteModule, SharedMatBadgeIconModule, SharedMatBooleanModule, SharedMatChipsModule, SharedMatDateTimeModule, SharedMatDurationModule, SharedMatLatLongModule, SharedMatNumpadModule, SharedMatSwipeModule, SharedMaterialModule, SharedModule, SharedPipesModule, SharedRoutingModule, SharedTestingModule, SharedTestsPage, SharedValidators, SocialErrorCodes, SocialModule, Software, StartableService, StatusById, StatusIds, StatusList, StrIncludesPipe, StrLengthPipe, SwipeTestPage, TableSelectColumnsComponent, TableTestPage, TableTestingModule, TextForm, TextPopover, TextPopoverTestingModule, TextPopoverTestingPage, ToStringPipe, Toasts, ToolbarComponent, ToolbarToken, TranslatablePipe, TranslateContextPipe, TranslateContextService, UploadFile, UploadFileComponent, UploadFilePopover, UploadFileTestingModule, UploadFileTestingPage, UriUtils, UserEvent, UserEventFilter, UserEventFragments, 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, equals, equalsOrNil, escapeRegExp, fadeInAnimation, fadeInOutAnimation, filterFalse, filterNotNil, filterNumberInput, filterTrue, firstArrayValue, firstFalse, firstFalsePromise, firstNotNil, firstNotNilPromise, firstTrue, 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, isAndroid, isBlankString, isControlHasInput, isCordova, isEmptyArray, isIOS, isInputElement, isInstanceOf, isInt, isMobile, isNil, isNilOrBlank, isNilOrNaN, isNotEmptyArray, isNotNil, isNotNilBoolean, isNotNilOrBlank, isNotNilOrNaN, isNotNilString, isNumber, isNumberRange, isProgressEvent, isResponseEvent, isTouchUi, isWindows, joinProperties, joinPropertiesPath, logFormErrors, markAllAsTouched, markAsUntouched, markControlAsTouched, markFormGroupAsTouched, matchMedia, matchUpperCase, mergeLoadResult, mixHex, moment$5 as moment, 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, testUserAgent, toBoolean, toDateISOString, toDuration, toFloat, toInt, toNotNil, toNumber, trimEmptyToNull, tz, uncapitalizeFirstLetter, updateValueAndValidity, waitFor, waitForTrue, waitIdle, waitWhilePending, ɵ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, AppUpdateOfflineModeCard as ɵi, AppIconComponent as ɵj, DateTestPage as ɵk, NumpadTestPage as ɵl, MatBadgeIconTestPage as ɵm, ToastTestingModule as ɵn, ToastTestingPage as ɵo };
29188
+ export { APP_ABOUT_DEVELOPERS, APP_ABOUT_PARTNERS, APP_CONFIG_OPTIONS, APP_FORM_ERROR_I18N_KEYS, 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, AbstractUserEventService, Account, AccountPage, AccountService, AccountToStringPipe, ActionsColumnComponent, AdminModule, AdminRoutingModule, Alerts, AndroidOsEnvironment, AnimationState, AppEditor, AppEditorOptions, AppEntityEditor, AppEntityEditorModal, AppEntityEditorModalOptions, 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, AutoTitleDirective, AutocompleteTestPage, AutofocusDirective, Base58, BaseEntityService, BaseGraphqlService, BaseGraphqlServiceOptions, BaseReferential, Beans, CORE_CONFIG_OPTIONS, CORE_TESTING_PAGES, CellValueChangeListener, ChipsTestPage, Color, ColorScale, ComponentDirtyGuard, ConfigService, Configuration, CoreModule, CoreTestingModule, CryptoService, CsvUtils, DATE_ISO_PATTERN, DATE_UNIX_MS_TIMESTAMP, DATE_UNIX_TIMESTAMP, DEFAULT_MENU_SHOW_WHEN, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLACEHOLDER_CHAR, DEFAULT_REQUIRED_COLUMNS, DateDiffDurationPipe, DateFormatPipe, DateFromNowPipe, DateTimeTestPage, DateUtils, Department, DepartmentToStringPipe, DragAndDropDirective, DurationPipe, ENTITIES_STORAGE_KEY_PREFIX, ENVIRONMENT, EmptyArrayPipe, EntitiesService, EntitiesStorage, EntitiesTableDataSource, Entity, EntityClass, EntityClasses, EntityFilter, EntityFilterUtils, EntityMetadataComponent, EntityStore, EntityUtils, Environment, ErrorCodes, EvenPipe, FileResponse, FileService, FileSizePipe, FilesUtils, FormArrayHelper, FormButtonsBarComponent, FormButtonsBarToken, FormErrorTranslatePipe, FormErrorTranslator, FormFieldValuesHolder, FormGetArrayPipe, FormGetControlPipe, FormGetGroupPipe, FormGetPipe, FormGetValuePipe, Fragments$1 as Fragments, GraphqlService, HAMMER_PRESS_TIME, HAMMER_TAP_TIME, HighlightPipe, HomePage, Hotkeys, HotkeysDialogComponent, InMemoryEntitiesService, IsLoginAccountPipe, IsNilOrBlankPipe, IsNotNilOrBlankPipe, IsNotOnFieldModePipe, IsOnFieldModePipe, Job, JobModule, JobProgression, JobProgressionComponent, JobProgressionService, JobProgressionServiceToken, 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, MatBadgeIconDirective, MatBooleanField, MatChipsField, MatDate, MatDateShort, MatDateTime, MatDuration, MatLatLongField, MatNumpadComponent, MatNumpadContainerComponent, MatNumpadContent, MatPaginatorI18n, MatStepperI18n, MatSwipeField, MaterialTestingModule, MathAbsPipe, MenuComponent, MenuItems, MenuService, Message, MessageFilter, MessageForm, MessageModal, MessageModule, MessageService, MessageTypeList, MessageTypes, MimeTypes, ModalToolbarComponent, NetworkService, NgInitDirective, NgVarDirective, NotEmptyArrayPipe, NumberFormatPipe, NumpadDirective, OddPipe, PRIORITIZED_AUTHORITIES, PUBKEY_REGEXP, Peer, Person, PersonFilter, PersonFragments, PersonService, PersonToStringPipe, PersonUtils, PersonValidatorService, PlatformService, ProgressBarService, ProgressInterceptor, PropertyFormatPipe, PropertyGetPipe, RESERVED_END_COLUMNS, RESERVED_START_COLUMNS, Referential, ReferentialFilter, ReferentialRef, ReferentialUtils, ReferentialValidatorService, RegisterConfirmPage, ResizableComponent, ResizableDirective, ResizableModule, SETTINGS_DISPLAY_COLUMNS, SETTINGS_FILTER, SETTINGS_PAGE_SIZE, SETTINGS_SORTED_COLUMN, SETTINGS_STORAGE_KEY, SETTINGS_TRANSIENT_PROPERTIES, SHARED_MATERIAL_TESTING_PAGES, SHARED_TESTING_PAGES, SPACE_PLACEHOLDER_CHAR, SelectPeerModal, ServerErrorCodes, SettingsPage, SharedAsyncValidators, SharedDirectivesModule, SharedFormArrayValidators, SharedFormGroupValidators, SharedHotkeysModule, SharedMatAutocompleteModule, SharedMatBadgeIconModule, SharedMatBooleanModule, SharedMatChipsModule, SharedMatDateTimeModule, SharedMatDurationModule, SharedMatLatLongModule, SharedMatNumpadModule, SharedMatSwipeModule, SharedMaterialModule, SharedModule, SharedPipesModule, SharedRoutingModule, SharedTestingModule, SharedTestsPage, SharedValidators, SocialErrorCodes, SocialModule, SocialModuleOptionsToken, Software, StartableService, StatusById, StatusIds, StatusList, StrIncludesPipe, StrLengthPipe, SwipeTestPage, TableSelectColumnsComponent, TableTestPage, TableTestingModule, TextForm, TextPopover, TextPopoverTestingModule, TextPopoverTestingPage, ToStringPipe, Toasts, ToolbarComponent, ToolbarToken, TranslatablePipe, TranslateContextPipe, TranslateContextService, UploadFile, UploadFileComponent, UploadFilePopover, UploadFileTestingModule, UploadFileTestingPage, UriUtils, UserEvent, UserEventFilter, UserEventFragments, UserEventModule, UserEventNotificationComponent, UserEventService, UserEventServiceToken, UserEventTypes, UserEventsTable, UserSettings, UsersPage, accountToString, adaptValueToControl, addValueInArray, arrayDistinct, arrayGroupBy, arraySize, asInputElement, canHaveFocus, capitalizeFirstLetter, chainPromises, changeCaseToUnderscore, clearValueInArray, copyEntity2Form, createPromiseEvent, createPromiseEventEmitter, departmentToString, departmentsToString, disableControls, emitPromiseEvent, entityToString, equals, equalsOrNil, escapeRegExp, fadeInAnimation, fadeInOutAnimation, filterFalse, filterNotNil, filterNumberInput, filterTrue, firstArrayValue, firstFalse, firstFalsePromise, firstNotNil, firstNotNilPromise, firstTrue, 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, isAndroid, isBlankString, isControlHasInput, isCordova, isEmptyArray, isIOS, isInputElement, isInstanceOf, isInt, isMobile, isNil, isNilOrBlank, isNilOrNaN, isNotEmptyArray, isNotNil, isNotNilBoolean, isNotNilOrBlank, isNotNilOrNaN, isNotNilString, isNumber, isNumberRange, isProgressEvent, isResponseEvent, isTouchUi, isWindows, joinProperties, joinPropertiesPath, logFormErrors, markAllAsTouched, markAsUntouched, markControlAsTouched, markFormGroupAsTouched, matchMedia, matchUpperCase, mergeLoadResult, mixHex, moment$5 as moment, 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, testUserAgent, toBoolean, toDateISOString, toDuration, toFloat, toInt, toNotNil, toNumber, trimEmptyToNull, tz, uncapitalizeFirstLetter, updateValueAndValidity, waitFor, waitForTrue, waitIdle, waitWhilePending, ɵ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, AppUpdateOfflineModeCard as ɵi, AppIconComponent as ɵj, UserEventNotificationList as ɵk, JobProgressionList as ɵl, DateTestPage as ɵm, NumpadTestPage as ɵn, MatBadgeIconTestPage as ɵo, ToastTestingModule as ɵp, ToastTestingPage as ɵq };
28543
29189
  //# sourceMappingURL=sumaris-net.ngx-components.js.map