valtech-components 4.0.254 → 4.0.255
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/lib/components/organisms/preferences-view/preferences-view.component.mjs +7 -11
- package/esm2022/lib/services/firebase/config.mjs +6 -3
- package/esm2022/lib/services/firebase/messaging.service.mjs +58 -21
- package/esm2022/lib/version.mjs +2 -2
- package/fesm2022/valtech-components.mjs +69 -32
- package/fesm2022/valtech-components.mjs.map +1 -1
- package/lib/components/atoms/rights-footer/rights-footer.component.d.ts +2 -2
- package/lib/components/atoms/text/text.component.d.ts +1 -1
- package/lib/components/atoms/user-avatar/user-avatar.component.d.ts +1 -1
- package/lib/components/molecules/features-list/features-list.component.d.ts +1 -1
- package/lib/components/organisms/article/article.component.d.ts +3 -3
- package/lib/components/organisms/chat-window/chat-window.component.d.ts +1 -1
- package/lib/components/organisms/landing-steps/landing-steps.component.d.ts +1 -1
- package/lib/components/organisms/member-import-modal/member-import-modal.component.d.ts +1 -1
- package/lib/components/organisms/preferences-view/preferences-view.component.d.ts +2 -2
- package/lib/components/organisms/search-header/search-header.component.d.ts +1 -1
- package/lib/services/firebase/messaging.service.d.ts +15 -3
- package/lib/version.d.ts +1 -1
- package/package.json +1 -1
|
@@ -12,7 +12,7 @@ import { provideAnalytics, getAnalytics, Analytics, logEvent, setUserId, setUser
|
|
|
12
12
|
import { provideFirebaseApp, initializeApp } from '@angular/fire/app';
|
|
13
13
|
import * as i1 from '@angular/fire/auth';
|
|
14
14
|
import { provideAuth, getAuth, connectAuthEmulator, authState, signInWithCustomToken, signOut } from '@angular/fire/auth';
|
|
15
|
-
import { provideMessaging, getMessaging
|
|
15
|
+
import { provideMessaging, getMessaging } from '@angular/fire/messaging';
|
|
16
16
|
import * as i1$2 from '@angular/fire/storage';
|
|
17
17
|
import { provideStorage, getStorage, connectStorageEmulator, ref, uploadBytesResumable, getDownloadURL, getMetadata, deleteObject, listAll } from '@angular/fire/storage';
|
|
18
18
|
import { takeUntilDestroyed, toSignal, toObservable } from '@angular/core/rxjs-interop';
|
|
@@ -20,7 +20,7 @@ import * as i1$4 from '@angular/router';
|
|
|
20
20
|
import { Router, NavigationEnd, RouterLink, ActivatedRoute, RouterOutlet, RouterLinkActive, RouterModule } from '@angular/router';
|
|
21
21
|
import * as i2 from '@ionic/angular';
|
|
22
22
|
import { IonicModule, ToastController, NavController } from '@ionic/angular';
|
|
23
|
-
import { isSupported, getMessaging as getMessaging$1 } from 'firebase/messaging';
|
|
23
|
+
import { isSupported, getMessaging as getMessaging$1, getToken, deleteToken, onMessage } from 'firebase/messaging';
|
|
24
24
|
import { getApps, getApp, initializeApp as initializeApp$1 } from 'firebase/app';
|
|
25
25
|
import * as i2$1 from '@ionic/angular/standalone';
|
|
26
26
|
import { AlertController, IonAvatar, IonCard, IonIcon, IonButton, IonSpinner, IonText, IonModal, IonHeader, IonToolbar, IonContent, IonButtons, IonTitle, IonProgressBar, IonSkeletonText, IonFab, IonFabButton, IonFabList, IonLabel, IonCardContent, IonCardHeader, IonCardTitle, IonCardSubtitle, IonCheckbox, IonRadio, IonRadioGroup, IonTextarea, IonDatetime, IonSelect, IonSelectOption, IonPopover, IonList, IonItem, IonInput, IonChip, IonRange, IonSearchbar, IonSegment, IonSegmentButton, IonToggle, IonRippleEffect, IonAccordion, IonAccordionGroup, IonTabBar, IonTabButton, IonBadge, IonBreadcrumb, IonBreadcrumbs, IonInfiniteScroll, IonInfiniteScrollContent, IonNote, IonDatetimeButton, ToastController as ToastController$1, IonCol, IonRow, IonRefresher, IonRefresherContent, IonMenuButton, IonFooter, IonListHeader, IonGrid, MenuController, IonMenu, IonMenuToggle, IonSplitPane } from '@ionic/angular/standalone';
|
|
@@ -57,7 +57,7 @@ import fixWebmDuration from 'fix-webm-duration';
|
|
|
57
57
|
* Current version of valtech-components.
|
|
58
58
|
* This is automatically updated during the publish process.
|
|
59
59
|
*/
|
|
60
|
-
const VERSION = '4.0.
|
|
60
|
+
const VERSION = '4.0.255';
|
|
61
61
|
|
|
62
62
|
// Control de estado de refresco (singleton a nivel de módulo)
|
|
63
63
|
let isRefreshing = false;
|
|
@@ -719,9 +719,12 @@ function provideValtechFirebase(config) {
|
|
|
719
719
|
// Messaging (FCM) - solo si está explícitamente habilitado y estamos en browser.
|
|
720
720
|
// Requiere Service Worker + Notifications API → skip total en SSR/Node.
|
|
721
721
|
if (config.enableMessaging && isBrowser) {
|
|
722
|
-
// Pre-registrar SW custom antes de que Firebase lo intente
|
|
722
|
+
// Pre-registrar SW custom antes de que Firebase lo intente. Usamos un
|
|
723
|
+
// scope dedicado para no competir con Angular `ngsw-worker.js` en `/`.
|
|
723
724
|
if ('serviceWorker' in navigator) {
|
|
724
|
-
navigator.serviceWorker
|
|
725
|
+
navigator.serviceWorker
|
|
726
|
+
.register('/firebase-messaging-sw.js', { scope: '/firebase-cloud-messaging-push-scope' })
|
|
727
|
+
.catch(console.error);
|
|
725
728
|
}
|
|
726
729
|
providers.push(provideMessaging(() => getMessaging()));
|
|
727
730
|
}
|
|
@@ -5399,6 +5402,8 @@ class MessagingService {
|
|
|
5399
5402
|
* Es un *optimistic hint* — la verdad la confirma el siguiente `getToken()`.
|
|
5400
5403
|
*/
|
|
5401
5404
|
this.TOKEN_STORAGE_KEY = 'valtech_fcm_token';
|
|
5405
|
+
this.FCM_SW_URL = '/firebase-messaging-sw.js';
|
|
5406
|
+
this.FCM_SW_SCOPE = '/firebase-cloud-messaging-push-scope';
|
|
5402
5407
|
/**
|
|
5403
5408
|
* Timeout (ms) para `navigator.serviceWorker.ready` dentro de `getToken()`.
|
|
5404
5409
|
*
|
|
@@ -5407,6 +5412,14 @@ class MessagingService {
|
|
|
5407
5412
|
* de colgarse indefinidamente.
|
|
5408
5413
|
*/
|
|
5409
5414
|
this.SW_READY_TIMEOUT_MS = 10_000;
|
|
5415
|
+
/**
|
|
5416
|
+
* Timeout duro para la llamada de Firebase `getToken()`.
|
|
5417
|
+
*
|
|
5418
|
+
* En Chromium puede quedar pendiente sin resolver si la registración FCM no
|
|
5419
|
+
* responde (VAPID/proyecto/API/red). Sin este timeout, el watchdog externo
|
|
5420
|
+
* fuerza un reload que no corrige la causa y deja un diagnóstico pobre.
|
|
5421
|
+
*/
|
|
5422
|
+
this.FCM_GET_TOKEN_TIMEOUT_MS = 15_000;
|
|
5410
5423
|
/**
|
|
5411
5424
|
* Timeout (ms) del watchdog de `enable()` antes de auto-recargar la página.
|
|
5412
5425
|
*
|
|
@@ -5415,10 +5428,10 @@ class MessagingService {
|
|
|
5415
5428
|
* cold load. El timeout de `getToken()` (SW_READY_TIMEOUT_MS) no cubre eso;
|
|
5416
5429
|
* por eso `enable()` envuelve el flujo completo en este watchdog.
|
|
5417
5430
|
*
|
|
5418
|
-
* Un flujo exitoso real llega a token+device en ~4s;
|
|
5431
|
+
* Un flujo exitoso real llega a token+device en ~4s; 25s es holgado y no
|
|
5419
5432
|
* atrapa un éxito lento.
|
|
5420
5433
|
*/
|
|
5421
|
-
this.ENABLE_WATCHDOG_MS =
|
|
5434
|
+
this.ENABLE_WATCHDOG_MS = 25_000;
|
|
5422
5435
|
/**
|
|
5423
5436
|
* Key de sessionStorage — garantiza un solo auto-reload por sesión. Si el
|
|
5424
5437
|
* flujo se cuelga una 2ª vez tras el reload, NO se recarga de nuevo (anti-loop).
|
|
@@ -5444,8 +5457,8 @@ class MessagingService {
|
|
|
5444
5457
|
try {
|
|
5445
5458
|
const app = getApps().length > 0 ? getApp() : initializeApp$1(this.config.firebase);
|
|
5446
5459
|
// Cast a Messaging (token de AngularFire) — el tipo subyacente del SDK
|
|
5447
|
-
// es el mismo. Las funciones getToken/deleteToken/onMessage
|
|
5448
|
-
//
|
|
5460
|
+
// es el mismo. Las funciones getToken/deleteToken/onMessage se llaman
|
|
5461
|
+
// desde `firebase/messaging` directo para evitar wrappers de Zone.
|
|
5449
5462
|
return getMessaging$1(app);
|
|
5450
5463
|
}
|
|
5451
5464
|
catch (err) {
|
|
@@ -5763,10 +5776,10 @@ class MessagingService {
|
|
|
5763
5776
|
// queda listo en SW_READY_TIMEOUT_MS, esta promesa rechaza limpio.
|
|
5764
5777
|
await this.waitForServiceWorkerReady();
|
|
5765
5778
|
console.log('[Messaging] SW ready, calling Firebase getToken()...');
|
|
5766
|
-
const token = await getToken(messaging, {
|
|
5779
|
+
const token = await this.withTimeout(getToken(messaging, {
|
|
5767
5780
|
vapidKey,
|
|
5768
5781
|
serviceWorkerRegistration: registration,
|
|
5769
|
-
});
|
|
5782
|
+
}), this.FCM_GET_TOKEN_TIMEOUT_MS, `[Messaging] Firebase getToken no respondió en ${this.FCM_GET_TOKEN_TIMEOUT_MS / 1000}s`);
|
|
5770
5783
|
console.log('[Messaging] Firebase getToken returned:', token ? `${token.slice(0, 16)}…` : 'null/empty');
|
|
5771
5784
|
this.stateSubject.next({
|
|
5772
5785
|
...this.stateSubject.value,
|
|
@@ -5787,18 +5800,28 @@ class MessagingService {
|
|
|
5787
5800
|
/**
|
|
5788
5801
|
* Resuelve el `ServiceWorkerRegistration` del SW de FCM.
|
|
5789
5802
|
*
|
|
5790
|
-
* El SW
|
|
5791
|
-
*
|
|
5803
|
+
* El SW de FCM ya se registra una vez en el bootstrap de la app (`config.ts`)
|
|
5804
|
+
* con un scope dedicado. Reutilizamos ese registro en lugar de re-registrarlo
|
|
5792
5805
|
* en cada `getToken()` — re-registrar repetidamente dispara revalidaciones del
|
|
5793
5806
|
* SW en iOS PWA. Solo registramos como fallback si todavía no existe.
|
|
5794
5807
|
*/
|
|
5795
5808
|
async resolveServiceWorkerRegistration() {
|
|
5796
|
-
const existing = await
|
|
5809
|
+
const existing = await this.findFcmServiceWorkerRegistration();
|
|
5797
5810
|
if (existing) {
|
|
5811
|
+
console.log('[Messaging] SW registration resolved:', existing.scope, existing.active?.scriptURL || existing.installing?.scriptURL || existing.waiting?.scriptURL);
|
|
5798
5812
|
return existing;
|
|
5799
5813
|
}
|
|
5800
5814
|
console.warn('[Messaging] SW not registered yet, registering as fallback');
|
|
5801
|
-
|
|
5815
|
+
const registration = await navigator.serviceWorker.register(this.FCM_SW_URL, {
|
|
5816
|
+
scope: this.FCM_SW_SCOPE,
|
|
5817
|
+
});
|
|
5818
|
+
console.log('[Messaging] SW registration created:', registration.scope, registration.active?.scriptURL || registration.installing?.scriptURL || registration.waiting?.scriptURL);
|
|
5819
|
+
return registration;
|
|
5820
|
+
}
|
|
5821
|
+
async findFcmServiceWorkerRegistration() {
|
|
5822
|
+
const expectedScope = new URL(this.FCM_SW_SCOPE, window.location.origin).href;
|
|
5823
|
+
const registrations = await navigator.serviceWorker.getRegistrations();
|
|
5824
|
+
return registrations.find(registration => registration.scope === expectedScope);
|
|
5802
5825
|
}
|
|
5803
5826
|
/**
|
|
5804
5827
|
* Espera a que el Service Worker quede activo (`navigator.serviceWorker.ready`)
|
|
@@ -5814,17 +5837,35 @@ class MessagingService {
|
|
|
5814
5837
|
* El timer se limpia en ambas ramas para no dejar timers colgando.
|
|
5815
5838
|
*/
|
|
5816
5839
|
waitForServiceWorkerReady() {
|
|
5840
|
+
return this.withTimeout(this.findFcmServiceWorkerRegistration().then(registration => {
|
|
5841
|
+
if (!registration) {
|
|
5842
|
+
throw new Error('[Messaging] service worker FCM no registrado');
|
|
5843
|
+
}
|
|
5844
|
+
if (registration.active) {
|
|
5845
|
+
return undefined;
|
|
5846
|
+
}
|
|
5847
|
+
return new Promise((resolve, reject) => {
|
|
5848
|
+
const worker = registration.installing || registration.waiting;
|
|
5849
|
+
if (!worker) {
|
|
5850
|
+
reject(new Error('[Messaging] service worker FCM sin worker activo'));
|
|
5851
|
+
return;
|
|
5852
|
+
}
|
|
5853
|
+
worker.addEventListener('statechange', () => {
|
|
5854
|
+
if (worker.state === 'activated')
|
|
5855
|
+
resolve();
|
|
5856
|
+
if (worker.state === 'redundant') {
|
|
5857
|
+
reject(new Error('[Messaging] service worker FCM quedó redundante'));
|
|
5858
|
+
}
|
|
5859
|
+
});
|
|
5860
|
+
});
|
|
5861
|
+
}), this.SW_READY_TIMEOUT_MS, `[Messaging] service worker no quedó listo en ${this.SW_READY_TIMEOUT_MS / 1000}s`);
|
|
5862
|
+
}
|
|
5863
|
+
withTimeout(promise, ms, message) {
|
|
5817
5864
|
let timer;
|
|
5818
|
-
const ready = navigator.serviceWorker.ready.then(() => {
|
|
5819
|
-
if (timer !== undefined)
|
|
5820
|
-
clearTimeout(timer);
|
|
5821
|
-
});
|
|
5822
5865
|
const timeout = new Promise((_, reject) => {
|
|
5823
|
-
timer = setTimeout(() =>
|
|
5824
|
-
reject(new Error(`[Messaging] service worker no quedó listo en ${this.SW_READY_TIMEOUT_MS / 1000}s`));
|
|
5825
|
-
}, this.SW_READY_TIMEOUT_MS);
|
|
5866
|
+
timer = setTimeout(() => reject(new Error(message)), ms);
|
|
5826
5867
|
});
|
|
5827
|
-
return Promise.race([
|
|
5868
|
+
return Promise.race([promise, timeout]).finally(() => {
|
|
5828
5869
|
if (timer !== undefined)
|
|
5829
5870
|
clearTimeout(timer);
|
|
5830
5871
|
});
|
|
@@ -47001,12 +47042,8 @@ class PreferencesViewComponent {
|
|
|
47001
47042
|
this.fontSizeTitle = computed(() => this.t('fontSizeTitle'));
|
|
47002
47043
|
this.fontSizeHint = computed(() => this.t('fontSizeHint'));
|
|
47003
47044
|
this.langProps = computed(() => ({
|
|
47004
|
-
currentLanguage: this.prefs.language(),
|
|
47005
47045
|
availableLanguages: this.resolvedConfig().supportedLanguages,
|
|
47006
|
-
|
|
47007
|
-
disabled: this.saving(),
|
|
47008
|
-
expand: 'block',
|
|
47009
|
-
fill: 'outline',
|
|
47046
|
+
showFlags: false,
|
|
47010
47047
|
}));
|
|
47011
47048
|
this.themePickerProps = computed(() => ({
|
|
47012
47049
|
selectedValue: this.prefs.theme(),
|
|
@@ -47137,7 +47174,7 @@ class PreferencesViewComponent {
|
|
|
47137
47174
|
content: langHint(),
|
|
47138
47175
|
}"
|
|
47139
47176
|
/>
|
|
47140
|
-
<val-language-selector [props]="langProps()" (languageChange)="onLanguageChange($event)" />
|
|
47177
|
+
<val-language-selector-v2 [props]="langProps()" (languageChange)="onLanguageChange($event)" />
|
|
47141
47178
|
</div>
|
|
47142
47179
|
</section>
|
|
47143
47180
|
}
|
|
@@ -47166,14 +47203,14 @@ class PreferencesViewComponent {
|
|
|
47166
47203
|
</section>
|
|
47167
47204
|
}
|
|
47168
47205
|
</div>
|
|
47169
|
-
`, isInline: true, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.settings-section{padding:16px 0}.settings-section+.settings-section{border-top:1px solid var(--val-border-color, rgba(0, 0, 0, .08))}.section-body{display:flex;flex-direction:column;gap:10px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: PopoverSelectorComponent, selector: "val-popover-selector", inputs: ["props"], outputs: ["selectionChange"] }, { kind: "component", type:
|
|
47206
|
+
`, isInline: true, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.settings-section{padding:16px 0}.settings-section+.settings-section{border-top:1px solid var(--val-border-color, rgba(0, 0, 0, .08))}.section-body{display:flex;flex-direction:column;gap:10px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: PopoverSelectorComponent, selector: "val-popover-selector", inputs: ["props"], outputs: ["selectionChange"] }, { kind: "component", type: LanguageSelectorV2Component, selector: "val-language-selector-v2", inputs: ["props"], outputs: ["languageChange"] }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }] }); }
|
|
47170
47207
|
}
|
|
47171
47208
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PreferencesViewComponent, decorators: [{
|
|
47172
47209
|
type: Component,
|
|
47173
47210
|
args: [{ selector: 'val-preferences-view', standalone: true, imports: [
|
|
47174
47211
|
CommonModule,
|
|
47175
47212
|
PopoverSelectorComponent,
|
|
47176
|
-
|
|
47213
|
+
LanguageSelectorV2Component,
|
|
47177
47214
|
DisplayComponent,
|
|
47178
47215
|
TitleComponent,
|
|
47179
47216
|
TextComponent,
|
|
@@ -47234,7 +47271,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
47234
47271
|
content: langHint(),
|
|
47235
47272
|
}"
|
|
47236
47273
|
/>
|
|
47237
|
-
<val-language-selector [props]="langProps()" (languageChange)="onLanguageChange($event)" />
|
|
47274
|
+
<val-language-selector-v2 [props]="langProps()" (languageChange)="onLanguageChange($event)" />
|
|
47238
47275
|
</div>
|
|
47239
47276
|
</section>
|
|
47240
47277
|
}
|