valtech-components 2.0.838 → 2.0.839
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/molecules/update-banner/types.mjs +4 -4
- package/esm2022/lib/components/molecules/update-banner/update-banner.component.mjs +5 -5
- package/esm2022/lib/config/company-footer.config.mjs +10 -3
- package/esm2022/lib/services/firebase/index.mjs +2 -2
- package/esm2022/lib/services/firebase/messaging.service.mjs +212 -3
- package/esm2022/lib/services/firebase/types.mjs +1 -1
- package/esm2022/lib/version.mjs +2 -2
- package/fesm2022/valtech-components.mjs +228 -12
- package/fesm2022/valtech-components.mjs.map +1 -1
- package/lib/components/molecules/update-banner/types.d.ts +3 -3
- package/lib/config/company-footer.config.d.ts +4 -0
- package/lib/services/firebase/index.d.ts +1 -1
- package/lib/services/firebase/messaging.service.d.ts +110 -1
- package/lib/services/firebase/types.d.ts +30 -0
- package/lib/version.d.ts +1 -1
- package/package.json +1 -1
|
@@ -15,7 +15,7 @@ export declare const UPDATE_BANNER_I18N_NAMESPACE = "UpdateBanner";
|
|
|
15
15
|
*/
|
|
16
16
|
export declare const UPDATE_BANNER_DEFAULT_CONTENT: {
|
|
17
17
|
readonly es: {
|
|
18
|
-
readonly availableTitle: "Hay una versión nueva disponible";
|
|
18
|
+
readonly availableTitle: "✨ Hay una versión nueva disponible";
|
|
19
19
|
readonly availableMessage: "Actualiza para obtener las últimas mejoras.";
|
|
20
20
|
readonly requiredTitle: "Debes actualizar para continuar";
|
|
21
21
|
readonly requiredMessage: "Esta versión ya no es compatible. Actualiza para seguir.";
|
|
@@ -23,7 +23,7 @@ export declare const UPDATE_BANNER_DEFAULT_CONTENT: {
|
|
|
23
23
|
readonly dismissAction: "Cerrar";
|
|
24
24
|
};
|
|
25
25
|
readonly en: {
|
|
26
|
-
readonly availableTitle: "A new version is available";
|
|
26
|
+
readonly availableTitle: "✨ A new version is available";
|
|
27
27
|
readonly availableMessage: "Update to get the latest improvements.";
|
|
28
28
|
readonly requiredTitle: "You must update to continue";
|
|
29
29
|
readonly requiredMessage: "This version is no longer supported. Please update.";
|
|
@@ -31,7 +31,7 @@ export declare const UPDATE_BANNER_DEFAULT_CONTENT: {
|
|
|
31
31
|
readonly dismissAction: "Close";
|
|
32
32
|
};
|
|
33
33
|
readonly pt: {
|
|
34
|
-
readonly availableTitle: "Há uma nova versão disponível";
|
|
34
|
+
readonly availableTitle: "✨ Há uma nova versão disponível";
|
|
35
35
|
readonly availableMessage: "Atualize para obter as últimas melhorias.";
|
|
36
36
|
readonly requiredTitle: "Você precisa atualizar para continuar";
|
|
37
37
|
readonly requiredMessage: "Esta versão não é mais compatível. Atualize para seguir.";
|
|
@@ -64,6 +64,8 @@ export declare const VALTECH_FOOTER_I18N: {
|
|
|
64
64
|
aboutUs: string;
|
|
65
65
|
privacyPolicy: string;
|
|
66
66
|
termsConditions: string;
|
|
67
|
+
cookiesPolicy: string;
|
|
68
|
+
legalNotice: string;
|
|
67
69
|
contactSupport: string;
|
|
68
70
|
faq: string;
|
|
69
71
|
feedback: string;
|
|
@@ -75,6 +77,8 @@ export declare const VALTECH_FOOTER_I18N: {
|
|
|
75
77
|
aboutUs: string;
|
|
76
78
|
privacyPolicy: string;
|
|
77
79
|
termsConditions: string;
|
|
80
|
+
cookiesPolicy: string;
|
|
81
|
+
legalNotice: string;
|
|
78
82
|
contactSupport: string;
|
|
79
83
|
faq: string;
|
|
80
84
|
feedback: string;
|
|
@@ -36,7 +36,7 @@ export { CollectionOptions, FirestoreCollection, FirestoreCollectionFactory, Sub
|
|
|
36
36
|
export { QueryBuilder, query } from './utils/query-builder';
|
|
37
37
|
export { buildPath, extractPathParams, getCollectionPath, getDocumentId, isCollectionPath, isDocumentPath, isValidPath, joinPath, } from './utils/path-builder';
|
|
38
38
|
export { StorageService } from './storage.service';
|
|
39
|
-
export { MessagingService } from './messaging.service';
|
|
39
|
+
export { MessagingService, type EnablePushOptions, type RegisterDeviceFn, } from './messaging.service';
|
|
40
40
|
export { NotificationDocument, NotificationsService } from './notifications.service';
|
|
41
41
|
export { AnalyticsService } from './analytics.service';
|
|
42
42
|
export { AnalyticsRouterTracker } from './analytics-router-tracker';
|
|
@@ -7,8 +7,28 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { Injector, NgZone } from '@angular/core';
|
|
9
9
|
import { Observable } from 'rxjs';
|
|
10
|
-
import { NotificationAction, NotificationClickEvent, NotificationPayload, NotificationPermission, ValtechFirebaseConfig } from './types';
|
|
10
|
+
import { EnablePushResult, NotificationAction, NotificationClickEvent, NotificationPayload, NotificationPermission, ValtechFirebaseConfig } from './types';
|
|
11
11
|
import * as i0 from "@angular/core";
|
|
12
|
+
/**
|
|
13
|
+
* Callback opcional de registro de device para `MessagingService.enable()`.
|
|
14
|
+
*
|
|
15
|
+
* El registro del device vive en `AuthService` (necesita el JWT + el endpoint
|
|
16
|
+
* del backend). `MessagingService` NO puede depender de `AuthService` sin
|
|
17
|
+
* crear un ciclo (AuthService → MessagingService). Por eso el caller le pasa
|
|
18
|
+
* el paso de registro como callback. Recibe el token FCM y devuelve si el
|
|
19
|
+
* device quedó registrado.
|
|
20
|
+
*/
|
|
21
|
+
export type RegisterDeviceFn = (token: string) => Promise<boolean>;
|
|
22
|
+
/**
|
|
23
|
+
* Opciones de `MessagingService.enable()`.
|
|
24
|
+
*/
|
|
25
|
+
export interface EnablePushOptions {
|
|
26
|
+
/**
|
|
27
|
+
* Paso opcional de registro de device en el backend. Si se omite, `enable()`
|
|
28
|
+
* resuelve con `status: 'enabled'` apenas obtiene el token (sin registrar).
|
|
29
|
+
*/
|
|
30
|
+
registerDevice?: RegisterDeviceFn;
|
|
31
|
+
}
|
|
12
32
|
/**
|
|
13
33
|
* Estado interno del servicio de messaging
|
|
14
34
|
*/
|
|
@@ -69,6 +89,31 @@ export declare class MessagingService {
|
|
|
69
89
|
* Es un *optimistic hint* — la verdad la confirma el siguiente `getToken()`.
|
|
70
90
|
*/
|
|
71
91
|
private readonly TOKEN_STORAGE_KEY;
|
|
92
|
+
/**
|
|
93
|
+
* Timeout (ms) para `navigator.serviceWorker.ready` dentro de `getToken()`.
|
|
94
|
+
*
|
|
95
|
+
* En un cold load el SW puede no estar activado aún y `serviceWorker.ready`
|
|
96
|
+
* no resolver nunca. Pasado este tiempo, `getToken()` rechaza limpio en lugar
|
|
97
|
+
* de colgarse indefinidamente.
|
|
98
|
+
*/
|
|
99
|
+
private readonly SW_READY_TIMEOUT_MS;
|
|
100
|
+
/**
|
|
101
|
+
* Timeout (ms) del watchdog de `enable()` antes de auto-recargar la página.
|
|
102
|
+
*
|
|
103
|
+
* El flujo de activación a veces se cuelga ANTES de `getToken()` — caso
|
|
104
|
+
* típico: `Notification.requestPermission()` no muestra el popup del SO en un
|
|
105
|
+
* cold load. El timeout de `getToken()` (SW_READY_TIMEOUT_MS) no cubre eso;
|
|
106
|
+
* por eso `enable()` envuelve el flujo completo en este watchdog.
|
|
107
|
+
*
|
|
108
|
+
* Un flujo exitoso real llega a token+device en ~4s; 15s es holgado y no
|
|
109
|
+
* atrapa un éxito lento.
|
|
110
|
+
*/
|
|
111
|
+
private readonly ENABLE_WATCHDOG_MS;
|
|
112
|
+
/**
|
|
113
|
+
* Key de sessionStorage — garantiza un solo auto-reload por sesión. Si el
|
|
114
|
+
* flujo se cuelga una 2ª vez tras el reload, NO se recarga de nuevo (anti-loop).
|
|
115
|
+
*/
|
|
116
|
+
private readonly AUTORELOAD_FLAG;
|
|
72
117
|
constructor(injector: Injector, config: ValtechFirebaseConfig, platformId: Object, ngZone: NgZone);
|
|
73
118
|
/**
|
|
74
119
|
* Obtiene la instancia de Messaging via Firebase SDK directo (NO AngularFire DI).
|
|
@@ -122,6 +167,56 @@ export declare class MessagingService {
|
|
|
122
167
|
* ```
|
|
123
168
|
*/
|
|
124
169
|
requestPermission(): Promise<string | null>;
|
|
170
|
+
/**
|
|
171
|
+
* Flujo completo de activación de push, robusto, cross-app.
|
|
172
|
+
*
|
|
173
|
+
* Orquesta: pedir permiso → SW ready → obtener token FCM → (opcional)
|
|
174
|
+
* registrar el device en backend. Es el punto de orquestación único que cada
|
|
175
|
+
* app del factory consume — la lógica de robustez vive aquí, no en cada página.
|
|
176
|
+
*
|
|
177
|
+
* **Watchdog de auto-reload.** El flujo a veces se cuelga ANTES de `getToken()`
|
|
178
|
+
* (ej. `Notification.requestPermission()` que no muestra el popup en un cold
|
|
179
|
+
* load) — el timeout interno de `getToken()` no cubre ese caso. Por eso
|
|
180
|
+
* `enable()` envuelve el flujo entero en un watchdog: si no alcanza un estado
|
|
181
|
+
* terminal en `ENABLE_WATCHDOG_MS` (15s):
|
|
182
|
+
* - 1ª vez en la sesión → marca un flag en `sessionStorage` y hace
|
|
183
|
+
* `window.location.reload()` (un fresh load suele tener el SW activo).
|
|
184
|
+
* Resuelve con `{ status: 'timeout', reloaded: true }`.
|
|
185
|
+
* - ya se auto-recargó antes → NO recarga (anti-loop): limpia el flag y
|
|
186
|
+
* resuelve con `{ status: 'timeout', reloaded: false }` para que la app
|
|
187
|
+
* muestre un error.
|
|
188
|
+
*
|
|
189
|
+
* NO hace throw — siempre resuelve con un `EnablePushResult` descriptivo que
|
|
190
|
+
* la página consumidora inspecciona para decidir qué toast mostrar.
|
|
191
|
+
*
|
|
192
|
+
* @param options.registerDevice Callback opcional que registra el device en
|
|
193
|
+
* el backend (vive en `AuthService` — se pasa como callback para evitar el
|
|
194
|
+
* ciclo de DI AuthService ↔ MessagingService).
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```typescript
|
|
198
|
+
* const result = await messaging.enable({
|
|
199
|
+
* registerDevice: (token) => auth.registerDevice(token).then(r => r.registered),
|
|
200
|
+
* });
|
|
201
|
+
* if (result.status === 'enabled') {
|
|
202
|
+
* // result.token disponible — push activo
|
|
203
|
+
* } else if (result.status === 'timeout' && !result.reloaded) {
|
|
204
|
+
* // mostrar error: el flujo se colgó y ya se consumió el auto-reload
|
|
205
|
+
* }
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
enable(options?: EnablePushOptions): Promise<EnablePushResult>;
|
|
209
|
+
/**
|
|
210
|
+
* Flujo real de activación, sin el watchdog (lo envuelve `enable()`).
|
|
211
|
+
* Resuelve siempre con un `EnablePushResult` — no hace throw.
|
|
212
|
+
*/
|
|
213
|
+
private runEnableFlow;
|
|
214
|
+
/** True si ya se auto-recargó una vez en esta sesión. */
|
|
215
|
+
private hasAutoReloaded;
|
|
216
|
+
/** Marca que se consumió el auto-reload de esta sesión. */
|
|
217
|
+
private markAutoReloaded;
|
|
218
|
+
/** Limpia el flag anti-loop — tras un éxito o un fallo definitivo. */
|
|
219
|
+
private clearAutoReloadFlag;
|
|
125
220
|
/**
|
|
126
221
|
* Obtiene el token FCM actual (sin solicitar permiso).
|
|
127
222
|
*
|
|
@@ -142,6 +237,20 @@ export declare class MessagingService {
|
|
|
142
237
|
* SW en iOS PWA. Solo registramos como fallback si todavía no existe.
|
|
143
238
|
*/
|
|
144
239
|
private resolveServiceWorkerRegistration;
|
|
240
|
+
/**
|
|
241
|
+
* Espera a que el Service Worker quede activo (`navigator.serviceWorker.ready`)
|
|
242
|
+
* pero con un timeout duro.
|
|
243
|
+
*
|
|
244
|
+
* `navigator.serviceWorker.ready` resuelve cuando hay un SW *activo*. En un
|
|
245
|
+
* cold load (primera visita, SW recién registrado, iOS PWA recién abierta) el
|
|
246
|
+
* SW puede quedar en estado `installing`/`waiting` y `ready` no resolver nunca
|
|
247
|
+
* → `getToken()` se cuelga. El `Promise.race` contra un `setTimeout` garantiza
|
|
248
|
+
* que esta espera termina: o gana `ready` (caso normal) o gana el timeout y
|
|
249
|
+
* lanzamos un error claro para que `getToken()` rechace en vez de colgarse.
|
|
250
|
+
*
|
|
251
|
+
* El timer se limpia en ambas ramas para no dejar timers colgando.
|
|
252
|
+
*/
|
|
253
|
+
private waitForServiceWorkerReady;
|
|
145
254
|
/**
|
|
146
255
|
* Persiste el token FCM en localStorage (o lo limpia si es null/empty).
|
|
147
256
|
*/
|
|
@@ -346,6 +346,36 @@ export interface NotificationAction {
|
|
|
346
346
|
/** Datos adicionales para la acción */
|
|
347
347
|
actionData?: Record<string, unknown>;
|
|
348
348
|
}
|
|
349
|
+
/**
|
|
350
|
+
* Resultado de `MessagingService.enable()`.
|
|
351
|
+
*
|
|
352
|
+
* Tipo discriminado descriptivo (NO se hace throw): la página consumidora lo
|
|
353
|
+
* inspecciona para decidir qué toast/UX mostrar.
|
|
354
|
+
*
|
|
355
|
+
* `status`:
|
|
356
|
+
* - `enabled` — permiso otorgado + token FCM obtenido (`token` presente).
|
|
357
|
+
* - `denied` — el usuario denegó el permiso del navegador.
|
|
358
|
+
* - `unsupported` — el navegador no soporta FCM (incl. iOS sin standalone).
|
|
359
|
+
* - `error` — el flujo falló por otra causa (ver `reason`).
|
|
360
|
+
* - `timeout` — el flujo se colgó >15s. Si `reloaded` es true, la página
|
|
361
|
+
* está por recargarse (auto-reload, 1ª vez en la sesión); si
|
|
362
|
+
* es false, ya se consumió el auto-reload y la página debe
|
|
363
|
+
* mostrar un error sin recargar.
|
|
364
|
+
*/
|
|
365
|
+
export interface EnablePushResult {
|
|
366
|
+
/** Resultado del flujo de activación de push. */
|
|
367
|
+
status: 'enabled' | 'denied' | 'unsupported' | 'error' | 'timeout';
|
|
368
|
+
/** Token FCM — presente solo cuando `status === 'enabled'`. */
|
|
369
|
+
token?: string;
|
|
370
|
+
/** Detalle legible de la causa — presente en `error` / `timeout`. */
|
|
371
|
+
reason?: string;
|
|
372
|
+
/**
|
|
373
|
+
* Solo para `status === 'timeout'`: true si se disparó el auto-reload
|
|
374
|
+
* (la página está por recargarse). False si el auto-reload ya se consumió
|
|
375
|
+
* esta sesión y la app debe mostrar un error en lugar de recargar.
|
|
376
|
+
*/
|
|
377
|
+
reloaded?: boolean;
|
|
378
|
+
}
|
|
349
379
|
/**
|
|
350
380
|
* Evento de click en una notificación
|
|
351
381
|
*/
|
package/lib/version.d.ts
CHANGED