@idea-ionic/uncommon 8.11.5 → 8.11.6

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.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { output, input, viewChild, Input, ChangeDetectionStrategy, Component, inject, Injectable, ChangeDetectorRef } from '@angular/core';
2
+ import { output, input, viewChild, Input, ChangeDetectionStrategy, Component, inject, Injectable, signal, model } from '@angular/core';
3
3
  import * as i1 from '@angular/forms';
4
4
  import { FormsModule } from '@angular/forms';
5
5
  import { IonText, IonList, IonInput, IonItem, IonAccordion, IonAccordionGroup, Platform, IonButton, IonIcon, IonLabel, IonSpinner, IonCardContent, IonCard, PopoverController, IonAvatar, IonContent, IonGrid, IonRow, IonCol, AlertController, ModalController, IonFooter, IonButtons, IonTitle, IonToolbar, IonHeader, IonCardTitle, IonCardHeader, IonImg, IonTextarea, IonListHeader, IonFab, IonFabButton, IonBadge, IonSegmentButton, IonSegment } from '@ionic/angular/standalone';
@@ -535,17 +535,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
535
535
  */
536
536
  class IDEAOfflineService {
537
537
  constructor() {
538
- // get an instant status
539
- this._isOffline = !navigator.onLine;
538
+ /**
539
+ * True/false if the platform is offline/online.
540
+ */
541
+ this._isOffline = signal(typeof navigator === 'undefined' ? false : !navigator.onLine, /* @ts-ignore */
542
+ ...(ngDevMode ? [{ debugName: "_isOffline" }] : /* istanbul ignore next */ []));
540
543
  if (Network) {
541
544
  // set the basic listener for changes
542
- this.listenerHandler = Network.addListener('networkStatusChange', status => (this._isOffline = !status.connected));
545
+ this.listenerHandler = Network.addListener('networkStatusChange', status => this._isOffline.set(!status.connected));
543
546
  // create the observable to subscribe to the network changes; note: it won't run until subscribed
544
547
  this.observable = new Observable(observer => {
545
548
  // remove the basic listener add add one that supports a subscription
546
549
  Network.removeAllListeners();
547
550
  this.listenerHandler = Network.addListener('networkStatusChange', status => {
548
- this._isOffline = !status.connected;
551
+ this._isOffline.set(!status.connected);
549
552
  if (observer)
550
553
  observer.next(status.connected);
551
554
  });
@@ -562,13 +565,13 @@ class IDEAOfflineService {
562
565
  * Quickly check the connection status.
563
566
  */
564
567
  isOnline() {
565
- return !this._isOffline;
568
+ return !this._isOffline();
566
569
  }
567
570
  /**
568
571
  * Quickly check the connection status.
569
572
  */
570
573
  isOffline() {
571
- return this._isOffline;
574
+ return this._isOffline();
572
575
  }
573
576
  /**
574
577
  * Quickly check for online connection.
@@ -577,7 +580,7 @@ class IDEAOfflineService {
577
580
  if (!Network)
578
581
  return false;
579
582
  const status = await Network.getStatus();
580
- this._isOffline = !status.connected;
583
+ this._isOffline.set(!status.connected);
581
584
  return status.connected;
582
585
  }
583
586
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: IDEAOfflineService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
@@ -1017,14 +1020,11 @@ class IDEAOldAttachmentsComponent {
1017
1020
  this._message = inject(IDEAMessageService);
1018
1021
  this._tc = inject(IDEATinCanService);
1019
1022
  this._api = inject(IDEAAWSAPIService);
1020
- this._cd = inject(ChangeDetectorRef);
1021
1023
  this._offline = inject(IDEAOfflineService);
1022
1024
  this._translate = inject(IDEATranslationsService);
1023
1025
  /**
1024
1026
  * The team from which we want to load the resources. Default: try to guess current team.
1025
1027
  */
1026
- // TODO: Skipped for migration because:
1027
- // Your application code writes to the input. This prevents migration.
1028
1028
  this.team = null;
1029
1029
  /**
1030
1030
  * The path to the online API resource, as an array. Don't include the team. E.g. `['entities', entityId]`.
@@ -1034,12 +1034,11 @@ class IDEAOldAttachmentsComponent {
1034
1034
  /**
1035
1035
  * The array in which we want to add/remove attachments.
1036
1036
  */
1037
- this.attachments = input(null, /* @ts-ignore */
1037
+ this.attachments = model(null, /* @ts-ignore */
1038
1038
  ...(ngDevMode ? [{ debugName: "attachments" }] : /* istanbul ignore next */ []));
1039
1039
  /**
1040
1040
  * Regulate the mode (view/edit).
1041
1041
  */
1042
- // TODO: Skipped for migration because: This input is used in a control flow expression (e.g. `@if` or `*ngIf`) and migrating would break narrowing currently.
1043
1042
  this.editMode = false;
1044
1043
  /**
1045
1044
  * Show errors as reported from the parent component.
@@ -1054,7 +1053,8 @@ class IDEAOldAttachmentsComponent {
1054
1053
  /**
1055
1054
  * Stack of errors from the last upload.
1056
1055
  */
1057
- this.uploadErrors = [];
1056
+ this.uploadErrors = signal([], /* @ts-ignore */
1057
+ ...(ngDevMode ? [{ debugName: "uploadErrors" }] : /* istanbul ignore next */ []));
1058
1058
  }
1059
1059
  ngOnInit() {
1060
1060
  // if the team isn't specified, try to guess it in the usual IDEA's paths
@@ -1074,7 +1074,7 @@ class IDEAOldAttachmentsComponent {
1074
1074
  document.getElementById('attachmentPicker').click();
1075
1075
  }
1076
1076
  addAttachmentFromFile(ev) {
1077
- this.uploadErrors = new Array();
1077
+ this.uploadErrors.set(new Array());
1078
1078
  const files = ev.target ? ev.target.files : {};
1079
1079
  for (let i = 0; i < files.length; i++) {
1080
1080
  const file = files.item(i);
@@ -1112,27 +1112,27 @@ class IDEAOldAttachmentsComponent {
1112
1112
  content = await heic2any({ blob: content, toType: 'image/jpeg' });
1113
1113
  }
1114
1114
  const attachment = new Attachment({ name, format });
1115
- this.attachments().push(attachment);
1115
+ this.attachments.update(list => [...(list ?? []), attachment]);
1116
1116
  try {
1117
1117
  const signedURL = await this._api.patchResource(this.requestURL, {
1118
1118
  body: { action: 'ATTACHMENTS_PUT', attachmentId: attachment.attachmentId }
1119
1119
  });
1120
1120
  await this._api.rawRequest().put(signedURL.url, content).toPromise();
1121
1121
  attachment.attachmentId = signedURL.id;
1122
+ // set a fresh array reference so the upload result (attachmentId) re-renders
1123
+ this.attachments.update(list => (list ? [...list] : list));
1122
1124
  }
1123
1125
  catch (error) {
1124
- this.uploadErrors.push(name);
1126
+ this.uploadErrors.update(errors => [...errors, name]);
1125
1127
  this.removeAttachment(attachment);
1126
1128
  this._message.error('IDEA_TEAMS.ATTACHMENTS.ERROR_UPLOADING_ATTACHMENT');
1127
1129
  }
1128
- finally {
1129
- this._cd.markForCheck(); // zoneless: re-check after the awaited upload settles
1130
- }
1130
+ }
1131
+ hideUploadError(err) {
1132
+ this.uploadErrors.update(errors => errors.filter(e => e !== err));
1131
1133
  }
1132
1134
  removeAttachment(attachment) {
1133
- const index = this.attachments().indexOf(attachment);
1134
- if (index !== -1)
1135
- this.attachments().splice(index, 1);
1135
+ this.attachments.update(list => (list ? list.filter(att => att !== attachment) : list));
1136
1136
  }
1137
1137
  async openAttachment(attachment) {
1138
1138
  try {
@@ -1162,7 +1162,7 @@ class IDEAOldAttachmentsComponent {
1162
1162
  }
1163
1163
  }
1164
1164
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: IDEAOldAttachmentsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1165
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: IDEAOldAttachmentsComponent, isStandalone: true, selector: "idea-old-attachments", inputs: { team: { classPropertyName: "team", publicName: "team", isSignal: false, isRequired: false, transformFunction: null }, pathResource: { classPropertyName: "pathResource", publicName: "pathResource", isSignal: true, isRequired: false, transformFunction: null }, attachments: { classPropertyName: "attachments", publicName: "attachments", isSignal: true, isRequired: false, transformFunction: null }, editMode: { classPropertyName: "editMode", publicName: "editMode", isSignal: false, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, lines: { classPropertyName: "lines", publicName: "lines", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
1165
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: IDEAOldAttachmentsComponent, isStandalone: true, selector: "idea-old-attachments", inputs: { team: { classPropertyName: "team", publicName: "team", isSignal: false, isRequired: false, transformFunction: null }, pathResource: { classPropertyName: "pathResource", publicName: "pathResource", isSignal: true, isRequired: false, transformFunction: null }, attachments: { classPropertyName: "attachments", publicName: "attachments", isSignal: true, isRequired: false, transformFunction: null }, editMode: { classPropertyName: "editMode", publicName: "editMode", isSignal: false, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, lines: { classPropertyName: "lines", publicName: "lines", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { attachments: "attachmentsChange" }, ngImport: i0, template: `
1166
1166
  @for (att of attachments(); track att; let index = $index; let odd = $odd) {
1167
1167
  <ion-item
1168
1168
  class="attachments"
@@ -1219,7 +1219,7 @@ class IDEAOldAttachmentsComponent {
1219
1219
  <!----->
1220
1220
  @if (editMode) {
1221
1221
  <div>
1222
- @for (err of uploadErrors; track err) {
1222
+ @for (err of uploadErrors(); track err) {
1223
1223
  <ion-item class="attachments" [lines]="lines()">
1224
1224
  <ion-icon name="alert-circle" slot="start" color="danger" />
1225
1225
  <ion-label color="danger">
@@ -1231,7 +1231,7 @@ class IDEAOldAttachmentsComponent {
1231
1231
  color="danger"
1232
1232
  fill="clear"
1233
1233
  [title]="'IDEA_TEAMS.ATTACHMENTS.HIDE_ERROR' | translate"
1234
- (click)="uploadErrors.splice(uploadErrors.indexOf(err), 1)"
1234
+ (click)="hideUploadError(err)"
1235
1235
  >
1236
1236
  <ion-icon name="close" slot="icon-only" />
1237
1237
  </ion-button>
@@ -1321,7 +1321,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
1321
1321
  <!----->
1322
1322
  @if (editMode) {
1323
1323
  <div>
1324
- @for (err of uploadErrors; track err) {
1324
+ @for (err of uploadErrors(); track err) {
1325
1325
  <ion-item class="attachments" [lines]="lines()">
1326
1326
  <ion-icon name="alert-circle" slot="start" color="danger" />
1327
1327
  <ion-label color="danger">
@@ -1333,7 +1333,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
1333
1333
  color="danger"
1334
1334
  fill="clear"
1335
1335
  [title]="'IDEA_TEAMS.ATTACHMENTS.HIDE_ERROR' | translate"
1336
- (click)="uploadErrors.splice(uploadErrors.indexOf(err), 1)"
1336
+ (click)="hideUploadError(err)"
1337
1337
  >
1338
1338
  <ion-icon name="close" slot="icon-only" />
1339
1339
  </ion-button>
@@ -1365,7 +1365,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
1365
1365
  `, styles: [".attachments ion-button[slot=start],.attachments ion-icon[slot=start]{margin-right:10px}.attachments .loadingWarning{font-style:italic;font-size:.8em;color:var(--ion-color-medium)}.noAttachments{font-style:italic}.selectIcon{margin:0;padding-left:4px;font-size:.8em;color:var(--ion-color-medium)}.selectable{cursor:pointer}\n"] }]
1366
1366
  }], propDecorators: { team: [{
1367
1367
  type: Input
1368
- }], pathResource: [{ type: i0.Input, args: [{ isSignal: true, alias: "pathResource", required: false }] }], attachments: [{ type: i0.Input, args: [{ isSignal: true, alias: "attachments", required: false }] }], editMode: [{
1368
+ }], pathResource: [{ type: i0.Input, args: [{ isSignal: true, alias: "pathResource", required: false }] }], attachments: [{ type: i0.Input, args: [{ isSignal: true, alias: "attachments", required: false }] }, { type: i0.Output, args: ["attachmentsChange"] }], editMode: [{
1369
1369
  type: Input
1370
1370
  }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], lines: [{ type: i0.Input, args: [{ isSignal: true, alias: "lines", required: false }] }] } });
1371
1371
  /**
@@ -1388,9 +1388,14 @@ class IDEAAnnouncementComponent {
1388
1388
  this._env = inject(IDEAEnvironment);
1389
1389
  this._tc = inject(IDEATinCanService);
1390
1390
  this._storage = inject(IDEAStorageService);
1391
- this._cdr = inject(ChangeDetectorRef);
1391
+ /**
1392
+ * The color for the announcement card.
1393
+ */
1394
+ this.color = model('dark', /* @ts-ignore */
1395
+ ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
1396
+ this.htmlContent = signal(null, /* @ts-ignore */
1397
+ ...(ngDevMode ? [{ debugName: "htmlContent" }] : /* istanbul ignore next */ []));
1392
1398
  this.storageKey = (this._env.idea.project || 'app').concat('_LAST_ANNOUNCEMENT');
1393
- this.color = 'dark';
1394
1399
  }
1395
1400
  async ngOnInit() {
1396
1401
  // standard IDEA: the announcement is read in the Init Guard
@@ -1398,21 +1403,19 @@ class IDEAAnnouncementComponent {
1398
1403
  if (this.announcement && this.announcement.content) {
1399
1404
  const lastRead = await this._storage.get(this.storageKey);
1400
1405
  if (!lastRead || this.announcement.content !== lastRead)
1401
- this.htmlContent = mdToHtml(this.announcement.content);
1402
- this._cdr.markForCheck();
1406
+ this.htmlContent.set(mdToHtml(this.announcement.content));
1403
1407
  }
1404
1408
  }
1405
1409
  async markAsRead() {
1406
1410
  await this._storage.set(this.storageKey, this.announcement.content);
1407
- this.htmlContent = null;
1408
- this._cdr.markForCheck();
1411
+ this.htmlContent.set(null);
1409
1412
  }
1410
1413
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: IDEAAnnouncementComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1411
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: IDEAAnnouncementComponent, isStandalone: true, selector: "idea-announcement", inputs: { color: "color" }, ngImport: i0, template: `
1412
- @if (htmlContent) {
1413
- <ion-card class="announcementCard" [color]="color">
1414
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: IDEAAnnouncementComponent, isStandalone: true, selector: "idea-announcement", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { color: "colorChange" }, ngImport: i0, template: `
1415
+ @if (htmlContent()) {
1416
+ <ion-card class="announcementCard" [color]="color()">
1414
1417
  <ion-card-content>
1415
- <p class="htmlContent" [innerHTML]="htmlContent"></p>
1418
+ <p class="htmlContent" [innerHTML]="htmlContent()"></p>
1416
1419
  <p class="ion-text-right">
1417
1420
  <ion-button color="primary" (click)="markAsRead()">
1418
1421
  {{ 'COMMON.GOT_IT' | translate }}
@@ -1430,10 +1433,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
1430
1433
  imports: [IDEATranslatePipe, IonButton, IonCardContent, IonCard],
1431
1434
  changeDetection: ChangeDetectionStrategy.OnPush,
1432
1435
  template: `
1433
- @if (htmlContent) {
1434
- <ion-card class="announcementCard" [color]="color">
1436
+ @if (htmlContent()) {
1437
+ <ion-card class="announcementCard" [color]="color()">
1435
1438
  <ion-card-content>
1436
- <p class="htmlContent" [innerHTML]="htmlContent"></p>
1439
+ <p class="htmlContent" [innerHTML]="htmlContent()"></p>
1437
1440
  <p class="ion-text-right">
1438
1441
  <ion-button color="primary" (click)="markAsRead()">
1439
1442
  {{ 'COMMON.GOT_IT' | translate }}
@@ -1444,9 +1447,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
1444
1447
  }
1445
1448
  `
1446
1449
  }]
1447
- }], ctorParameters: () => [], propDecorators: { color: [{
1448
- type: Input
1449
- }] } });
1450
+ }], ctorParameters: () => [], propDecorators: { color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }, { type: i0.Output, args: ["colorChange"] }] } });
1450
1451
 
1451
1452
  /**
1452
1453
  * Pick a color from a defined set.
@@ -1853,12 +1854,16 @@ class IDEACalendarPickerComponent {
1853
1854
  this.timePicker = false;
1854
1855
  this.manualTimePicker = false;
1855
1856
  this.hideClearButton = false;
1857
+ this.refDate = signal(undefined, /* @ts-ignore */
1858
+ ...(ngDevMode ? [{ debugName: "refDate" }] : /* istanbul ignore next */ []));
1859
+ this.calendarGrid = signal(undefined, /* @ts-ignore */
1860
+ ...(ngDevMode ? [{ debugName: "calendarGrid" }] : /* istanbul ignore next */ []));
1856
1861
  }
1857
1862
  ngOnInit() {
1858
1863
  this.today = new Date();
1859
1864
  const inputDate = this.inputDate;
1860
- this.refDate = inputDate ? new Date(inputDate) : new Date(this.today);
1861
- this.selectedDate = new Date(this.refDate);
1865
+ this.refDate.set(inputDate ? new Date(inputDate) : new Date(this.today));
1866
+ this.selectedDate = new Date(this.refDate());
1862
1867
  this.hour = 12; // to endure timezones
1863
1868
  this.minute = 0;
1864
1869
  const manualTimePicker = this.manualTimePicker;
@@ -1872,7 +1877,7 @@ class IDEACalendarPickerComponent {
1872
1877
  this.selectedDate.setMinutes(this.minute);
1873
1878
  }
1874
1879
  }
1875
- this.buildCalendarGrid(this.refDate);
1880
+ this.buildCalendarGrid(this.refDate());
1876
1881
  this.hours = Array.from(Array(24).keys()).map(i => '0'.concat(i.toString()).slice(-2));
1877
1882
  this.minutes = Array.from(Array(12).keys()).map(i => '0'.concat((i * 5).toString()).slice(-2));
1878
1883
  // build the weekdays based on the current locale
@@ -1907,21 +1912,21 @@ class IDEACalendarPickerComponent {
1907
1912
  let haventFoundFirstDay = true;
1908
1913
  // index used to build the dates of the month, starting from the first one
1909
1914
  let index = 1;
1910
- this.calendarGrid = new Array();
1915
+ const calendarGrid = new Array();
1911
1916
  for (let i = 0; i < 6; i++) {
1912
- this.calendarGrid[i] = new Array();
1917
+ calendarGrid[i] = new Array();
1913
1918
  for (let j = 0; j < 7; j++) {
1914
1919
  if (haventFoundFirstDay) {
1915
1920
  if (this.getLocalisedDay(firstDateOfMonth) === j) {
1916
1921
  // note: considers Sunday
1917
1922
  haventFoundFirstDay = false;
1918
- this.calendarGrid[i][j] = new Date(firstDateOfMonth);
1923
+ calendarGrid[i][j] = new Date(firstDateOfMonth);
1919
1924
  // now the I've found the first date of the month I can fill the calendar
1920
1925
  // the dates fromt the previous month, until there's space in the grid
1921
1926
  for (let y = this.getLocalisedDay(firstDateOfMonth); y >= 0; y--) {
1922
1927
  const d = new Date(firstDateOfMonth);
1923
1928
  d.setDate(firstDateOfMonth.getDate() - y);
1924
- this.calendarGrid[i][this.getLocalisedDay(firstDateOfMonth) - y] = d;
1929
+ calendarGrid[i][this.getLocalisedDay(firstDateOfMonth) - y] = d;
1925
1930
  }
1926
1931
  }
1927
1932
  }
@@ -1929,24 +1934,37 @@ class IDEACalendarPickerComponent {
1929
1934
  // fill the following dates until there's space in the grid
1930
1935
  const d = new Date(firstDateOfMonth);
1931
1936
  d.setDate(firstDateOfMonth.getDate() + index++);
1932
- this.calendarGrid[i][j] = d;
1937
+ calendarGrid[i][j] = d;
1933
1938
  }
1934
1939
  }
1935
1940
  }
1941
+ this.calendarGrid.set(calendarGrid);
1936
1942
  }
1937
1943
  addYears(offset) {
1938
- this.refDate.setFullYear(this.refDate.getFullYear() + offset);
1939
- this.buildCalendarGrid(this.refDate);
1944
+ this.refDate.update(d => {
1945
+ const refDate = new Date(d);
1946
+ refDate.setFullYear(refDate.getFullYear() + offset);
1947
+ return refDate;
1948
+ });
1949
+ this.buildCalendarGrid(this.refDate());
1940
1950
  }
1941
1951
  setYear(year) {
1942
1952
  if (!year)
1943
1953
  return;
1944
- this.refDate.setFullYear(Number(year));
1945
- this.buildCalendarGrid(this.refDate);
1954
+ this.refDate.update(d => {
1955
+ const refDate = new Date(d);
1956
+ refDate.setFullYear(Number(year));
1957
+ return refDate;
1958
+ });
1959
+ this.buildCalendarGrid(this.refDate());
1946
1960
  }
1947
1961
  addMonths(offset) {
1948
- this.refDate.setMonth(this.refDate.getMonth() + offset);
1949
- this.buildCalendarGrid(this.refDate);
1962
+ this.refDate.update(d => {
1963
+ const refDate = new Date(d);
1964
+ refDate.setMonth(refDate.getMonth() + offset);
1965
+ return refDate;
1966
+ });
1967
+ this.buildCalendarGrid(this.refDate());
1950
1968
  }
1951
1969
  async showMonths() {
1952
1970
  const buttons = [];
@@ -1957,7 +1975,7 @@ class IDEACalendarPickerComponent {
1957
1975
  type: 'radio',
1958
1976
  label: month.toLocaleDateString(this._translate.getCurrentLang(), { month: 'long' }),
1959
1977
  value: i.toString(),
1960
- checked: i === this.refDate.getMonth() + 1
1978
+ checked: i === this.refDate().getMonth() + 1
1961
1979
  });
1962
1980
  month.setMonth(month.getMonth() + 1);
1963
1981
  }
@@ -1965,8 +1983,12 @@ class IDEACalendarPickerComponent {
1965
1983
  buttons.push({
1966
1984
  text: this._translate._('COMMON.SELECT'),
1967
1985
  handler: (m) => {
1968
- this.refDate.setMonth(parseInt(m, 10) - 1);
1969
- this.buildCalendarGrid(this.refDate);
1986
+ this.refDate.update(d => {
1987
+ const refDate = new Date(d);
1988
+ refDate.setMonth(parseInt(m, 10) - 1);
1989
+ return refDate;
1990
+ });
1991
+ this.buildCalendarGrid(this.refDate());
1970
1992
  }
1971
1993
  });
1972
1994
  const alert = await this._alert.create({
@@ -2013,7 +2035,7 @@ class IDEACalendarPickerComponent {
2013
2035
  this._modal.dismiss();
2014
2036
  }
2015
2037
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: IDEACalendarPickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2016
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: IDEACalendarPickerComponent, isStandalone: true, selector: "idea-calendar-picker", inputs: { inputDate: "inputDate", timePicker: "timePicker", manualTimePicker: "manualTimePicker", title: "title", hideClearButton: "hideClearButton", min: "min", max: "max" }, ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"ideaToolbar\">\n <ion-title>{{ title }}</ion-title>\n <ion-buttons slot=\"end\">\n @if (!hideClearButton) {\n <ion-button [title]=\"'IDEA_UNCOMMON.CALENDAR.RESET' | translate\" (click)=\"save(true)\">\n <ion-icon slot=\"icon-only\" name=\"arrow-undo\" />\n </ion-button>\n }\n <ion-button [title]=\"'IDEA_UNCOMMON.CALENDAR.CLOSE_WITHOUT_SAVING' | translate\" (click)=\"close()\">\n <ion-icon slot=\"icon-only\" name=\"close\" />\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n<ion-content>\n <ion-grid fixed class=\"ion-no-padding\">\n <ion-row>\n <h1 class=\"headerLabel\">{{ 'IDEA_UNCOMMON.CALENDAR.SET_DATE' | translate }}</h1>\n </ion-row>\n <ion-row style=\"margin-bottom: 8px\">\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"5\">\n <ion-row class=\"ion-align-items-center ion-justify-content-center\">\n <ion-col class=\"ion-text-right ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addYears(-1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-back\" />\n </ion-button>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding tappable\" [size]=\"6\">\n <ion-input\n type=\"text\"\n [placeholder]=\"'IDEA_UNCOMMON.CALENDAR.YEAR' | translate\"\n [value]=\"refDate.getFullYear()\"\n (ionFocus)=\"$event.target.value = ''\"\n (ionChange)=\"setYear($event?.target?.value)\"\n >\n </ion-input>\n </ion-col>\n <ion-col class=\"ion-text-left ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addYears(+1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-forward\" />\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"7\">\n <ion-row class=\"ion-align-items-center\">\n <ion-col class=\"ion-text-right ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addMonths(-1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-back\" />\n </ion-button>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding tappable\" [size]=\"6\" (click)=\"showMonths()\">\n {{ refDate | dateLocale: 'MMMM' }}\n </ion-col>\n <ion-col class=\"ion-text-left ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addMonths(+1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-forward\" />\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-col>\n </ion-row>\n <ion-row style=\"margin-bottom: 8px\" class=\"headerLabel\">\n @for (ddd of weekDays; track ddd) {\n <ion-col class=\"ion-text-center ion-no-padding\">\n {{ ddd }}\n </ion-col>\n }\n </ion-row>\n @for (row of calendarGrid; track row; let i = $index) {\n <ion-row class=\"ion-align-items-center\">\n @for (col of row; track col; let j = $index) {\n <ion-col class=\"ion-text-center ion-no-padding\">\n <ion-button\n size=\"small\"\n shape=\"round\"\n [color]=\"\n isSameDay(calendarGrid[i][j], today) || isSameDay(calendarGrid[i][j], selectedDate)\n ? 'primary'\n : isSameMonth(calendarGrid[i][j], refDate)\n ? 'dark'\n : 'medium'\n \"\n [fill]=\"\n isSameDay(calendarGrid[i][j], selectedDate)\n ? 'solid'\n : isSameDay(calendarGrid[i][j], today)\n ? 'outline'\n : 'clear'\n \"\n [disabled]=\"!isDateSelectable(calendarGrid[i][j])\"\n (click)=\"selectDate(calendarGrid[i][j])\"\n >\n {{ calendarGrid[i][j] | date: 'd' }}\n </ion-button>\n </ion-col>\n }\n </ion-row>\n }\n @if (timePicker || manualTimePicker) {\n <ion-row>\n <h1 class=\"headerLabel\">\n {{ 'IDEA_UNCOMMON.CALENDAR.SET_TIME' | translate }}\n </h1>\n </ion-row>\n }\n @if (timePicker) {\n <ion-row class=\"ion-align-items-center\">\n <ion-col class=\"ion-no-padding\" [size]=\"7\">\n <ion-row>\n @for (hour of hours; track hour) {\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"2\">\n <ion-button\n size=\"small\"\n shape=\"round\"\n [color]=\"isSameHour(hour) ? 'primary' : 'dark'\"\n [fill]=\"isSameHour(hour) ? 'solid' : 'clear'\"\n (click)=\"selectHour(hour)\"\n >\n {{ hour }}\n </ion-button>\n </ion-col>\n }\n </ion-row>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"1\"> : </ion-col>\n <ion-col class=\"ion-no-padding\" [size]=\"4\">\n <ion-row>\n @for (minute of minutes; track minute) {\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"4\">\n <ion-button\n size=\"small\"\n shape=\"round\"\n [color]=\"isSameMinute(minute) ? 'primary' : 'dark'\"\n [fill]=\"isSameMinute(minute) ? 'solid' : 'clear'\"\n (click)=\"selectMinute(minute)\"\n >\n {{ minute }}\n </ion-button>\n </ion-col>\n }\n </ion-row>\n </ion-col>\n </ion-row>\n }\n @if (manualTimePicker) {\n <ion-row class=\"ion-align-items-center manualTimePicker\">\n <ion-col class=\"ion-text-center\" [size]=\"12\">\n <input type=\"number\" min=\"0\" max=\"23\" [value]=\"hour\" (change)=\"selectHour($event.target.value)\" />\n <span>:</span>\n <input type=\"number\" min=\"0\" max=\"59\" [value]=\"minute\" (change)=\"selectMinute($event.target.value)\" />\n </ion-col>\n </ion-row>\n }\n </ion-grid>\n</ion-content>\n<ion-footer>\n <ion-toolbar color=\"ideaToolbar\" class=\"tappable\" (click)=\"save()\">\n <ion-title class=\"ion-text-center\">\n <ion-icon name=\"checkmark\" />\n </ion-title>\n </ion-toolbar>\n</ion-footer>\n", styles: ["ion-content{--background: var(--ion-color-white)}h1{margin:10px}.headerLabel{font-size:.7em;font-weight:700;text-transform:uppercase}ion-col,ion-button{font-size:.95em}ion-button{margin:3px 0}ion-footer ion-toolbar{--min-height: 40px}.tappable{cursor:pointer}.manualTimePicker{padding:20px}.manualTimePicker input[type=number],.manualTimePicker span{font-size:2.5em}.manualTimePicker input[type=number]{border:none;border-bottom:2px solid var(--ion-color-dark);text-align:center}.manualTimePicker span{padding-left:4px;padding-right:4px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: IonFooter, selector: "ion-footer", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonInput, selector: "ion-input", inputs: ["accept", "autocapitalize", "autocomplete", "autocorrect", "autofocus", "clearInput", "clearOnEdit", "color", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "max", "maxlength", "min", "minlength", "mode", "multiple", "name", "pattern", "placeholder", "readonly", "required", "shape", "size", "spellcheck", "step", "type", "value"] }, { kind: "component", type: IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: IonRow, selector: "ion-row" }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonGrid, selector: "ion-grid", inputs: ["fixed"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "pipe", type: i1$1.DatePipe, name: "date" }, { kind: "pipe", type: IDEATranslatePipe, name: "translate" }, { kind: "pipe", type: IDEALocalizedDatePipe, name: "dateLocale" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2038
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: IDEACalendarPickerComponent, isStandalone: true, selector: "idea-calendar-picker", inputs: { inputDate: "inputDate", timePicker: "timePicker", manualTimePicker: "manualTimePicker", title: "title", hideClearButton: "hideClearButton", min: "min", max: "max" }, ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"ideaToolbar\">\n <ion-title>{{ title }}</ion-title>\n <ion-buttons slot=\"end\">\n @if (!hideClearButton) {\n <ion-button [title]=\"'IDEA_UNCOMMON.CALENDAR.RESET' | translate\" (click)=\"save(true)\">\n <ion-icon slot=\"icon-only\" name=\"arrow-undo\" />\n </ion-button>\n }\n <ion-button [title]=\"'IDEA_UNCOMMON.CALENDAR.CLOSE_WITHOUT_SAVING' | translate\" (click)=\"close()\">\n <ion-icon slot=\"icon-only\" name=\"close\" />\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n<ion-content>\n <ion-grid fixed class=\"ion-no-padding\">\n <ion-row>\n <h1 class=\"headerLabel\">{{ 'IDEA_UNCOMMON.CALENDAR.SET_DATE' | translate }}</h1>\n </ion-row>\n <ion-row style=\"margin-bottom: 8px\">\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"5\">\n <ion-row class=\"ion-align-items-center ion-justify-content-center\">\n <ion-col class=\"ion-text-right ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addYears(-1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-back\" />\n </ion-button>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding tappable\" [size]=\"6\">\n <ion-input\n type=\"text\"\n [placeholder]=\"'IDEA_UNCOMMON.CALENDAR.YEAR' | translate\"\n [value]=\"refDate().getFullYear()\"\n (ionFocus)=\"$event.target.value = ''\"\n (ionChange)=\"setYear($event?.target?.value)\"\n >\n </ion-input>\n </ion-col>\n <ion-col class=\"ion-text-left ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addYears(+1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-forward\" />\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"7\">\n <ion-row class=\"ion-align-items-center\">\n <ion-col class=\"ion-text-right ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addMonths(-1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-back\" />\n </ion-button>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding tappable\" [size]=\"6\" (click)=\"showMonths()\">\n {{ refDate() | dateLocale: 'MMMM' }}\n </ion-col>\n <ion-col class=\"ion-text-left ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addMonths(+1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-forward\" />\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-col>\n </ion-row>\n <ion-row style=\"margin-bottom: 8px\" class=\"headerLabel\">\n @for (ddd of weekDays; track ddd) {\n <ion-col class=\"ion-text-center ion-no-padding\">\n {{ ddd }}\n </ion-col>\n }\n </ion-row>\n @for (row of calendarGrid(); track row; let i = $index) {\n <ion-row class=\"ion-align-items-center\">\n @for (col of row; track col; let j = $index) {\n <ion-col class=\"ion-text-center ion-no-padding\">\n <ion-button\n size=\"small\"\n shape=\"round\"\n [color]=\"\n isSameDay(calendarGrid()[i][j], today) || isSameDay(calendarGrid()[i][j], selectedDate)\n ? 'primary'\n : isSameMonth(calendarGrid()[i][j], refDate())\n ? 'dark'\n : 'medium'\n \"\n [fill]=\"\n isSameDay(calendarGrid()[i][j], selectedDate)\n ? 'solid'\n : isSameDay(calendarGrid()[i][j], today)\n ? 'outline'\n : 'clear'\n \"\n [disabled]=\"!isDateSelectable(calendarGrid()[i][j])\"\n (click)=\"selectDate(calendarGrid()[i][j])\"\n >\n {{ calendarGrid()[i][j] | date: 'd' }}\n </ion-button>\n </ion-col>\n }\n </ion-row>\n }\n @if (timePicker || manualTimePicker) {\n <ion-row>\n <h1 class=\"headerLabel\">\n {{ 'IDEA_UNCOMMON.CALENDAR.SET_TIME' | translate }}\n </h1>\n </ion-row>\n }\n @if (timePicker) {\n <ion-row class=\"ion-align-items-center\">\n <ion-col class=\"ion-no-padding\" [size]=\"7\">\n <ion-row>\n @for (hour of hours; track hour) {\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"2\">\n <ion-button\n size=\"small\"\n shape=\"round\"\n [color]=\"isSameHour(hour) ? 'primary' : 'dark'\"\n [fill]=\"isSameHour(hour) ? 'solid' : 'clear'\"\n (click)=\"selectHour(hour)\"\n >\n {{ hour }}\n </ion-button>\n </ion-col>\n }\n </ion-row>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"1\"> : </ion-col>\n <ion-col class=\"ion-no-padding\" [size]=\"4\">\n <ion-row>\n @for (minute of minutes; track minute) {\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"4\">\n <ion-button\n size=\"small\"\n shape=\"round\"\n [color]=\"isSameMinute(minute) ? 'primary' : 'dark'\"\n [fill]=\"isSameMinute(minute) ? 'solid' : 'clear'\"\n (click)=\"selectMinute(minute)\"\n >\n {{ minute }}\n </ion-button>\n </ion-col>\n }\n </ion-row>\n </ion-col>\n </ion-row>\n }\n @if (manualTimePicker) {\n <ion-row class=\"ion-align-items-center manualTimePicker\">\n <ion-col class=\"ion-text-center\" [size]=\"12\">\n <input type=\"number\" min=\"0\" max=\"23\" [value]=\"hour\" (change)=\"selectHour($event.target.value)\" />\n <span>:</span>\n <input type=\"number\" min=\"0\" max=\"59\" [value]=\"minute\" (change)=\"selectMinute($event.target.value)\" />\n </ion-col>\n </ion-row>\n }\n </ion-grid>\n</ion-content>\n<ion-footer>\n <ion-toolbar color=\"ideaToolbar\" class=\"tappable\" (click)=\"save()\">\n <ion-title class=\"ion-text-center\">\n <ion-icon name=\"checkmark\" />\n </ion-title>\n </ion-toolbar>\n</ion-footer>\n", styles: ["ion-content{--background: var(--ion-color-white)}h1{margin:10px}.headerLabel{font-size:.7em;font-weight:700;text-transform:uppercase}ion-col,ion-button{font-size:.95em}ion-button{margin:3px 0}ion-footer ion-toolbar{--min-height: 40px}.tappable{cursor:pointer}.manualTimePicker{padding:20px}.manualTimePicker input[type=number],.manualTimePicker span{font-size:2.5em}.manualTimePicker input[type=number]{border:none;border-bottom:2px solid var(--ion-color-dark);text-align:center}.manualTimePicker span{padding-left:4px;padding-right:4px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: IonFooter, selector: "ion-footer", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonInput, selector: "ion-input", inputs: ["accept", "autocapitalize", "autocomplete", "autocorrect", "autofocus", "clearInput", "clearOnEdit", "color", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "max", "maxlength", "min", "minlength", "mode", "multiple", "name", "pattern", "placeholder", "readonly", "required", "shape", "size", "spellcheck", "step", "type", "value"] }, { kind: "component", type: IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: IonRow, selector: "ion-row" }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonGrid, selector: "ion-grid", inputs: ["fixed"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "pipe", type: i1$1.DatePipe, name: "date" }, { kind: "pipe", type: IDEATranslatePipe, name: "translate" }, { kind: "pipe", type: IDEALocalizedDatePipe, name: "dateLocale" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2017
2039
  }
2018
2040
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: IDEACalendarPickerComponent, decorators: [{
2019
2041
  type: Component,
@@ -2034,7 +2056,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
2034
2056
  IonTitle,
2035
2057
  IonToolbar,
2036
2058
  IonHeader
2037
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ion-header>\n <ion-toolbar color=\"ideaToolbar\">\n <ion-title>{{ title }}</ion-title>\n <ion-buttons slot=\"end\">\n @if (!hideClearButton) {\n <ion-button [title]=\"'IDEA_UNCOMMON.CALENDAR.RESET' | translate\" (click)=\"save(true)\">\n <ion-icon slot=\"icon-only\" name=\"arrow-undo\" />\n </ion-button>\n }\n <ion-button [title]=\"'IDEA_UNCOMMON.CALENDAR.CLOSE_WITHOUT_SAVING' | translate\" (click)=\"close()\">\n <ion-icon slot=\"icon-only\" name=\"close\" />\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n<ion-content>\n <ion-grid fixed class=\"ion-no-padding\">\n <ion-row>\n <h1 class=\"headerLabel\">{{ 'IDEA_UNCOMMON.CALENDAR.SET_DATE' | translate }}</h1>\n </ion-row>\n <ion-row style=\"margin-bottom: 8px\">\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"5\">\n <ion-row class=\"ion-align-items-center ion-justify-content-center\">\n <ion-col class=\"ion-text-right ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addYears(-1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-back\" />\n </ion-button>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding tappable\" [size]=\"6\">\n <ion-input\n type=\"text\"\n [placeholder]=\"'IDEA_UNCOMMON.CALENDAR.YEAR' | translate\"\n [value]=\"refDate.getFullYear()\"\n (ionFocus)=\"$event.target.value = ''\"\n (ionChange)=\"setYear($event?.target?.value)\"\n >\n </ion-input>\n </ion-col>\n <ion-col class=\"ion-text-left ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addYears(+1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-forward\" />\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"7\">\n <ion-row class=\"ion-align-items-center\">\n <ion-col class=\"ion-text-right ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addMonths(-1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-back\" />\n </ion-button>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding tappable\" [size]=\"6\" (click)=\"showMonths()\">\n {{ refDate | dateLocale: 'MMMM' }}\n </ion-col>\n <ion-col class=\"ion-text-left ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addMonths(+1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-forward\" />\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-col>\n </ion-row>\n <ion-row style=\"margin-bottom: 8px\" class=\"headerLabel\">\n @for (ddd of weekDays; track ddd) {\n <ion-col class=\"ion-text-center ion-no-padding\">\n {{ ddd }}\n </ion-col>\n }\n </ion-row>\n @for (row of calendarGrid; track row; let i = $index) {\n <ion-row class=\"ion-align-items-center\">\n @for (col of row; track col; let j = $index) {\n <ion-col class=\"ion-text-center ion-no-padding\">\n <ion-button\n size=\"small\"\n shape=\"round\"\n [color]=\"\n isSameDay(calendarGrid[i][j], today) || isSameDay(calendarGrid[i][j], selectedDate)\n ? 'primary'\n : isSameMonth(calendarGrid[i][j], refDate)\n ? 'dark'\n : 'medium'\n \"\n [fill]=\"\n isSameDay(calendarGrid[i][j], selectedDate)\n ? 'solid'\n : isSameDay(calendarGrid[i][j], today)\n ? 'outline'\n : 'clear'\n \"\n [disabled]=\"!isDateSelectable(calendarGrid[i][j])\"\n (click)=\"selectDate(calendarGrid[i][j])\"\n >\n {{ calendarGrid[i][j] | date: 'd' }}\n </ion-button>\n </ion-col>\n }\n </ion-row>\n }\n @if (timePicker || manualTimePicker) {\n <ion-row>\n <h1 class=\"headerLabel\">\n {{ 'IDEA_UNCOMMON.CALENDAR.SET_TIME' | translate }}\n </h1>\n </ion-row>\n }\n @if (timePicker) {\n <ion-row class=\"ion-align-items-center\">\n <ion-col class=\"ion-no-padding\" [size]=\"7\">\n <ion-row>\n @for (hour of hours; track hour) {\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"2\">\n <ion-button\n size=\"small\"\n shape=\"round\"\n [color]=\"isSameHour(hour) ? 'primary' : 'dark'\"\n [fill]=\"isSameHour(hour) ? 'solid' : 'clear'\"\n (click)=\"selectHour(hour)\"\n >\n {{ hour }}\n </ion-button>\n </ion-col>\n }\n </ion-row>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"1\"> : </ion-col>\n <ion-col class=\"ion-no-padding\" [size]=\"4\">\n <ion-row>\n @for (minute of minutes; track minute) {\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"4\">\n <ion-button\n size=\"small\"\n shape=\"round\"\n [color]=\"isSameMinute(minute) ? 'primary' : 'dark'\"\n [fill]=\"isSameMinute(minute) ? 'solid' : 'clear'\"\n (click)=\"selectMinute(minute)\"\n >\n {{ minute }}\n </ion-button>\n </ion-col>\n }\n </ion-row>\n </ion-col>\n </ion-row>\n }\n @if (manualTimePicker) {\n <ion-row class=\"ion-align-items-center manualTimePicker\">\n <ion-col class=\"ion-text-center\" [size]=\"12\">\n <input type=\"number\" min=\"0\" max=\"23\" [value]=\"hour\" (change)=\"selectHour($event.target.value)\" />\n <span>:</span>\n <input type=\"number\" min=\"0\" max=\"59\" [value]=\"minute\" (change)=\"selectMinute($event.target.value)\" />\n </ion-col>\n </ion-row>\n }\n </ion-grid>\n</ion-content>\n<ion-footer>\n <ion-toolbar color=\"ideaToolbar\" class=\"tappable\" (click)=\"save()\">\n <ion-title class=\"ion-text-center\">\n <ion-icon name=\"checkmark\" />\n </ion-title>\n </ion-toolbar>\n</ion-footer>\n", styles: ["ion-content{--background: var(--ion-color-white)}h1{margin:10px}.headerLabel{font-size:.7em;font-weight:700;text-transform:uppercase}ion-col,ion-button{font-size:.95em}ion-button{margin:3px 0}ion-footer ion-toolbar{--min-height: 40px}.tappable{cursor:pointer}.manualTimePicker{padding:20px}.manualTimePicker input[type=number],.manualTimePicker span{font-size:2.5em}.manualTimePicker input[type=number]{border:none;border-bottom:2px solid var(--ion-color-dark);text-align:center}.manualTimePicker span{padding-left:4px;padding-right:4px}\n"] }]
2059
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ion-header>\n <ion-toolbar color=\"ideaToolbar\">\n <ion-title>{{ title }}</ion-title>\n <ion-buttons slot=\"end\">\n @if (!hideClearButton) {\n <ion-button [title]=\"'IDEA_UNCOMMON.CALENDAR.RESET' | translate\" (click)=\"save(true)\">\n <ion-icon slot=\"icon-only\" name=\"arrow-undo\" />\n </ion-button>\n }\n <ion-button [title]=\"'IDEA_UNCOMMON.CALENDAR.CLOSE_WITHOUT_SAVING' | translate\" (click)=\"close()\">\n <ion-icon slot=\"icon-only\" name=\"close\" />\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n<ion-content>\n <ion-grid fixed class=\"ion-no-padding\">\n <ion-row>\n <h1 class=\"headerLabel\">{{ 'IDEA_UNCOMMON.CALENDAR.SET_DATE' | translate }}</h1>\n </ion-row>\n <ion-row style=\"margin-bottom: 8px\">\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"5\">\n <ion-row class=\"ion-align-items-center ion-justify-content-center\">\n <ion-col class=\"ion-text-right ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addYears(-1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-back\" />\n </ion-button>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding tappable\" [size]=\"6\">\n <ion-input\n type=\"text\"\n [placeholder]=\"'IDEA_UNCOMMON.CALENDAR.YEAR' | translate\"\n [value]=\"refDate().getFullYear()\"\n (ionFocus)=\"$event.target.value = ''\"\n (ionChange)=\"setYear($event?.target?.value)\"\n >\n </ion-input>\n </ion-col>\n <ion-col class=\"ion-text-left ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addYears(+1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-forward\" />\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"7\">\n <ion-row class=\"ion-align-items-center\">\n <ion-col class=\"ion-text-right ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addMonths(-1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-back\" />\n </ion-button>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding tappable\" [size]=\"6\" (click)=\"showMonths()\">\n {{ refDate() | dateLocale: 'MMMM' }}\n </ion-col>\n <ion-col class=\"ion-text-left ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addMonths(+1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-forward\" />\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-col>\n </ion-row>\n <ion-row style=\"margin-bottom: 8px\" class=\"headerLabel\">\n @for (ddd of weekDays; track ddd) {\n <ion-col class=\"ion-text-center ion-no-padding\">\n {{ ddd }}\n </ion-col>\n }\n </ion-row>\n @for (row of calendarGrid(); track row; let i = $index) {\n <ion-row class=\"ion-align-items-center\">\n @for (col of row; track col; let j = $index) {\n <ion-col class=\"ion-text-center ion-no-padding\">\n <ion-button\n size=\"small\"\n shape=\"round\"\n [color]=\"\n isSameDay(calendarGrid()[i][j], today) || isSameDay(calendarGrid()[i][j], selectedDate)\n ? 'primary'\n : isSameMonth(calendarGrid()[i][j], refDate())\n ? 'dark'\n : 'medium'\n \"\n [fill]=\"\n isSameDay(calendarGrid()[i][j], selectedDate)\n ? 'solid'\n : isSameDay(calendarGrid()[i][j], today)\n ? 'outline'\n : 'clear'\n \"\n [disabled]=\"!isDateSelectable(calendarGrid()[i][j])\"\n (click)=\"selectDate(calendarGrid()[i][j])\"\n >\n {{ calendarGrid()[i][j] | date: 'd' }}\n </ion-button>\n </ion-col>\n }\n </ion-row>\n }\n @if (timePicker || manualTimePicker) {\n <ion-row>\n <h1 class=\"headerLabel\">\n {{ 'IDEA_UNCOMMON.CALENDAR.SET_TIME' | translate }}\n </h1>\n </ion-row>\n }\n @if (timePicker) {\n <ion-row class=\"ion-align-items-center\">\n <ion-col class=\"ion-no-padding\" [size]=\"7\">\n <ion-row>\n @for (hour of hours; track hour) {\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"2\">\n <ion-button\n size=\"small\"\n shape=\"round\"\n [color]=\"isSameHour(hour) ? 'primary' : 'dark'\"\n [fill]=\"isSameHour(hour) ? 'solid' : 'clear'\"\n (click)=\"selectHour(hour)\"\n >\n {{ hour }}\n </ion-button>\n </ion-col>\n }\n </ion-row>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"1\"> : </ion-col>\n <ion-col class=\"ion-no-padding\" [size]=\"4\">\n <ion-row>\n @for (minute of minutes; track minute) {\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"4\">\n <ion-button\n size=\"small\"\n shape=\"round\"\n [color]=\"isSameMinute(minute) ? 'primary' : 'dark'\"\n [fill]=\"isSameMinute(minute) ? 'solid' : 'clear'\"\n (click)=\"selectMinute(minute)\"\n >\n {{ minute }}\n </ion-button>\n </ion-col>\n }\n </ion-row>\n </ion-col>\n </ion-row>\n }\n @if (manualTimePicker) {\n <ion-row class=\"ion-align-items-center manualTimePicker\">\n <ion-col class=\"ion-text-center\" [size]=\"12\">\n <input type=\"number\" min=\"0\" max=\"23\" [value]=\"hour\" (change)=\"selectHour($event.target.value)\" />\n <span>:</span>\n <input type=\"number\" min=\"0\" max=\"59\" [value]=\"minute\" (change)=\"selectMinute($event.target.value)\" />\n </ion-col>\n </ion-row>\n }\n </ion-grid>\n</ion-content>\n<ion-footer>\n <ion-toolbar color=\"ideaToolbar\" class=\"tappable\" (click)=\"save()\">\n <ion-title class=\"ion-text-center\">\n <ion-icon name=\"checkmark\" />\n </ion-title>\n </ion-toolbar>\n</ion-footer>\n", styles: ["ion-content{--background: var(--ion-color-white)}h1{margin:10px}.headerLabel{font-size:.7em;font-weight:700;text-transform:uppercase}ion-col,ion-button{font-size:.95em}ion-button{margin:3px 0}ion-footer ion-toolbar{--min-height: 40px}.tappable{cursor:pointer}.manualTimePicker{padding:20px}.manualTimePicker input[type=number],.manualTimePicker span{font-size:2.5em}.manualTimePicker input[type=number]{border:none;border-bottom:2px solid var(--ion-color-dark);text-align:center}.manualTimePicker span{padding-left:4px;padding-right:4px}\n"] }]
2038
2060
  }], propDecorators: { inputDate: [{
2039
2061
  type: Input
2040
2062
  }], timePicker: [{
@@ -2055,7 +2077,6 @@ class IDEADateTimeComponent {
2055
2077
  constructor() {
2056
2078
  this._modal = inject(ModalController);
2057
2079
  this._translate = inject(IDEATranslationsService);
2058
- this._cdr = inject(ChangeDetectorRef);
2059
2080
  /**
2060
2081
  * The date to show/pick.
2061
2082
  */
@@ -2133,12 +2154,14 @@ class IDEADateTimeComponent {
2133
2154
  ...(ngDevMode ? [undefined, { debugName: "max" }] : /* istanbul ignore next */ []));
2134
2155
  this.dateChange = output();
2135
2156
  this.iconSelect = output();
2136
- this.isOpening = false;
2157
+ this.valueToDisplay = signal('', /* @ts-ignore */
2158
+ ...(ngDevMode ? [{ debugName: "valueToDisplay" }] : /* istanbul ignore next */ []));
2159
+ this.isOpening = signal(false, /* @ts-ignore */
2160
+ ...(ngDevMode ? [{ debugName: "isOpening" }] : /* istanbul ignore next */ []));
2137
2161
  }
2138
2162
  ngOnInit() {
2139
2163
  this.langChangeSubscription = this._translate.onLangChange.subscribe(() => {
2140
- this.valueToDisplay = this.getValueToDisplay(this.date());
2141
- this._cdr.markForCheck();
2164
+ this.valueToDisplay.set(this.getValueToDisplay(this.date()));
2142
2165
  });
2143
2166
  }
2144
2167
  ngOnDestroy() {
@@ -2147,12 +2170,12 @@ class IDEADateTimeComponent {
2147
2170
  }
2148
2171
  ngOnChanges(changes) {
2149
2172
  if (changes.date || changes.timePicker)
2150
- this.valueToDisplay = this.getValueToDisplay(this.date());
2173
+ this.valueToDisplay.set(this.getValueToDisplay(this.date()));
2151
2174
  }
2152
2175
  async openCalendarPicker() {
2153
- if (this.disabled() || this.isOpening)
2176
+ if (this.disabled() || this.isOpening())
2154
2177
  return;
2155
- this.isOpening = true;
2178
+ this.isOpening.set(true);
2156
2179
  const modal = await this._modal.create({
2157
2180
  component: IDEACalendarPickerComponent,
2158
2181
  componentProps: {
@@ -2174,7 +2197,7 @@ class IDEADateTimeComponent {
2174
2197
  }
2175
2198
  });
2176
2199
  modal.present();
2177
- this.isOpening = false;
2200
+ this.isOpening.set(false);
2178
2201
  }
2179
2202
  getValueToDisplay(date) {
2180
2203
  return !date
@@ -2197,7 +2220,7 @@ class IDEADateTimeComponent {
2197
2220
  [lines]="lines()"
2198
2221
  [title]="placeholder() || label() || ''"
2199
2222
  [button]="!disabled()"
2200
- [disabled]="isOpening"
2223
+ [disabled]="isOpening()"
2201
2224
  [class.withLabel]="label()"
2202
2225
  (click)="openCalendarPicker()"
2203
2226
  >
@@ -2221,12 +2244,12 @@ class IDEADateTimeComponent {
2221
2244
  </ion-label>
2222
2245
  }
2223
2246
  <ion-label class="value" [class.selectable]="!disabled()">
2224
- @if (!valueToDisplay && !disabled()) {
2247
+ @if (!valueToDisplay() && !disabled()) {
2225
2248
  <ion-text class="placeholder" [class.selectable]="!disabled()">
2226
2249
  {{ placeholder() }}
2227
2250
  </ion-text>
2228
2251
  }
2229
- {{ valueToDisplay }}
2252
+ {{ valueToDisplay() }}
2230
2253
  </ion-label>
2231
2254
  @if (!disabled()) {
2232
2255
  <ion-icon slot="end" icon="caret-down" class="selectIcon" [class.selectable]="!disabled()" />
@@ -2243,7 +2266,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
2243
2266
  [lines]="lines()"
2244
2267
  [title]="placeholder() || label() || ''"
2245
2268
  [button]="!disabled()"
2246
- [disabled]="isOpening"
2269
+ [disabled]="isOpening()"
2247
2270
  [class.withLabel]="label()"
2248
2271
  (click)="openCalendarPicker()"
2249
2272
  >
@@ -2267,12 +2290,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
2267
2290
  </ion-label>
2268
2291
  }
2269
2292
  <ion-label class="value" [class.selectable]="!disabled()">
2270
- @if (!valueToDisplay && !disabled()) {
2293
+ @if (!valueToDisplay() && !disabled()) {
2271
2294
  <ion-text class="placeholder" [class.selectable]="!disabled()">
2272
2295
  {{ placeholder() }}
2273
2296
  </ion-text>
2274
2297
  }
2275
- {{ valueToDisplay }}
2298
+ {{ valueToDisplay() }}
2276
2299
  </ion-label>
2277
2300
  @if (!disabled()) {
2278
2301
  <ion-icon slot="end" icon="caret-down" class="selectIcon" [class.selectable]="!disabled()" />
@@ -2500,6 +2523,12 @@ class IDEAEchoPage {
2500
2523
  this._loading = inject(IDEALoadingService);
2501
2524
  this._API = inject(IDEAAWSAPIService);
2502
2525
  this._translate = inject(IDEATranslationsService);
2526
+ this.content = signal(undefined, /* @ts-ignore */
2527
+ ...(ngDevMode ? [{ debugName: "content" }] : /* istanbul ignore next */ []));
2528
+ this.htmlContent = signal(undefined, /* @ts-ignore */
2529
+ ...(ngDevMode ? [{ debugName: "htmlContent" }] : /* istanbul ignore next */ []));
2530
+ this.success = signal(undefined, /* @ts-ignore */
2531
+ ...(ngDevMode ? [{ debugName: "success" }] : /* istanbul ignore next */ []));
2503
2532
  }
2504
2533
  ngOnInit() {
2505
2534
  const request = this._route.snapshot.paramMap.get('request') || this._route.snapshot.queryParams.request;
@@ -2532,15 +2561,15 @@ class IDEAEchoPage {
2532
2561
  this.endExternalCalendarsIntegrationFlow(ExternalCalendarSources.GOOGLE, code, state);
2533
2562
  break;
2534
2563
  default:
2535
- this.success = false;
2536
- this.content = this._translate._('IDEA_UNCOMMON.ECHO.INVALID_ACTION');
2564
+ this.success.set(false);
2565
+ this.content.set(this._translate._('IDEA_UNCOMMON.ECHO.INVALID_ACTION'));
2537
2566
  break;
2538
2567
  }
2539
2568
  }
2540
2569
  maintenanceMode() {
2541
2570
  const announcement = this._tc.get('idea-announcement');
2542
2571
  if (announcement && announcement.content)
2543
- this.htmlContent = mdToHtml(announcement.content);
2572
+ this.htmlContent.set(mdToHtml(announcement.content));
2544
2573
  }
2545
2574
  async followRegistrationLink(code, user) {
2546
2575
  try {
@@ -2554,12 +2583,12 @@ class IDEAEchoPage {
2554
2583
  cognitoUserPoolClientId: this._env.aws.cognito.userPoolClientId
2555
2584
  }
2556
2585
  });
2557
- this.success = true;
2558
- this.content = this._translate._('IDEA_UNCOMMON.ECHO.ACCOUNT_CONFIRMED');
2586
+ this.success.set(true);
2587
+ this.content.set(this._translate._('IDEA_UNCOMMON.ECHO.ACCOUNT_CONFIRMED'));
2559
2588
  }
2560
2589
  catch (error) {
2561
- this.success = false;
2562
- this.content = this._translate._('IDEA_UNCOMMON.ECHO.REQUEST_FAILED');
2590
+ this.success.set(false);
2591
+ this.content.set(this._translate._('IDEA_UNCOMMON.ECHO.REQUEST_FAILED'));
2563
2592
  }
2564
2593
  finally {
2565
2594
  this._loading.hide();
@@ -2569,12 +2598,12 @@ class IDEAEchoPage {
2569
2598
  try {
2570
2599
  await this._loading.show();
2571
2600
  await this._API.getResource('invitations', { idea: true, resourceId: code });
2572
- this.success = true;
2573
- this.content = this._translate._('IDEA_UNCOMMON.ECHO.TEAM_JOINED');
2601
+ this.success.set(true);
2602
+ this.content.set(this._translate._('IDEA_UNCOMMON.ECHO.TEAM_JOINED'));
2574
2603
  }
2575
2604
  catch (error) {
2576
- this.success = false;
2577
- this.content = this._translate._('IDEA_UNCOMMON.ECHO.REQUEST_FAILED');
2605
+ this.success.set(false);
2606
+ this.content.set(this._translate._('IDEA_UNCOMMON.ECHO.REQUEST_FAILED'));
2578
2607
  }
2579
2608
  finally {
2580
2609
  this._loading.hide();
@@ -2588,28 +2617,28 @@ class IDEAEchoPage {
2588
2617
  resourceId: code,
2589
2618
  params: { project: this._env.idea.project }
2590
2619
  });
2591
- this.success = true;
2592
- this.content = this._translate._('IDEA_UNCOMMON.ECHO.EMAIL_CHANGED');
2620
+ this.success.set(true);
2621
+ this.content.set(this._translate._('IDEA_UNCOMMON.ECHO.EMAIL_CHANGED'));
2593
2622
  }
2594
2623
  catch (error) {
2595
- this.success = false;
2596
- this.content = this._translate._('IDEA_UNCOMMON.ECHO.REQUEST_FAILED');
2624
+ this.success.set(false);
2625
+ this.content.set(this._translate._('IDEA_UNCOMMON.ECHO.REQUEST_FAILED'));
2597
2626
  }
2598
2627
  finally {
2599
2628
  this._loading.hide();
2600
2629
  }
2601
2630
  }
2602
2631
  endGitHubIntegrationFlow(success) {
2603
- this.success = success;
2604
- this.content = success
2632
+ this.success.set(success);
2633
+ this.content.set(success
2605
2634
  ? this._translate._('IDEA_UNCOMMON.ECHO.GITHUB_SOURCE_INTEGRATION_SUCCESS')
2606
- : this._translate._('IDEA_UNCOMMON.ECHO.GITHUB_SOURCE_INTEGRATION_ERROR');
2635
+ : this._translate._('IDEA_UNCOMMON.ECHO.GITHUB_SOURCE_INTEGRATION_ERROR'));
2607
2636
  }
2608
2637
  endTrelloIntegrationFlow(token) {
2609
- this.success = Boolean(token);
2610
- this.content = token
2638
+ this.success.set(Boolean(token));
2639
+ this.content.set(token
2611
2640
  ? this._translate._('IDEA_UNCOMMON.ECHO.TRELLO_SOURCE_INTEGRATION_SUCCESS', { token })
2612
- : this._translate._('IDEA_UNCOMMON.ECHO.TRELLO_SOURCE_INTEGRATION_ERROR');
2641
+ : this._translate._('IDEA_UNCOMMON.ECHO.TRELLO_SOURCE_INTEGRATION_ERROR'));
2613
2642
  }
2614
2643
  async endExternalCalendarsIntegrationFlow(service, code, calendarId) {
2615
2644
  try {
@@ -2618,12 +2647,12 @@ class IDEAEchoPage {
2618
2647
  idea: true,
2619
2648
  body: { action: 'SET_EXTERNAL_INTEGRATION', service, code, calendarId, project: this._env.idea.project }
2620
2649
  });
2621
- this.success = true;
2622
- this.content = this._translate._('IDEA_UNCOMMON.ECHO.EXTERNAL_CALENDARS_SOURCE_INTEGRATION_SUCCESS');
2650
+ this.success.set(true);
2651
+ this.content.set(this._translate._('IDEA_UNCOMMON.ECHO.EXTERNAL_CALENDARS_SOURCE_INTEGRATION_SUCCESS'));
2623
2652
  }
2624
2653
  catch (error) {
2625
- this.success = false;
2626
- this.content = this._translate._('IDEA_UNCOMMON.ECHO.EXTERNAL_CALENDARS_SOURCE_INTEGRATION_ERROR');
2654
+ this.success.set(false);
2655
+ this.content.set(this._translate._('IDEA_UNCOMMON.ECHO.EXTERNAL_CALENDARS_SOURCE_INTEGRATION_ERROR'));
2627
2656
  }
2628
2657
  finally {
2629
2658
  this._loading.hide();
@@ -2637,20 +2666,20 @@ class IDEAEchoPage {
2637
2666
  <ion-img [src]="'assets/icons/icon-auth.svg'" />
2638
2667
  <ion-card-header>
2639
2668
  <ion-card-title>
2640
- @if (success === true) {
2669
+ @if (success() === true) {
2641
2670
  <ion-icon testId="echoSuccess" color="success" name="checkmark" />
2642
2671
  }
2643
- @if (success === false) {
2672
+ @if (success() === false) {
2644
2673
  <ion-icon testId="echoFailure" color="danger" name="close" />
2645
2674
  }
2646
2675
  </ion-card-title>
2647
2676
  </ion-card-header>
2648
2677
  <ion-card-content class="selectable">
2649
- {{ content }}
2650
- @if (htmlContent) {
2651
- <div class="htmlContent" [innerHTML]="htmlContent"></div>
2678
+ {{ content() }}
2679
+ @if (htmlContent()) {
2680
+ <div class="htmlContent" [innerHTML]="htmlContent()"></div>
2652
2681
  }
2653
- @if (success === false || success === true) {
2682
+ @if (success() === false || success() === true) {
2654
2683
  <p class="youCanCloseMe">
2655
2684
  <i>{{ 'IDEA_UNCOMMON.ECHO.YOU_CAN_CLOSE_THE_PAGE' | translate }}</i>
2656
2685
  </p>
@@ -2670,20 +2699,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
2670
2699
  <ion-img [src]="'assets/icons/icon-auth.svg'" />
2671
2700
  <ion-card-header>
2672
2701
  <ion-card-title>
2673
- @if (success === true) {
2702
+ @if (success() === true) {
2674
2703
  <ion-icon testId="echoSuccess" color="success" name="checkmark" />
2675
2704
  }
2676
- @if (success === false) {
2705
+ @if (success() === false) {
2677
2706
  <ion-icon testId="echoFailure" color="danger" name="close" />
2678
2707
  }
2679
2708
  </ion-card-title>
2680
2709
  </ion-card-header>
2681
2710
  <ion-card-content class="selectable">
2682
- {{ content }}
2683
- @if (htmlContent) {
2684
- <div class="htmlContent" [innerHTML]="htmlContent"></div>
2711
+ {{ content() }}
2712
+ @if (htmlContent()) {
2713
+ <div class="htmlContent" [innerHTML]="htmlContent()"></div>
2685
2714
  }
2686
- @if (success === false || success === true) {
2715
+ @if (success() === false || success() === true) {
2687
2716
  <p class="youCanCloseMe">
2688
2717
  <i>{{ 'IDEA_UNCOMMON.ECHO.YOU_CAN_CLOSE_THE_PAGE' | translate }}</i>
2689
2718
  </p>
@@ -2938,6 +2967,26 @@ class IDEAOfflineDataService {
2938
2967
  this._translate = inject(IDEATranslationsService);
2939
2968
  this._offline = inject(IDEAOfflineService);
2940
2969
  this._API = inject(IDEAAWSAPIService);
2970
+ /**
2971
+ * True when running a synchronization.
2972
+ */
2973
+ this.synchronizing = signal(undefined, /* @ts-ignore */
2974
+ ...(ngDevMode ? [{ debugName: "synchronizing" }] : /* istanbul ignore next */ []));
2975
+ /**
2976
+ * The timestamp of the last synchronization.
2977
+ */
2978
+ this.lastSyncAt = signal(undefined, /* @ts-ignore */
2979
+ ...(ngDevMode ? [{ debugName: "lastSyncAt" }] : /* istanbul ignore next */ []));
2980
+ /**
2981
+ * True if an error happened in the last synchronization.
2982
+ */
2983
+ this.errorInLastSync = signal(undefined, /* @ts-ignore */
2984
+ ...(ngDevMode ? [{ debugName: "errorInLastSync" }] : /* istanbul ignore next */ []));
2985
+ /**
2986
+ * True if the synchronisation is too vast and so require a manual action of the user.
2987
+ */
2988
+ this.requiresManualConfirmation = signal(undefined, /* @ts-ignore */
2989
+ ...(ngDevMode ? [{ debugName: "requiresManualConfirmation" }] : /* istanbul ignore next */ []));
2941
2990
  this.queueAPIRequests = new Array();
2942
2991
  this.resources = new Array();
2943
2992
  this.cacheableResources = {};
@@ -2945,7 +2994,7 @@ class IDEAOfflineDataService {
2945
2994
  this._offline.subscribe(isOnline => {
2946
2995
  if (isOnline) {
2947
2996
  // try a synchronization if needed or if the last one failed
2948
- if (this.errorInLastSync)
2997
+ if (this.errorInLastSync())
2949
2998
  this.synchronize();
2950
2999
  else
2951
3000
  this.synchronizeIfNeeded();
@@ -3013,8 +3062,8 @@ class IDEAOfflineDataService {
3013
3062
  loadLastSyncAt() {
3014
3063
  return new Promise(resolve => {
3015
3064
  this._storage.get(this.lastSyncKey).then((lastSyncAt) => {
3016
- this.lastSyncAt = lastSyncAt ? Number(lastSyncAt) : null;
3017
- resolve(this.lastSyncAt);
3065
+ this.lastSyncAt.set(lastSyncAt ? Number(lastSyncAt) : null);
3066
+ resolve(this.lastSyncAt());
3018
3067
  });
3019
3068
  });
3020
3069
  }
@@ -3022,7 +3071,7 @@ class IDEAOfflineDataService {
3022
3071
  * Update last sync at info in the local storage.
3023
3072
  */
3024
3073
  saveLastSyncAt(lastSyncAt) {
3025
- this.lastSyncAt = lastSyncAt;
3074
+ this.lastSyncAt.set(lastSyncAt);
3026
3075
  return this._storage.set(this.lastSyncKey, lastSyncAt);
3027
3076
  }
3028
3077
  /**
@@ -3155,22 +3204,22 @@ class IDEAOfflineDataService {
3155
3204
  cr.synchronizing = true;
3156
3205
  const success = await this.syncResourceDeltaRecords(resource, delta.records[resource]);
3157
3206
  if (!success)
3158
- this.errorInLastSync = true;
3207
+ this.errorInLastSync.set(true);
3159
3208
  }
3160
3209
  }));
3161
3210
  // if there were error of if there is nothing left to sync, we are done
3162
- if (this.errorInLastSync || !delta.next)
3211
+ if (this.errorInLastSync() || !delta.next)
3163
3212
  return done();
3164
3213
  // otherwise, get the next page of the delta and go recursive until we are done
3165
3214
  const params = { next: delta.next, limit: NUM_ELEMENTS_WITH_MANUAL_SYNC };
3166
- if (this.lastSyncAt)
3167
- params.since = this.lastSyncAt;
3215
+ if (this.lastSyncAt())
3216
+ params.since = this.lastSyncAt();
3168
3217
  const newDelta = await this._API.getResource(this.teamId ? `teams/${this.teamId}/delta` : 'delta', { params });
3169
3218
  this.syncDeltaRecords(newDelta, done);
3170
3219
  }
3171
3220
  catch (err) {
3172
3221
  // if something went wrong, stops the operation, since we need to make sure the entire flow is consistent
3173
- this.errorInLastSync = true;
3222
+ this.errorInLastSync.set(true);
3174
3223
  return done();
3175
3224
  }
3176
3225
  }
@@ -3246,7 +3295,7 @@ class IDEAOfflineDataService {
3246
3295
  return;
3247
3296
  this.loadLastSyncAt().then(() => this.loadQueueAPIRequest()
3248
3297
  .then(() => {
3249
- if (this.queueAPIRequests.length || Date.now() > this.lastSyncAt + SYNC_EXPIRATION_INTERVAL)
3298
+ if (this.queueAPIRequests.length || Date.now() > this.lastSyncAt() + SYNC_EXPIRATION_INTERVAL)
3250
3299
  this.synchronize();
3251
3300
  resolve();
3252
3301
  })
@@ -3259,11 +3308,11 @@ class IDEAOfflineDataService {
3259
3308
  */
3260
3309
  synchronize(manualConfirmation) {
3261
3310
  return new Promise((resolve, reject) => {
3262
- if (!this.isAllowed() || this.synchronizing)
3311
+ if (!this.isAllowed() || this.synchronizing())
3263
3312
  return resolve();
3264
- this.synchronizing = true;
3265
- this.errorInLastSync = false;
3266
- this.requiresManualConfirmation = false;
3313
+ this.synchronizing.set(true);
3314
+ this.errorInLastSync.set(false);
3315
+ this.requiresManualConfirmation.set(false);
3267
3316
  // load the lastSyncAt information
3268
3317
  this.loadLastSyncAt().then(() =>
3269
3318
  // try to run all the pending API requests in the queue, i.e. uploading the offline resources changed
@@ -3273,21 +3322,21 @@ class IDEAOfflineDataService {
3273
3322
  const now = Date.now();
3274
3323
  // prepare a first "short" Delta request, to see if there is a lot of data to process
3275
3324
  const params = { limit: NUM_ELEMENTS_WITHOUT_MANUAL_SYNC };
3276
- if (this.lastSyncAt)
3277
- params.since = this.lastSyncAt;
3325
+ if (this.lastSyncAt())
3326
+ params.since = this.lastSyncAt();
3278
3327
  this._API
3279
3328
  .getResource(this.teamId ? `teams/${this.teamId}/delta` : 'delta', { params })
3280
3329
  .then((delta) => {
3281
3330
  // decide if to proceed with the sync: in case there is another page, it requires a manual confirmation
3282
3331
  if (delta.next && !manualConfirmation) {
3283
- this.requiresManualConfirmation = true;
3284
- this.synchronizing = false;
3332
+ this.requiresManualConfirmation.set(true);
3333
+ this.synchronizing.set(false);
3285
3334
  return resolve();
3286
3335
  }
3287
3336
  // save the records in the delta and request possible new pages
3288
3337
  this.syncDeltaRecords(delta, () => {
3289
- if (this.errorInLastSync) {
3290
- this.synchronizing = false;
3338
+ if (this.errorInLastSync()) {
3339
+ this.synchronizing.set(false);
3291
3340
  resolve();
3292
3341
  }
3293
3342
  else {
@@ -3296,13 +3345,13 @@ class IDEAOfflineDataService {
3296
3345
  // update the timestamp of last sync
3297
3346
  this.saveLastSyncAt(now)
3298
3347
  .then(() => {
3299
- this.synchronizing = false;
3348
+ this.synchronizing.set(false);
3300
3349
  resolve();
3301
3350
  })
3302
3351
  .catch(() => {
3303
3352
  // we couldn't save the last sync info
3304
- this.errorInLastSync = true;
3305
- this.synchronizing = false;
3353
+ this.errorInLastSync.set(true);
3354
+ this.synchronizing.set(false);
3306
3355
  reject();
3307
3356
  });
3308
3357
  }
@@ -3310,15 +3359,15 @@ class IDEAOfflineDataService {
3310
3359
  })
3311
3360
  .catch(() => {
3312
3361
  // we couldn't acquire new information (Delta)
3313
- this.errorInLastSync = true;
3314
- this.synchronizing = false;
3362
+ this.errorInLastSync.set(true);
3363
+ this.synchronizing.set(false);
3315
3364
  reject();
3316
3365
  });
3317
3366
  })
3318
3367
  .catch(() => {
3319
3368
  // we stop, since we don't want backend data to override local info not yet uploaded
3320
- this.errorInLastSync = true;
3321
- this.synchronizing = false;
3369
+ this.errorInLastSync.set(true);
3370
+ this.synchronizing.set(false);
3322
3371
  reject();
3323
3372
  }));
3324
3373
  });
@@ -3366,13 +3415,13 @@ class IDEACacheableResourceComponent {
3366
3415
  @if (resource().error) {
3367
3416
  <ion-icon slot="end" name="alert-circle" color="danger" />
3368
3417
  }
3369
- @if (!resource().error && _offline.synchronizing && resource().synchronizing) {
3418
+ @if (!resource().error && _offline.synchronizing() && resource().synchronizing) {
3370
3419
  <ion-spinner slot="end" name="dots" />
3371
3420
  }
3372
- @if (!resource().error && !resource().synchronizing && !_offline.requiresManualConfirmation) {
3421
+ @if (!resource().error && !resource().synchronizing && !_offline.requiresManualConfirmation()) {
3373
3422
  <ion-icon slot="end" name="checkmark" color="success" />
3374
3423
  }
3375
- @if (!resource().error && !resource().synchronizing && _offline.requiresManualConfirmation) {
3424
+ @if (!resource().error && !resource().synchronizing && _offline.requiresManualConfirmation()) {
3376
3425
  <ion-icon slot="end" name="pause" color="dark" />
3377
3426
  }
3378
3427
  </ion-item>
@@ -3390,13 +3439,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
3390
3439
  @if (resource().error) {
3391
3440
  <ion-icon slot="end" name="alert-circle" color="danger" />
3392
3441
  }
3393
- @if (!resource().error && _offline.synchronizing && resource().synchronizing) {
3442
+ @if (!resource().error && _offline.synchronizing() && resource().synchronizing) {
3394
3443
  <ion-spinner slot="end" name="dots" />
3395
3444
  }
3396
- @if (!resource().error && !resource().synchronizing && !_offline.requiresManualConfirmation) {
3445
+ @if (!resource().error && !resource().synchronizing && !_offline.requiresManualConfirmation()) {
3397
3446
  <ion-icon slot="end" name="checkmark" color="success" />
3398
3447
  }
3399
- @if (!resource().error && !resource().synchronizing && _offline.requiresManualConfirmation) {
3448
+ @if (!resource().error && !resource().synchronizing && _offline.requiresManualConfirmation()) {
3400
3449
  <ion-icon slot="end" name="pause" color="dark" />
3401
3450
  }
3402
3451
  </ion-item>
@@ -3472,7 +3521,7 @@ class IDEAOfflineManagerComponent {
3472
3521
  this._modal.dismiss();
3473
3522
  }
3474
3523
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: IDEAOfflineManagerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3475
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: IDEAOfflineManagerComponent, isStandalone: true, selector: "idea-offline-manager", ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"ideaToolbar\">\n <ion-buttons slot=\"start\">\n <ion-button (click)=\"close()\">\n <ion-icon name=\"close\" slot=\"icon-only\" />\n </ion-button>\n </ion-buttons>\n <ion-title>\n {{ 'IDEA_UNCOMMON.OFFLINE.OFFLINE_MANAGER' | translate }}\n </ion-title>\n <ion-buttons slot=\"end\">\n <ion-button [title]=\"'IDEA_UNCOMMON.OFFLINE.ADVANCED_ACTIONS' | translate\" (click)=\"actions()\">\n <ion-icon slot=\"icon-only\" name=\"ellipsis-vertical\" />\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n<ion-content>\n <ion-list class=\"aList\">\n @if (!_offline.synchronizing && _offline.requiresManualConfirmation) {\n <ion-list-header>\n <ion-label>\n <h2>{{ 'IDEA_UNCOMMON.OFFLINE.HUGE_SYNC_NEED_MANUAL_CONFIRMATION' | translate }}</h2>\n </ion-label>\n </ion-list-header>\n }\n @if (!_offline.synchronizing) {\n <ion-list-header>\n <ion-label class=\"ion-text-wrap\">\n <h3 class=\"lastSyncAt\">\n {{ 'IDEA_UNCOMMON.OFFLINE.LAST_SYNC' | translate }}:\n <ion-text>\n {{\n _offline.lastSyncAt\n ? (_offline.lastSyncAt | dateLocale: 'medium')\n : ('IDEA_UNCOMMON.OFFLINE.NEVER' | translate)\n }}\n </ion-text>\n </h3>\n @if (_offline.errorInLastSync) {\n <p>\n <ion-text color=\"danger\">\n <b>{{ 'IDEA_UNCOMMON.OFFLINE.ERROR_DURING_LAST_SYNC' | translate }}</b>\n </ion-text>\n </p>\n }\n </ion-label>\n <ion-button\n size=\"small\"\n fill=\"solid\"\n [color]=\"_offline.errorInLastSync ? 'danger' : 'dark'\"\n [disabled]=\"_offline.isOffline()\"\n (click)=\"sync()\"\n >\n <ion-icon slot=\"start\" name=\"sync\" /> {{ 'IDEA_UNCOMMON.OFFLINE.SYNC_NOW' | translate }}\n </ion-button>\n </ion-list-header>\n }\n </ion-list>\n @if (_offline.synchronizing) {\n <ion-list class=\"aList\">\n <ion-item>\n <ion-label class=\"ion-text-center\">\n <ion-spinner name=\"crescent\" color=\"primary\" />\n </ion-label>\n </ion-item>\n </ion-list>\n }\n @if (_offline.useQueueAPIRequests) {\n <ion-list class=\"aList\">\n <ion-list-header>\n <ion-label>\n <h2>{{ 'IDEA_UNCOMMON.OFFLINE.UPLOAD' | translate }}</h2>\n <p>{{ 'IDEA_UNCOMMON.OFFLINE.UPLOAD_QUEUE_REQUESTS_EXPLANATION' | translate }}</p>\n </ion-label>\n </ion-list-header>\n @if (!_offline.queueAPIRequests.length) {\n <ion-item>\n <i>{{ 'IDEA_UNCOMMON.OFFLINE.NO_ELEMENTS_TO_UPLOAD' | translate }}</i>\n <ion-icon slot=\"end\" size=\"small\" name=\"checkmark\" color=\"success\" />\n </ion-item>\n }\n @for (r of _offline.queueAPIRequests; track r; let odd = $odd) {\n <ion-item [class.oddItem]=\"odd\">\n <ion-label class=\"ion-text-wrap\">\n {{ r.description }}\n @if (r.error) {\n <p>\n <ion-text color=\"danger\">\n <b>{{ 'IDEA_UNCOMMON.OFFLINE.ERROR_CODE' | translate }}: {{ r.error }}</b>\n </ion-text>\n <br />\n {{ 'IDEA_UNCOMMON.OFFLINE.REQUEST_FAILED_EXPLANATION_I' | translate }}<br />\n {{ 'IDEA_UNCOMMON.OFFLINE.REQUEST_FAILED_EXPLANATION_II' | translate }}<br />\n {{ 'IDEA_UNCOMMON.OFFLINE.REQUEST_FAILED_EXPLANATION_III' | translate }}\n </p>\n }\n </ion-label>\n @if (r.error) {\n <ion-button\n fill=\"clear\"\n slot=\"end\"\n color=\"danger\"\n [title]=\"'IDEA_UNCOMMON.OFFLINE.CANCEL_REQUEST' | translate\"\n (click)=\"deleteRequest(r)\"\n >\n <ion-icon slot=\"icon-only\" name=\"trash\" />\n </ion-button>\n }\n @if (r.error) {\n <ion-button\n fill=\"clear\"\n slot=\"end\"\n color=\"medium\"\n [title]=\"'IDEA_UNCOMMON.OFFLINE.DOWNLOAD_LOG' | translate\"\n (click)=\"downloadRequestLog(r)\"\n >\n <ion-icon slot=\"icon-only\" name=\"download\" />\n </ion-button>\n }\n @if (_offline.synchronizing) {\n <ion-spinner slot=\"end\" name=\"dots\" />\n }\n </ion-item>\n }\n </ion-list>\n }\n @if (_offline.getResources().length) {\n <ion-list class=\"aList\">\n <ion-list-header>\n <ion-label>\n <h2>{{ 'IDEA_UNCOMMON.OFFLINE.DOWNLOAD' | translate }}</h2>\n <p>{{ 'IDEA_UNCOMMON.OFFLINE.DOWNLOAD_RESOURCES_EXPLANATION' | translate }}</p>\n </ion-label>\n </ion-list-header>\n @for (r of _offline.getResources(); track r) {\n <idea-cacheable-resource [resource]=\"_offline.getCacheableResource(r)\" />\n }\n </ion-list>\n }\n</ion-content>\n", styles: [".aList{max-width:500px;margin:0 auto}.aList ion-list-header{padding-right:10px}.aList ion-list-header h3.lastSyncAt{margin-top:6px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IDEACacheableResourceComponent, selector: "idea-cacheable-resource", inputs: ["resource"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonListHeader, selector: "ion-list-header", inputs: ["color", "lines", "mode"] }, { kind: "component", type: IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "pipe", type: IDEATranslatePipe, name: "translate" }, { kind: "pipe", type: IDEALocalizedDatePipe, name: "dateLocale" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3524
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: IDEAOfflineManagerComponent, isStandalone: true, selector: "idea-offline-manager", ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"ideaToolbar\">\n <ion-buttons slot=\"start\">\n <ion-button (click)=\"close()\">\n <ion-icon name=\"close\" slot=\"icon-only\" />\n </ion-button>\n </ion-buttons>\n <ion-title>\n {{ 'IDEA_UNCOMMON.OFFLINE.OFFLINE_MANAGER' | translate }}\n </ion-title>\n <ion-buttons slot=\"end\">\n <ion-button [title]=\"'IDEA_UNCOMMON.OFFLINE.ADVANCED_ACTIONS' | translate\" (click)=\"actions()\">\n <ion-icon slot=\"icon-only\" name=\"ellipsis-vertical\" />\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n<ion-content>\n <ion-list class=\"aList\">\n @if (!_offline.synchronizing() && _offline.requiresManualConfirmation()) {\n <ion-list-header>\n <ion-label>\n <h2>{{ 'IDEA_UNCOMMON.OFFLINE.HUGE_SYNC_NEED_MANUAL_CONFIRMATION' | translate }}</h2>\n </ion-label>\n </ion-list-header>\n }\n @if (!_offline.synchronizing()) {\n <ion-list-header>\n <ion-label class=\"ion-text-wrap\">\n <h3 class=\"lastSyncAt\">\n {{ 'IDEA_UNCOMMON.OFFLINE.LAST_SYNC' | translate }}:\n <ion-text>\n {{\n _offline.lastSyncAt()\n ? (_offline.lastSyncAt() | dateLocale: 'medium')\n : ('IDEA_UNCOMMON.OFFLINE.NEVER' | translate)\n }}\n </ion-text>\n </h3>\n @if (_offline.errorInLastSync()) {\n <p>\n <ion-text color=\"danger\">\n <b>{{ 'IDEA_UNCOMMON.OFFLINE.ERROR_DURING_LAST_SYNC' | translate }}</b>\n </ion-text>\n </p>\n }\n </ion-label>\n <ion-button\n size=\"small\"\n fill=\"solid\"\n [color]=\"_offline.errorInLastSync() ? 'danger' : 'dark'\"\n [disabled]=\"_offline.isOffline()\"\n (click)=\"sync()\"\n >\n <ion-icon slot=\"start\" name=\"sync\" /> {{ 'IDEA_UNCOMMON.OFFLINE.SYNC_NOW' | translate }}\n </ion-button>\n </ion-list-header>\n }\n </ion-list>\n @if (_offline.synchronizing()) {\n <ion-list class=\"aList\">\n <ion-item>\n <ion-label class=\"ion-text-center\">\n <ion-spinner name=\"crescent\" color=\"primary\" />\n </ion-label>\n </ion-item>\n </ion-list>\n }\n @if (_offline.useQueueAPIRequests) {\n <ion-list class=\"aList\">\n <ion-list-header>\n <ion-label>\n <h2>{{ 'IDEA_UNCOMMON.OFFLINE.UPLOAD' | translate }}</h2>\n <p>{{ 'IDEA_UNCOMMON.OFFLINE.UPLOAD_QUEUE_REQUESTS_EXPLANATION' | translate }}</p>\n </ion-label>\n </ion-list-header>\n @if (!_offline.queueAPIRequests.length) {\n <ion-item>\n <i>{{ 'IDEA_UNCOMMON.OFFLINE.NO_ELEMENTS_TO_UPLOAD' | translate }}</i>\n <ion-icon slot=\"end\" size=\"small\" name=\"checkmark\" color=\"success\" />\n </ion-item>\n }\n @for (r of _offline.queueAPIRequests; track r; let odd = $odd) {\n <ion-item [class.oddItem]=\"odd\">\n <ion-label class=\"ion-text-wrap\">\n {{ r.description }}\n @if (r.error) {\n <p>\n <ion-text color=\"danger\">\n <b>{{ 'IDEA_UNCOMMON.OFFLINE.ERROR_CODE' | translate }}: {{ r.error }}</b>\n </ion-text>\n <br />\n {{ 'IDEA_UNCOMMON.OFFLINE.REQUEST_FAILED_EXPLANATION_I' | translate }}<br />\n {{ 'IDEA_UNCOMMON.OFFLINE.REQUEST_FAILED_EXPLANATION_II' | translate }}<br />\n {{ 'IDEA_UNCOMMON.OFFLINE.REQUEST_FAILED_EXPLANATION_III' | translate }}\n </p>\n }\n </ion-label>\n @if (r.error) {\n <ion-button\n fill=\"clear\"\n slot=\"end\"\n color=\"danger\"\n [title]=\"'IDEA_UNCOMMON.OFFLINE.CANCEL_REQUEST' | translate\"\n (click)=\"deleteRequest(r)\"\n >\n <ion-icon slot=\"icon-only\" name=\"trash\" />\n </ion-button>\n }\n @if (r.error) {\n <ion-button\n fill=\"clear\"\n slot=\"end\"\n color=\"medium\"\n [title]=\"'IDEA_UNCOMMON.OFFLINE.DOWNLOAD_LOG' | translate\"\n (click)=\"downloadRequestLog(r)\"\n >\n <ion-icon slot=\"icon-only\" name=\"download\" />\n </ion-button>\n }\n @if (_offline.synchronizing()) {\n <ion-spinner slot=\"end\" name=\"dots\" />\n }\n </ion-item>\n }\n </ion-list>\n }\n @if (_offline.getResources().length) {\n <ion-list class=\"aList\">\n <ion-list-header>\n <ion-label>\n <h2>{{ 'IDEA_UNCOMMON.OFFLINE.DOWNLOAD' | translate }}</h2>\n <p>{{ 'IDEA_UNCOMMON.OFFLINE.DOWNLOAD_RESOURCES_EXPLANATION' | translate }}</p>\n </ion-label>\n </ion-list-header>\n @for (r of _offline.getResources(); track r) {\n <idea-cacheable-resource [resource]=\"_offline.getCacheableResource(r)\" />\n }\n </ion-list>\n }\n</ion-content>\n", styles: [".aList{max-width:500px;margin:0 auto}.aList ion-list-header{padding-right:10px}.aList ion-list-header h3.lastSyncAt{margin-top:6px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IDEACacheableResourceComponent, selector: "idea-cacheable-resource", inputs: ["resource"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonListHeader, selector: "ion-list-header", inputs: ["color", "lines", "mode"] }, { kind: "component", type: IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "pipe", type: IDEATranslatePipe, name: "translate" }, { kind: "pipe", type: IDEALocalizedDatePipe, name: "dateLocale" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3476
3525
  }
3477
3526
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: IDEAOfflineManagerComponent, decorators: [{
3478
3527
  type: Component,
@@ -3494,7 +3543,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
3494
3543
  IonButton,
3495
3544
  IonToolbar,
3496
3545
  IonHeader
3497
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ion-header>\n <ion-toolbar color=\"ideaToolbar\">\n <ion-buttons slot=\"start\">\n <ion-button (click)=\"close()\">\n <ion-icon name=\"close\" slot=\"icon-only\" />\n </ion-button>\n </ion-buttons>\n <ion-title>\n {{ 'IDEA_UNCOMMON.OFFLINE.OFFLINE_MANAGER' | translate }}\n </ion-title>\n <ion-buttons slot=\"end\">\n <ion-button [title]=\"'IDEA_UNCOMMON.OFFLINE.ADVANCED_ACTIONS' | translate\" (click)=\"actions()\">\n <ion-icon slot=\"icon-only\" name=\"ellipsis-vertical\" />\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n<ion-content>\n <ion-list class=\"aList\">\n @if (!_offline.synchronizing && _offline.requiresManualConfirmation) {\n <ion-list-header>\n <ion-label>\n <h2>{{ 'IDEA_UNCOMMON.OFFLINE.HUGE_SYNC_NEED_MANUAL_CONFIRMATION' | translate }}</h2>\n </ion-label>\n </ion-list-header>\n }\n @if (!_offline.synchronizing) {\n <ion-list-header>\n <ion-label class=\"ion-text-wrap\">\n <h3 class=\"lastSyncAt\">\n {{ 'IDEA_UNCOMMON.OFFLINE.LAST_SYNC' | translate }}:\n <ion-text>\n {{\n _offline.lastSyncAt\n ? (_offline.lastSyncAt | dateLocale: 'medium')\n : ('IDEA_UNCOMMON.OFFLINE.NEVER' | translate)\n }}\n </ion-text>\n </h3>\n @if (_offline.errorInLastSync) {\n <p>\n <ion-text color=\"danger\">\n <b>{{ 'IDEA_UNCOMMON.OFFLINE.ERROR_DURING_LAST_SYNC' | translate }}</b>\n </ion-text>\n </p>\n }\n </ion-label>\n <ion-button\n size=\"small\"\n fill=\"solid\"\n [color]=\"_offline.errorInLastSync ? 'danger' : 'dark'\"\n [disabled]=\"_offline.isOffline()\"\n (click)=\"sync()\"\n >\n <ion-icon slot=\"start\" name=\"sync\" /> {{ 'IDEA_UNCOMMON.OFFLINE.SYNC_NOW' | translate }}\n </ion-button>\n </ion-list-header>\n }\n </ion-list>\n @if (_offline.synchronizing) {\n <ion-list class=\"aList\">\n <ion-item>\n <ion-label class=\"ion-text-center\">\n <ion-spinner name=\"crescent\" color=\"primary\" />\n </ion-label>\n </ion-item>\n </ion-list>\n }\n @if (_offline.useQueueAPIRequests) {\n <ion-list class=\"aList\">\n <ion-list-header>\n <ion-label>\n <h2>{{ 'IDEA_UNCOMMON.OFFLINE.UPLOAD' | translate }}</h2>\n <p>{{ 'IDEA_UNCOMMON.OFFLINE.UPLOAD_QUEUE_REQUESTS_EXPLANATION' | translate }}</p>\n </ion-label>\n </ion-list-header>\n @if (!_offline.queueAPIRequests.length) {\n <ion-item>\n <i>{{ 'IDEA_UNCOMMON.OFFLINE.NO_ELEMENTS_TO_UPLOAD' | translate }}</i>\n <ion-icon slot=\"end\" size=\"small\" name=\"checkmark\" color=\"success\" />\n </ion-item>\n }\n @for (r of _offline.queueAPIRequests; track r; let odd = $odd) {\n <ion-item [class.oddItem]=\"odd\">\n <ion-label class=\"ion-text-wrap\">\n {{ r.description }}\n @if (r.error) {\n <p>\n <ion-text color=\"danger\">\n <b>{{ 'IDEA_UNCOMMON.OFFLINE.ERROR_CODE' | translate }}: {{ r.error }}</b>\n </ion-text>\n <br />\n {{ 'IDEA_UNCOMMON.OFFLINE.REQUEST_FAILED_EXPLANATION_I' | translate }}<br />\n {{ 'IDEA_UNCOMMON.OFFLINE.REQUEST_FAILED_EXPLANATION_II' | translate }}<br />\n {{ 'IDEA_UNCOMMON.OFFLINE.REQUEST_FAILED_EXPLANATION_III' | translate }}\n </p>\n }\n </ion-label>\n @if (r.error) {\n <ion-button\n fill=\"clear\"\n slot=\"end\"\n color=\"danger\"\n [title]=\"'IDEA_UNCOMMON.OFFLINE.CANCEL_REQUEST' | translate\"\n (click)=\"deleteRequest(r)\"\n >\n <ion-icon slot=\"icon-only\" name=\"trash\" />\n </ion-button>\n }\n @if (r.error) {\n <ion-button\n fill=\"clear\"\n slot=\"end\"\n color=\"medium\"\n [title]=\"'IDEA_UNCOMMON.OFFLINE.DOWNLOAD_LOG' | translate\"\n (click)=\"downloadRequestLog(r)\"\n >\n <ion-icon slot=\"icon-only\" name=\"download\" />\n </ion-button>\n }\n @if (_offline.synchronizing) {\n <ion-spinner slot=\"end\" name=\"dots\" />\n }\n </ion-item>\n }\n </ion-list>\n }\n @if (_offline.getResources().length) {\n <ion-list class=\"aList\">\n <ion-list-header>\n <ion-label>\n <h2>{{ 'IDEA_UNCOMMON.OFFLINE.DOWNLOAD' | translate }}</h2>\n <p>{{ 'IDEA_UNCOMMON.OFFLINE.DOWNLOAD_RESOURCES_EXPLANATION' | translate }}</p>\n </ion-label>\n </ion-list-header>\n @for (r of _offline.getResources(); track r) {\n <idea-cacheable-resource [resource]=\"_offline.getCacheableResource(r)\" />\n }\n </ion-list>\n }\n</ion-content>\n", styles: [".aList{max-width:500px;margin:0 auto}.aList ion-list-header{padding-right:10px}.aList ion-list-header h3.lastSyncAt{margin-top:6px}\n"] }]
3546
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ion-header>\n <ion-toolbar color=\"ideaToolbar\">\n <ion-buttons slot=\"start\">\n <ion-button (click)=\"close()\">\n <ion-icon name=\"close\" slot=\"icon-only\" />\n </ion-button>\n </ion-buttons>\n <ion-title>\n {{ 'IDEA_UNCOMMON.OFFLINE.OFFLINE_MANAGER' | translate }}\n </ion-title>\n <ion-buttons slot=\"end\">\n <ion-button [title]=\"'IDEA_UNCOMMON.OFFLINE.ADVANCED_ACTIONS' | translate\" (click)=\"actions()\">\n <ion-icon slot=\"icon-only\" name=\"ellipsis-vertical\" />\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n<ion-content>\n <ion-list class=\"aList\">\n @if (!_offline.synchronizing() && _offline.requiresManualConfirmation()) {\n <ion-list-header>\n <ion-label>\n <h2>{{ 'IDEA_UNCOMMON.OFFLINE.HUGE_SYNC_NEED_MANUAL_CONFIRMATION' | translate }}</h2>\n </ion-label>\n </ion-list-header>\n }\n @if (!_offline.synchronizing()) {\n <ion-list-header>\n <ion-label class=\"ion-text-wrap\">\n <h3 class=\"lastSyncAt\">\n {{ 'IDEA_UNCOMMON.OFFLINE.LAST_SYNC' | translate }}:\n <ion-text>\n {{\n _offline.lastSyncAt()\n ? (_offline.lastSyncAt() | dateLocale: 'medium')\n : ('IDEA_UNCOMMON.OFFLINE.NEVER' | translate)\n }}\n </ion-text>\n </h3>\n @if (_offline.errorInLastSync()) {\n <p>\n <ion-text color=\"danger\">\n <b>{{ 'IDEA_UNCOMMON.OFFLINE.ERROR_DURING_LAST_SYNC' | translate }}</b>\n </ion-text>\n </p>\n }\n </ion-label>\n <ion-button\n size=\"small\"\n fill=\"solid\"\n [color]=\"_offline.errorInLastSync() ? 'danger' : 'dark'\"\n [disabled]=\"_offline.isOffline()\"\n (click)=\"sync()\"\n >\n <ion-icon slot=\"start\" name=\"sync\" /> {{ 'IDEA_UNCOMMON.OFFLINE.SYNC_NOW' | translate }}\n </ion-button>\n </ion-list-header>\n }\n </ion-list>\n @if (_offline.synchronizing()) {\n <ion-list class=\"aList\">\n <ion-item>\n <ion-label class=\"ion-text-center\">\n <ion-spinner name=\"crescent\" color=\"primary\" />\n </ion-label>\n </ion-item>\n </ion-list>\n }\n @if (_offline.useQueueAPIRequests) {\n <ion-list class=\"aList\">\n <ion-list-header>\n <ion-label>\n <h2>{{ 'IDEA_UNCOMMON.OFFLINE.UPLOAD' | translate }}</h2>\n <p>{{ 'IDEA_UNCOMMON.OFFLINE.UPLOAD_QUEUE_REQUESTS_EXPLANATION' | translate }}</p>\n </ion-label>\n </ion-list-header>\n @if (!_offline.queueAPIRequests.length) {\n <ion-item>\n <i>{{ 'IDEA_UNCOMMON.OFFLINE.NO_ELEMENTS_TO_UPLOAD' | translate }}</i>\n <ion-icon slot=\"end\" size=\"small\" name=\"checkmark\" color=\"success\" />\n </ion-item>\n }\n @for (r of _offline.queueAPIRequests; track r; let odd = $odd) {\n <ion-item [class.oddItem]=\"odd\">\n <ion-label class=\"ion-text-wrap\">\n {{ r.description }}\n @if (r.error) {\n <p>\n <ion-text color=\"danger\">\n <b>{{ 'IDEA_UNCOMMON.OFFLINE.ERROR_CODE' | translate }}: {{ r.error }}</b>\n </ion-text>\n <br />\n {{ 'IDEA_UNCOMMON.OFFLINE.REQUEST_FAILED_EXPLANATION_I' | translate }}<br />\n {{ 'IDEA_UNCOMMON.OFFLINE.REQUEST_FAILED_EXPLANATION_II' | translate }}<br />\n {{ 'IDEA_UNCOMMON.OFFLINE.REQUEST_FAILED_EXPLANATION_III' | translate }}\n </p>\n }\n </ion-label>\n @if (r.error) {\n <ion-button\n fill=\"clear\"\n slot=\"end\"\n color=\"danger\"\n [title]=\"'IDEA_UNCOMMON.OFFLINE.CANCEL_REQUEST' | translate\"\n (click)=\"deleteRequest(r)\"\n >\n <ion-icon slot=\"icon-only\" name=\"trash\" />\n </ion-button>\n }\n @if (r.error) {\n <ion-button\n fill=\"clear\"\n slot=\"end\"\n color=\"medium\"\n [title]=\"'IDEA_UNCOMMON.OFFLINE.DOWNLOAD_LOG' | translate\"\n (click)=\"downloadRequestLog(r)\"\n >\n <ion-icon slot=\"icon-only\" name=\"download\" />\n </ion-button>\n }\n @if (_offline.synchronizing()) {\n <ion-spinner slot=\"end\" name=\"dots\" />\n }\n </ion-item>\n }\n </ion-list>\n }\n @if (_offline.getResources().length) {\n <ion-list class=\"aList\">\n <ion-list-header>\n <ion-label>\n <h2>{{ 'IDEA_UNCOMMON.OFFLINE.DOWNLOAD' | translate }}</h2>\n <p>{{ 'IDEA_UNCOMMON.OFFLINE.DOWNLOAD_RESOURCES_EXPLANATION' | translate }}</p>\n </ion-label>\n </ion-list-header>\n @for (r of _offline.getResources(); track r) {\n <idea-cacheable-resource [resource]=\"_offline.getCacheableResource(r)\" />\n }\n </ion-list>\n }\n</ion-content>\n", styles: [".aList{max-width:500px;margin:0 auto}.aList ion-list-header{padding-right:10px}.aList ion-list-header h3.lastSyncAt{margin-top:6px}\n"] }]
3498
3547
  }] });
3499
3548
 
3500
3549
  class IDEAOfflineIndicatorComponent {
@@ -3540,7 +3589,8 @@ class IDEAOfflineIndicatorComponent {
3540
3589
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: IDEAOfflineIndicatorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3541
3590
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: IDEAOfflineIndicatorComponent, isStandalone: true, selector: "idea-offline-indicator", inputs: { vertical: { classPropertyName: "vertical", publicName: "vertical", isSignal: true, isRequired: false, transformFunction: null }, horizontal: { classPropertyName: "horizontal", publicName: "horizontal", isSignal: true, isRequired: false, transformFunction: null }, edge: { classPropertyName: "edge", publicName: "edge", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
3542
3591
  @if (
3543
- _platform.is('mobile') && (_offline.isOffline() || _offline.synchronizing || _offline.requiresManualConfirmation)
3592
+ _platform.is('mobile') &&
3593
+ (_offline.isOffline() || _offline.synchronizing() || _offline.requiresManualConfirmation())
3544
3594
  ) {
3545
3595
  <ion-fab [vertical]="vertical()" [horizontal]="horizontal()" [edge]="edge()">
3546
3596
  <ion-fab-button color="dark" size="small" (click)="showStatus()">
@@ -3549,11 +3599,11 @@ class IDEAOfflineIndicatorComponent {
3549
3599
  <ion-icon name="airplane" />
3550
3600
  }
3551
3601
  <!-- SYNCHRONISING -->
3552
- @if (_offline.isOnline() && _offline.synchronizing) {
3602
+ @if (_offline.isOnline() && _offline.synchronizing()) {
3553
3603
  <ion-icon name="sync" />
3554
3604
  }
3555
3605
  <!-- NEED MANAL SYNCHRONISATION -->
3556
- @if (_offline.isOnline() && _offline.requiresManualConfirmation) {
3606
+ @if (_offline.isOnline() && _offline.requiresManualConfirmation()) {
3557
3607
  <ion-icon name="pause" />
3558
3608
  }
3559
3609
  </ion-fab-button>
@@ -3569,7 +3619,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
3569
3619
  changeDetection: ChangeDetectionStrategy.OnPush,
3570
3620
  template: `
3571
3621
  @if (
3572
- _platform.is('mobile') && (_offline.isOffline() || _offline.synchronizing || _offline.requiresManualConfirmation)
3622
+ _platform.is('mobile') &&
3623
+ (_offline.isOffline() || _offline.synchronizing() || _offline.requiresManualConfirmation())
3573
3624
  ) {
3574
3625
  <ion-fab [vertical]="vertical()" [horizontal]="horizontal()" [edge]="edge()">
3575
3626
  <ion-fab-button color="dark" size="small" (click)="showStatus()">
@@ -3578,11 +3629,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
3578
3629
  <ion-icon name="airplane" />
3579
3630
  }
3580
3631
  <!-- SYNCHRONISING -->
3581
- @if (_offline.isOnline() && _offline.synchronizing) {
3632
+ @if (_offline.isOnline() && _offline.synchronizing()) {
3582
3633
  <ion-icon name="sync" />
3583
3634
  }
3584
3635
  <!-- NEED MANAL SYNCHRONISATION -->
3585
- @if (_offline.isOnline() && _offline.requiresManualConfirmation) {
3636
+ @if (_offline.isOnline() && _offline.requiresManualConfirmation()) {
3586
3637
  <ion-icon name="pause" />
3587
3638
  }
3588
3639
  </ion-fab-button>
@@ -3597,8 +3648,12 @@ class IDEASentimentComponent {
3597
3648
  this._env = inject(IDEAEnvironment);
3598
3649
  this._API = inject(IDEAAWSAPIService);
3599
3650
  this._translate = inject(IDEATranslationsService);
3600
- this._cd = inject(ChangeDetectorRef);
3601
3651
  this._offline = inject(IDEAOfflineService);
3652
+ /**
3653
+ * The sentiment detected from the input text.
3654
+ */
3655
+ this.sentiment = signal(null, /* @ts-ignore */
3656
+ ...(ngDevMode ? [{ debugName: "sentiment" }] : /* istanbul ignore next */ []));
3602
3657
  /**
3603
3658
  * The input text.
3604
3659
  */
@@ -3625,24 +3680,23 @@ class IDEASentimentComponent {
3625
3680
  }
3626
3681
  async detectSentiment(text) {
3627
3682
  if (!text || this._offline.isOffline())
3628
- this.sentiment = null;
3683
+ this.sentiment.set(null);
3629
3684
  else {
3630
3685
  try {
3631
3686
  const { sentiment } = await this._API.postResource('sentiment', {
3632
3687
  idea: true,
3633
3688
  body: { project: this._env.idea.project, language: this._translate.getCurrentLang(), text: this.text() }
3634
3689
  });
3635
- this.sentiment = sentiment;
3636
- this.change.emit(this.sentiment);
3690
+ this.sentiment.set(sentiment);
3691
+ this.change.emit(this.sentiment());
3637
3692
  }
3638
3693
  catch (error) {
3639
- this.sentiment = null;
3694
+ this.sentiment.set(null);
3640
3695
  }
3641
3696
  }
3642
- this._cd.markForCheck(); // zoneless: re-check after the awaited detection settles
3643
3697
  }
3644
3698
  getColorBySentiment(sentiment) {
3645
- sentiment = sentiment || this.sentiment;
3699
+ sentiment = sentiment || this.sentiment();
3646
3700
  switch (sentiment) {
3647
3701
  case Sentiment.POSITIVE:
3648
3702
  return 'success';
@@ -3660,17 +3714,17 @@ class IDEASentimentComponent {
3660
3714
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: IDEASentimentComponent, isStandalone: true, selector: "idea-sentiment", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null }, lines: { classPropertyName: "lines", publicName: "lines", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { change: "change" }, usesOnChanges: true, ngImport: i0, template: `
3661
3715
  @if (text() && _offline.isOnline()) {
3662
3716
  <ion-item [lines]="lines()" [color]="color()">
3663
- @if (!sentiment) {
3717
+ @if (!sentiment()) {
3664
3718
  <ion-spinner slot="end" size="small" />
3665
3719
  }
3666
- @if (sentiment) {
3720
+ @if (sentiment()) {
3667
3721
  <ion-badge
3668
3722
  slot="end"
3669
3723
  size="small"
3670
3724
  [color]="getColorBySentiment()"
3671
- [title]="'IDEA_UNCOMMON.SENTIMENT.RESULT_HINT' | translate: { sentiment: sentiment }"
3725
+ [title]="'IDEA_UNCOMMON.SENTIMENT.RESULT_HINT' | translate: { sentiment: sentiment() }"
3672
3726
  >
3673
- {{ 'IDEA_UNCOMMON.SENTIMENT.RESULT.' + sentiment | translate }}
3727
+ {{ 'IDEA_UNCOMMON.SENTIMENT.RESULT.' + sentiment() | translate }}
3674
3728
  </ion-badge>
3675
3729
  }
3676
3730
  </ion-item>
@@ -3686,17 +3740,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
3686
3740
  template: `
3687
3741
  @if (text() && _offline.isOnline()) {
3688
3742
  <ion-item [lines]="lines()" [color]="color()">
3689
- @if (!sentiment) {
3743
+ @if (!sentiment()) {
3690
3744
  <ion-spinner slot="end" size="small" />
3691
3745
  }
3692
- @if (sentiment) {
3746
+ @if (sentiment()) {
3693
3747
  <ion-badge
3694
3748
  slot="end"
3695
3749
  size="small"
3696
3750
  [color]="getColorBySentiment()"
3697
- [title]="'IDEA_UNCOMMON.SENTIMENT.RESULT_HINT' | translate: { sentiment: sentiment }"
3751
+ [title]="'IDEA_UNCOMMON.SENTIMENT.RESULT_HINT' | translate: { sentiment: sentiment() }"
3698
3752
  >
3699
- {{ 'IDEA_UNCOMMON.SENTIMENT.RESULT.' + sentiment | translate }}
3753
+ {{ 'IDEA_UNCOMMON.SENTIMENT.RESULT.' + sentiment() | translate }}
3700
3754
  </ion-badge>
3701
3755
  }
3702
3756
  </ion-item>
@@ -3712,8 +3766,6 @@ class IDEAFromTimeToTimeComponent {
3712
3766
  /**
3713
3767
  * Whether we should start picking the time displaying the afternoon (PM) or the morning (AM, default).
3714
3768
  */
3715
- // TODO: Skipped for migration because:
3716
- // Your application code writes to the input. This prevents migration.
3717
3769
  this.period = Periods.AM;
3718
3770
  this.Segments = Segments;
3719
3771
  this.Periods = Periods;
@@ -3875,7 +3927,6 @@ class IDEATimeIntervalComponent {
3875
3927
  constructor() {
3876
3928
  this._modal = inject(ModalController);
3877
3929
  this._translate = inject(IDEATranslationsService);
3878
- this._cdr = inject(ChangeDetectorRef);
3879
3930
  /**
3880
3931
  * The time interval to set.
3881
3932
  */
@@ -3953,13 +4004,15 @@ class IDEATimeIntervalComponent {
3953
4004
  * On select (with the field disabled) event.
3954
4005
  */
3955
4006
  this.selectWhenDisabled = output();
3956
- this.isOpening = false;
4007
+ this.valueToDisplay = signal('', /* @ts-ignore */
4008
+ ...(ngDevMode ? [{ debugName: "valueToDisplay" }] : /* istanbul ignore next */ []));
4009
+ this.isOpening = signal(false, /* @ts-ignore */
4010
+ ...(ngDevMode ? [{ debugName: "isOpening" }] : /* istanbul ignore next */ []));
3957
4011
  }
3958
4012
  ngOnInit() {
3959
4013
  // when the language changes, set the locale
3960
4014
  this.langChangeSubscription = this._translate.onLangChange.subscribe(() => {
3961
- this.valueToDisplay = this.getValueToDisplay(this.timeInterval());
3962
- this._cdr.markForCheck();
4015
+ this.valueToDisplay.set(this.getValueToDisplay(this.timeInterval()));
3963
4016
  });
3964
4017
  }
3965
4018
  ngOnDestroy() {
@@ -3968,7 +4021,7 @@ class IDEATimeIntervalComponent {
3968
4021
  }
3969
4022
  ngOnChanges(changes) {
3970
4023
  if (changes.timeInterval)
3971
- this.valueToDisplay = this.getValueToDisplay(changes.timeInterval.currentValue);
4024
+ this.valueToDisplay.set(this.getValueToDisplay(changes.timeInterval.currentValue));
3972
4025
  }
3973
4026
  getValueToDisplay(timeInterval) {
3974
4027
  if (!timeInterval || !timeInterval.isSet())
@@ -3978,9 +4031,9 @@ class IDEATimeIntervalComponent {
3978
4031
  return ''.concat(this._translate._('IDEA_UNCOMMON.FTTT.FROM'), ' ', new Date(timeInterval.from).toLocaleTimeString(this._translate.getCurrentLang(), dateOpts), ' ', this._translate._('IDEA_UNCOMMON.FTTT.TO').toLowerCase(), ' ', new Date(timeInterval.to).toLocaleTimeString(this._translate.getCurrentLang(), dateOpts));
3979
4032
  }
3980
4033
  async pickTimeInterval() {
3981
- if (this.isOpening)
4034
+ if (this.isOpening())
3982
4035
  return;
3983
- this.isOpening = true;
4036
+ this.isOpening.set(true);
3984
4037
  const modal = await this._modal.create({
3985
4038
  component: IDEAFromTimeToTimeComponent,
3986
4039
  componentProps: {
@@ -3994,13 +4047,12 @@ class IDEATimeIntervalComponent {
3994
4047
  modal.onDidDismiss().then((res) => {
3995
4048
  // if the content changed, update the internal values and notify the parent component
3996
4049
  if (res.data === true || res.data === false) {
3997
- this.valueToDisplay = this.getValueToDisplay(this.timeInterval());
4050
+ this.valueToDisplay.set(this.getValueToDisplay(this.timeInterval()));
3998
4051
  this.select.emit();
3999
- this._cdr.markForCheck();
4000
4052
  }
4001
4053
  });
4002
4054
  modal.present();
4003
- this.isOpening = false;
4055
+ this.isOpening.set(false);
4004
4056
  }
4005
4057
  doSelectWhenDisabled() {
4006
4058
  if (this.disabled())
@@ -4018,8 +4070,8 @@ class IDEATimeIntervalComponent {
4018
4070
  [color]="color()"
4019
4071
  [lines]="lines()"
4020
4072
  [button]="!disabled()"
4021
- [disabled]="isOpening"
4022
- [title]="placeholder() || valueToDisplay || ''"
4073
+ [disabled]="isOpening()"
4074
+ [title]="placeholder() || valueToDisplay() || ''"
4023
4075
  [class.withLabel]="label()"
4024
4076
  (click)="disabled() ? doSelectWhenDisabled() : pickTimeInterval()"
4025
4077
  >
@@ -4043,12 +4095,12 @@ class IDEATimeIntervalComponent {
4043
4095
  </ion-label>
4044
4096
  }
4045
4097
  <ion-label class="description" [class.selectable]="!disabled() || tappableWhenDisabled()">
4046
- @if (!valueToDisplay && !disabled()) {
4098
+ @if (!valueToDisplay() && !disabled()) {
4047
4099
  <ion-text class="placeholder" [class.selectable]="!disabled()">
4048
4100
  {{ placeholder() }}
4049
4101
  </ion-text>
4050
4102
  }
4051
- {{ valueToDisplay }}
4103
+ {{ valueToDisplay() }}
4052
4104
  </ion-label>
4053
4105
  @if (!disabled()) {
4054
4106
  <ion-icon slot="end" name="caret-down" class="selectIcon" [class.selectable]="!disabled()" />
@@ -4064,8 +4116,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
4064
4116
  [color]="color()"
4065
4117
  [lines]="lines()"
4066
4118
  [button]="!disabled()"
4067
- [disabled]="isOpening"
4068
- [title]="placeholder() || valueToDisplay || ''"
4119
+ [disabled]="isOpening()"
4120
+ [title]="placeholder() || valueToDisplay() || ''"
4069
4121
  [class.withLabel]="label()"
4070
4122
  (click)="disabled() ? doSelectWhenDisabled() : pickTimeInterval()"
4071
4123
  >
@@ -4089,12 +4141,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
4089
4141
  </ion-label>
4090
4142
  }
4091
4143
  <ion-label class="description" [class.selectable]="!disabled() || tappableWhenDisabled()">
4092
- @if (!valueToDisplay && !disabled()) {
4144
+ @if (!valueToDisplay() && !disabled()) {
4093
4145
  <ion-text class="placeholder" [class.selectable]="!disabled()">
4094
4146
  {{ placeholder() }}
4095
4147
  </ion-text>
4096
4148
  }
4097
- {{ valueToDisplay }}
4149
+ {{ valueToDisplay() }}
4098
4150
  </ion-label>
4099
4151
  @if (!disabled()) {
4100
4152
  <ion-icon slot="end" name="caret-down" class="selectIcon" [class.selectable]="!disabled()" />
@@ -4339,6 +4391,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
4339
4391
 
4340
4392
  /**
4341
4393
  * To subscribe push notifications events.
4394
+ *
4395
+ * These are `Observable` streams fed by native Capacitor listeners (outside the Angular zone). On a
4396
+ * zoneless consumer, render any state you derive from them reactively (signals or the `async` pipe);
4397
+ * a plain field assigned inside `.subscribe(...)` won't schedule change detection.
4342
4398
  */
4343
4399
  class IDEAPushNotificationsService {
4344
4400
  constructor() {