@sumaris-net/ngx-components 2.2.1-rc9 → 2.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2020/src/app/core/form/form.utils.mjs +30 -14
- package/esm2020/src/app/core/table/actions-column.component.mjs +3 -3
- package/esm2020/src/app/shared/forms.mjs +11 -5
- package/esm2020/src/app/social/user-event/user-event.service.mjs +6 -3
- package/fesm2015/sumaris-net.ngx-components.mjs +39 -20
- package/fesm2015/sumaris-net.ngx-components.mjs.map +1 -1
- package/fesm2020/sumaris-net.ngx-components.mjs +43 -18
- package/fesm2020/sumaris-net.ngx-components.mjs.map +1 -1
- package/package.json +1 -1
- package/src/app/core/form/form.utils.d.ts +5 -7
|
@@ -4160,7 +4160,7 @@ function disableControls(form, paths, opts) {
|
|
|
4160
4160
|
});
|
|
4161
4161
|
}
|
|
4162
4162
|
function addValueInArray(arrayControl, createControl, equals, isEmpty, value, options) {
|
|
4163
|
-
|
|
4163
|
+
const disabled = arrayControl.disabled;
|
|
4164
4164
|
let hasChanged = false;
|
|
4165
4165
|
let index = -1;
|
|
4166
4166
|
// Search if value already exists
|
|
@@ -4180,11 +4180,17 @@ function addValueInArray(arrayControl, createControl, equals, isEmpty, value, op
|
|
|
4180
4180
|
}
|
|
4181
4181
|
else {
|
|
4182
4182
|
const control = createControl(value);
|
|
4183
|
-
|
|
4183
|
+
// Apply parent disabled state, before to push it into the array
|
|
4184
|
+
// This is need to avoid parent form to be enabled
|
|
4185
|
+
if (disabled && control.enabled)
|
|
4186
|
+
control.disable({ emitEvent: false });
|
|
4187
|
+
else if (!disabled && control.disabled)
|
|
4188
|
+
control.enable({ emitEvent: false });
|
|
4189
|
+
arrayControl.push(control, options);
|
|
4184
4190
|
hasChanged = true;
|
|
4185
4191
|
}
|
|
4186
4192
|
if (hasChanged) {
|
|
4187
|
-
if (
|
|
4193
|
+
if (!options || options.emitEvent !== false) {
|
|
4188
4194
|
// Mark array control dirty
|
|
4189
4195
|
if (!isEmpty(value)) {
|
|
4190
4196
|
arrayControl.markAsDirty();
|
|
@@ -4596,11 +4602,11 @@ class AppFormArray extends UntypedFormArray {
|
|
|
4596
4602
|
this.createControl = createControl;
|
|
4597
4603
|
this.equals = equals;
|
|
4598
4604
|
this.isEmpty = isEmpty;
|
|
4599
|
-
this.options = options;
|
|
4600
|
-
this.setAllowEmptyArray(
|
|
4605
|
+
this.options = Object.assign({ allowEmptyArray: true, allowReuseControls: true }, options);
|
|
4606
|
+
this.setAllowEmptyArray(this.options.allowEmptyArray);
|
|
4601
4607
|
}
|
|
4602
4608
|
get allowEmptyArray() {
|
|
4603
|
-
return this.
|
|
4609
|
+
return this.options.allowEmptyArray;
|
|
4604
4610
|
}
|
|
4605
4611
|
set allowEmptyArray(value) {
|
|
4606
4612
|
this.setAllowEmptyArray(value);
|
|
@@ -4611,18 +4617,29 @@ class AppFormArray extends UntypedFormArray {
|
|
|
4611
4617
|
* @param options
|
|
4612
4618
|
*/
|
|
4613
4619
|
setValue(values, options) {
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4620
|
+
if (this.options.allowReuseControls === false) {
|
|
4621
|
+
const disabled = this.disabled;
|
|
4622
|
+
// Clean all
|
|
4623
|
+
this.resize(0, { emitEvent: options === null || options === void 0 ? void 0 : options.emitEvent });
|
|
4624
|
+
// Recreate each control, with a default value
|
|
4625
|
+
(values || []).forEach(value => {
|
|
4626
|
+
const control = this.createControl(value);
|
|
4627
|
+
// Apply parent disabled state, before to push it into the array
|
|
4628
|
+
// This is need to avoid parent form to be enabled, after calling AppFormArray.patchValue() (e.g. in table's row validator)
|
|
4629
|
+
if (disabled && control.enabled)
|
|
4630
|
+
control.disable({ emitEvent: false });
|
|
4631
|
+
else if (!disabled && control.disabled)
|
|
4632
|
+
control.enable({ emitEvent: false });
|
|
4633
|
+
this.push(control, options);
|
|
4634
|
+
});
|
|
4617
4635
|
}
|
|
4618
4636
|
else {
|
|
4619
|
-
this.resize((values === null || values === void 0 ? void 0 : values.length) || 0);
|
|
4637
|
+
this.resize((values === null || values === void 0 ? void 0 : values.length) || 0, { emitEvent: false });
|
|
4620
4638
|
super.setValue(values, options);
|
|
4621
4639
|
}
|
|
4622
4640
|
}
|
|
4623
4641
|
patchValue(values, options) {
|
|
4624
|
-
|
|
4625
|
-
if (((_a = this.options) === null || _a === void 0 ? void 0 : _a.resizeStrategy) === 'recreate') {
|
|
4642
|
+
if (this.options.allowReuseControls === false) {
|
|
4626
4643
|
const disabled = this.disabled;
|
|
4627
4644
|
// Clean all
|
|
4628
4645
|
this.resize(0, { emitEvent: options === null || options === void 0 ? void 0 : options.emitEvent });
|
|
@@ -4682,14 +4699,13 @@ class AppFormArray extends UntypedFormArray {
|
|
|
4682
4699
|
/**
|
|
4683
4700
|
* @param value
|
|
4684
4701
|
* @param options
|
|
4685
|
-
* @deprecated Use push() instead
|
|
4686
4702
|
*/
|
|
4687
4703
|
add(value, options) {
|
|
4688
4704
|
addValueInArray(this, this.createControl, this.equals, this.isEmpty, value, options);
|
|
4689
4705
|
}
|
|
4690
4706
|
removeAt(index, options) {
|
|
4691
4707
|
// Do not remove if last criterion
|
|
4692
|
-
if (
|
|
4708
|
+
if (this.options.allowEmptyArray === false && this.length === 1) {
|
|
4693
4709
|
this.clearAt(index, options);
|
|
4694
4710
|
}
|
|
4695
4711
|
else {
|
|
@@ -4724,11 +4740,11 @@ class AppFormArray extends UntypedFormArray {
|
|
|
4724
4740
|
/* -- internal -- */
|
|
4725
4741
|
setAllowEmptyArray(value) {
|
|
4726
4742
|
var _a;
|
|
4727
|
-
if (this.
|
|
4743
|
+
if (this.options.allowEmptyArray === value)
|
|
4728
4744
|
return; // Skip if same
|
|
4729
|
-
this.
|
|
4745
|
+
this.options.allowEmptyArray = value;
|
|
4730
4746
|
// Set required (or reste) min length validator
|
|
4731
|
-
if (this.
|
|
4747
|
+
if (this.options.allowEmptyArray) {
|
|
4732
4748
|
this.setValidators(this.options.validators || null);
|
|
4733
4749
|
}
|
|
4734
4750
|
else {
|
|
@@ -24373,10 +24389,10 @@ class ActionsColumnComponent {
|
|
|
24373
24389
|
}
|
|
24374
24390
|
}
|
|
24375
24391
|
ActionsColumnComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.8", ngImport: i0, type: ActionsColumnComponent, deps: [{ token: i1$6.MatTable }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
24376
|
-
ActionsColumnComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.8", type: ActionsColumnComponent, selector: "app-actions-column", inputs: { stickyEnd: "stickyEnd", canCancel: "canCancel", canDelete: "canDelete", canBackward: "canBackward", canForward: "canForward", canConfirmAndAdd: "canConfirmAndAdd", dirtyIcon: "dirtyIcon", optionsTitle: "optionsTitle", classList: ["class", "classList"], cellTemplate: "cellTemplate" }, outputs: { optionsClick: "optionsClick", cancelOrDeleteClick: "cancelOrDeleteClick", confirmAndAddClick: "confirmAndAddClick", backward: "backward", forward: "forward" }, viewQueries: [{ propertyName: "columnDef", first: true, predicate: MatColumnDef, descendants: true }, { propertyName: "matMenuTrigger", first: true, predicate: MatMenuTrigger, descendants: true }], ngImport: i0, template: "<ng-container matColumnDef=\"actions\" [stickyEnd]=\"stickyEnd\">\n <th mat-header-cell *matHeaderCellDef [class]=\"classList\" [class.mat-column-sticky]=\"stickyEnd\">\n <button mat-icon-button\n *ngIf=\"optionsClick.observers | isNotEmptyArray\"\n [title]=\"optionsTitle | translate\"\n (click)=\"optionsClick.emit($event)\">\n <mat-icon>more_vert</mat-icon>\n </button>\n <ng-content select=\"[matHeader]\"></ng-content>\n </th>\n <td mat-cell *matCellDef=\"let row\" [class]=\"classList\">\n <ng-container *ngIf=\"row.editing; else view\">\n\n <!-- pending -->\n <ion-spinner *ngIf=\"row.validator?.pending\" name=\"dots\" class=\"center\"></ion-spinner>\n\n <!-- new row -->\n <ng-container *ngIf=\"row.id === -1; else existingRow\">\n\n <!-- delete button -->\n <button mat-icon-button color=\"light\"\n *ngIf=\"canDelete && row.validator.invalid\"\n [title]=\"'COMMON.BTN_DELETE' | translate\"\n (click)=\"cancelOrDeleteClick.emit({event: $event, row: row})\">\n <mat-icon>delete_outline</mat-icon>\n </button>\n\n <!-- add button -->\n <button mat-icon-button color=\"light\"\n *ngIf=\"!canForward && canConfirmAndAdd && row.validator.valid\"\n [title]=\"'COMMON.BTN_ADD'|translate\"\n (click)=\"confirmAndAddClick.emit({event: $event, row: row})\">\n <mat-icon>add</mat-icon>\n </button>\n\n <!-- next button (invisible - focusable only) -->\n <button style=\"color: transparent;
|
|
24392
|
+
ActionsColumnComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.8", type: ActionsColumnComponent, selector: "app-actions-column", inputs: { stickyEnd: "stickyEnd", canCancel: "canCancel", canDelete: "canDelete", canBackward: "canBackward", canForward: "canForward", canConfirmAndAdd: "canConfirmAndAdd", dirtyIcon: "dirtyIcon", optionsTitle: "optionsTitle", classList: ["class", "classList"], cellTemplate: "cellTemplate" }, outputs: { optionsClick: "optionsClick", cancelOrDeleteClick: "cancelOrDeleteClick", confirmAndAddClick: "confirmAndAddClick", backward: "backward", forward: "forward" }, viewQueries: [{ propertyName: "columnDef", first: true, predicate: MatColumnDef, descendants: true }, { propertyName: "matMenuTrigger", first: true, predicate: MatMenuTrigger, descendants: true }], ngImport: i0, template: "<ng-container matColumnDef=\"actions\" [stickyEnd]=\"stickyEnd\">\n <th mat-header-cell *matHeaderCellDef [class]=\"classList\" [class.mat-column-sticky]=\"stickyEnd\">\n <button mat-icon-button\n *ngIf=\"optionsClick.observers | isNotEmptyArray\"\n [title]=\"optionsTitle | translate\"\n (click)=\"optionsClick.emit($event)\">\n <mat-icon>more_vert</mat-icon>\n </button>\n <ng-content select=\"[matHeader]\"></ng-content>\n </th>\n <td mat-cell *matCellDef=\"let row\" [class]=\"classList\">\n <ng-container *ngIf=\"row.editing; else view\">\n\n <!-- pending -->\n <ion-spinner *ngIf=\"row.validator?.pending\" name=\"dots\" class=\"center\"></ion-spinner>\n\n <!-- new row -->\n <ng-container *ngIf=\"row.id === -1; else existingRow\">\n\n <!-- delete button -->\n <button mat-icon-button color=\"light\"\n *ngIf=\"canDelete && row.validator.invalid\"\n [title]=\"'COMMON.BTN_DELETE' | translate\"\n (click)=\"cancelOrDeleteClick.emit({event: $event, row: row})\">\n <mat-icon>delete_outline</mat-icon>\n </button>\n\n <!-- add button -->\n <button mat-icon-button color=\"light\"\n *ngIf=\"!canForward && canConfirmAndAdd && row.validator.valid\"\n [title]=\"'COMMON.BTN_ADD'|translate\"\n (click)=\"confirmAndAddClick.emit({event: $event, row: row})\">\n <mat-icon>add</mat-icon>\n </button>\n\n <!-- next button (invisible - focusable only) -->\n <button class=\"mat-row-action-button\"\n style=\"color: transparent; position: absolute; top: 0; margin: 0; padding: 0; width: 0;\"\n *ngIf=\"canForward && row.validator.valid\"\n (focus)=\"this.forward.emit({event: $event, row: row})\">\n </button>\n </ng-container>\n\n <!-- existing row -->\n <ng-template #existingRow>\n <!-- cancel button -->\n <button mat-icon-button color=\"light\"\n *ngIf=\"canCancel && row.validator.dirty\"\n [title]=\"'COMMON.BTN_UNDO' | translate\"\n (click)=\"cancelOrDeleteClick.emit({event: $event, row: row})\">\n <mat-icon>undo</mat-icon>\n </button>\n\n <!-- next button (invisible - focusable only) -->\n <button class=\"mat-row-action-button\"\n style=\"color: transparent; position: absolute; top: 0; margin: 0; padding: 0; width: 0;\"\n *ngIf=\"canForward && row.validator.valid\"\n (focus)=\"this.forward.emit({event: $event, row: row})\">\n </button>\n </ng-template>\n\n </ng-container>\n\n <ng-template #view>\n <!-- dirty icon -->\n <ion-icon *ngIf=\"dirtyIcon && row.validator?.dirty\" [name]=\"dirtyIcon\" color=\"accent\" class=\"dirty-icon\"></ion-icon>\n\n <!-- backward button (invisible - focusable only) -->\n <button mat-icon-button class=\"mat-row-action-button\"\n style=\"color: transparent; position: absolute; top: 0; margin: 0; padding: 0; width: 0;\"\n *ngIf=\"canBackward\"\n (focus)=\"this.backward.emit({event: $event, row: row})\">\n </button>\n </ng-template>\n\n <ng-container *ngTemplateOutlet=\"cellTemplate; context: { $implicit: row }\"></ng-container>\n </td>\n\n <td mat-footer-cell *matFooterCellDef>\n <ng-content select=\"[matFooter]\"></ng-content>\n </td>\n</ng-container>\n", dependencies: [{ kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2.IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "directive", type: i1$6.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i1$6.MatColumnDef, selector: "[matColumnDef]", inputs: ["sticky", "matColumnDef"] }, { kind: "directive", type: i1$6.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i1$6.MatFooterCellDef, selector: "[matFooterCellDef]" }, { kind: "directive", type: i1$6.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i1$6.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "directive", type: i1$6.MatFooterCell, selector: "mat-footer-cell, td[mat-footer-cell]" }, { kind: "component", type: i8.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }, { kind: "pipe", type: NotEmptyArrayPipe, name: "isNotEmptyArray" }] });
|
|
24377
24393
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.8", ngImport: i0, type: ActionsColumnComponent, decorators: [{
|
|
24378
24394
|
type: Component,
|
|
24379
|
-
args: [{ selector: 'app-actions-column', template: "<ng-container matColumnDef=\"actions\" [stickyEnd]=\"stickyEnd\">\n <th mat-header-cell *matHeaderCellDef [class]=\"classList\" [class.mat-column-sticky]=\"stickyEnd\">\n <button mat-icon-button\n *ngIf=\"optionsClick.observers | isNotEmptyArray\"\n [title]=\"optionsTitle | translate\"\n (click)=\"optionsClick.emit($event)\">\n <mat-icon>more_vert</mat-icon>\n </button>\n <ng-content select=\"[matHeader]\"></ng-content>\n </th>\n <td mat-cell *matCellDef=\"let row\" [class]=\"classList\">\n <ng-container *ngIf=\"row.editing; else view\">\n\n <!-- pending -->\n <ion-spinner *ngIf=\"row.validator?.pending\" name=\"dots\" class=\"center\"></ion-spinner>\n\n <!-- new row -->\n <ng-container *ngIf=\"row.id === -1; else existingRow\">\n\n <!-- delete button -->\n <button mat-icon-button color=\"light\"\n *ngIf=\"canDelete && row.validator.invalid\"\n [title]=\"'COMMON.BTN_DELETE' | translate\"\n (click)=\"cancelOrDeleteClick.emit({event: $event, row: row})\">\n <mat-icon>delete_outline</mat-icon>\n </button>\n\n <!-- add button -->\n <button mat-icon-button color=\"light\"\n *ngIf=\"!canForward && canConfirmAndAdd && row.validator.valid\"\n [title]=\"'COMMON.BTN_ADD'|translate\"\n (click)=\"confirmAndAddClick.emit({event: $event, row: row})\">\n <mat-icon>add</mat-icon>\n </button>\n\n <!-- next button (invisible - focusable only) -->\n <button style=\"color: transparent;
|
|
24395
|
+
args: [{ selector: 'app-actions-column', template: "<ng-container matColumnDef=\"actions\" [stickyEnd]=\"stickyEnd\">\n <th mat-header-cell *matHeaderCellDef [class]=\"classList\" [class.mat-column-sticky]=\"stickyEnd\">\n <button mat-icon-button\n *ngIf=\"optionsClick.observers | isNotEmptyArray\"\n [title]=\"optionsTitle | translate\"\n (click)=\"optionsClick.emit($event)\">\n <mat-icon>more_vert</mat-icon>\n </button>\n <ng-content select=\"[matHeader]\"></ng-content>\n </th>\n <td mat-cell *matCellDef=\"let row\" [class]=\"classList\">\n <ng-container *ngIf=\"row.editing; else view\">\n\n <!-- pending -->\n <ion-spinner *ngIf=\"row.validator?.pending\" name=\"dots\" class=\"center\"></ion-spinner>\n\n <!-- new row -->\n <ng-container *ngIf=\"row.id === -1; else existingRow\">\n\n <!-- delete button -->\n <button mat-icon-button color=\"light\"\n *ngIf=\"canDelete && row.validator.invalid\"\n [title]=\"'COMMON.BTN_DELETE' | translate\"\n (click)=\"cancelOrDeleteClick.emit({event: $event, row: row})\">\n <mat-icon>delete_outline</mat-icon>\n </button>\n\n <!-- add button -->\n <button mat-icon-button color=\"light\"\n *ngIf=\"!canForward && canConfirmAndAdd && row.validator.valid\"\n [title]=\"'COMMON.BTN_ADD'|translate\"\n (click)=\"confirmAndAddClick.emit({event: $event, row: row})\">\n <mat-icon>add</mat-icon>\n </button>\n\n <!-- next button (invisible - focusable only) -->\n <button class=\"mat-row-action-button\"\n style=\"color: transparent; position: absolute; top: 0; margin: 0; padding: 0; width: 0;\"\n *ngIf=\"canForward && row.validator.valid\"\n (focus)=\"this.forward.emit({event: $event, row: row})\">\n </button>\n </ng-container>\n\n <!-- existing row -->\n <ng-template #existingRow>\n <!-- cancel button -->\n <button mat-icon-button color=\"light\"\n *ngIf=\"canCancel && row.validator.dirty\"\n [title]=\"'COMMON.BTN_UNDO' | translate\"\n (click)=\"cancelOrDeleteClick.emit({event: $event, row: row})\">\n <mat-icon>undo</mat-icon>\n </button>\n\n <!-- next button (invisible - focusable only) -->\n <button class=\"mat-row-action-button\"\n style=\"color: transparent; position: absolute; top: 0; margin: 0; padding: 0; width: 0;\"\n *ngIf=\"canForward && row.validator.valid\"\n (focus)=\"this.forward.emit({event: $event, row: row})\">\n </button>\n </ng-template>\n\n </ng-container>\n\n <ng-template #view>\n <!-- dirty icon -->\n <ion-icon *ngIf=\"dirtyIcon && row.validator?.dirty\" [name]=\"dirtyIcon\" color=\"accent\" class=\"dirty-icon\"></ion-icon>\n\n <!-- backward button (invisible - focusable only) -->\n <button mat-icon-button class=\"mat-row-action-button\"\n style=\"color: transparent; position: absolute; top: 0; margin: 0; padding: 0; width: 0;\"\n *ngIf=\"canBackward\"\n (focus)=\"this.backward.emit({event: $event, row: row})\">\n </button>\n </ng-template>\n\n <ng-container *ngTemplateOutlet=\"cellTemplate; context: { $implicit: row }\"></ng-container>\n </td>\n\n <td mat-footer-cell *matFooterCellDef>\n <ng-content select=\"[matFooter]\"></ng-content>\n </td>\n</ng-container>\n" }]
|
|
24380
24396
|
}], ctorParameters: function () { return [{ type: i1$6.MatTable }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { columnDef: [{
|
|
24381
24397
|
type: ViewChild,
|
|
24382
24398
|
args: [MatColumnDef]
|
|
@@ -29403,7 +29419,7 @@ class AbstractUserEventService extends BaseGraphqlService {
|
|
|
29403
29419
|
ngOnStart() {
|
|
29404
29420
|
return __awaiter(this, void 0, void 0, function* () {
|
|
29405
29421
|
// Update component when refresh is need (=login events)
|
|
29406
|
-
this._subscriptions.add(merge(this.accountService.onLogin, this.accountService.onLogout,
|
|
29422
|
+
this._subscriptions.add(merge(this.accountService.onLogin, this.accountService.onLogout, timer(1000) // Starts with - first attempt if account is ready
|
|
29407
29423
|
)
|
|
29408
29424
|
.pipe(
|
|
29409
29425
|
// Wait account service ready (can be restarted)
|
|
@@ -29773,6 +29789,9 @@ class AbstractUserEventService extends BaseGraphqlService {
|
|
|
29773
29789
|
});
|
|
29774
29790
|
}
|
|
29775
29791
|
onLogout() {
|
|
29792
|
+
// Reset counter
|
|
29793
|
+
this._countSubject.next(0);
|
|
29794
|
+
// Stop listening
|
|
29776
29795
|
this.stopListenCountChanges();
|
|
29777
29796
|
}
|
|
29778
29797
|
startListenCountChanges() {
|