valtech-components 2.0.803 → 2.0.805

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.
@@ -0,0 +1,33 @@
1
+ import { EnvironmentProviders, InjectionToken } from '@angular/core';
2
+ import { ValtechDonationConfig } from './types';
3
+ /**
4
+ * Token de inyección para la configuración de Donation/Support.
5
+ */
6
+ export declare const VALTECH_DONATION_CONFIG: InjectionToken<ValtechDonationConfig>;
7
+ /** Configuración por defecto — sin métodos habilitados. */
8
+ export declare const DEFAULT_DONATION_CONFIG: Partial<ValtechDonationConfig>;
9
+ /**
10
+ * Provee el feature de aportes (Support) a la aplicación Angular.
11
+ *
12
+ * Cada app del factory declara qué métodos habilita. La vista de Support
13
+ * (heredada o local) renderiza solo los métodos configurados.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * // main.ts
18
+ * provideValtechDonations({
19
+ * appId: 'showcase',
20
+ * methods: ['coffee', 'bank', 'ads'],
21
+ * coffee: { provider: 'buymeacoffee', url: 'https://buymeacoffee.com/valtech' },
22
+ * bank: {
23
+ * accounts: [{
24
+ * country: 'CL', bank: 'Banco X', accountType: 'Cuenta Corriente',
25
+ * number: '000000000', taxId: '11.111.111-1', holder: 'Valtech SpA',
26
+ * email: 'aportes@valtech.com', currency: 'CLP',
27
+ * }],
28
+ * },
29
+ * ads: { provider: 'admob', rewardedUnitId: 'ca-app-pub-xxx' },
30
+ * }),
31
+ * ```
32
+ */
33
+ export declare function provideValtechDonations(config: ValtechDonationConfig): EnvironmentProviders;
@@ -0,0 +1,54 @@
1
+ import { BankAccount, DonationActionResult, DonationMethod } from './types';
2
+ import * as i0 from "@angular/core";
3
+ /**
4
+ * `DonationService`
5
+ *
6
+ * Servicio cross-app para el feature de aportes voluntarios (Support).
7
+ * Patrón factory: cada app llama `provideValtechDonations(...)` y este
8
+ * servicio expone solo los métodos habilitados.
9
+ *
10
+ * **Fase 0 (placeholder)** — abre links externos + expone datos de config.
11
+ * Sin backend. Fases futuras: rewarded ads reales, webhook de café,
12
+ * registro de intents para una página de transparencia.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * private donations = inject(DonationService);
17
+ *
18
+ * methods = this.donations.enabledMethods(); // ['coffee', 'bank']
19
+ * onCoffee() { this.donations.openCoffee(); }
20
+ * accounts = this.donations.bankAccounts();
21
+ * ```
22
+ */
23
+ export declare class DonationService {
24
+ private readonly config;
25
+ /** Último intent registrado — útil para tests / debugging / UI feedback. */
26
+ private readonly _lastIntent;
27
+ readonly lastIntent: import("@angular/core").Signal<DonationMethod>;
28
+ /** Métodos habilitados por la app, en orden de config. */
29
+ readonly enabledMethods: import("@angular/core").Signal<DonationMethod[]>;
30
+ /** `true` si el método está habilitado en la config de la app. */
31
+ isEnabled(method: DonationMethod): boolean;
32
+ /** Cuentas bancarias configuradas (vacío si `bank` no está habilitado). */
33
+ bankAccounts(): BankAccount[];
34
+ /** Cuentas filtradas por país ISO — para apps multi-mercado. */
35
+ bankAccountsByCountry(country: string): BankAccount[];
36
+ /**
37
+ * Abre el checkout de "café" (Buy Me a Coffee / Ko-fi) en una pestaña nueva.
38
+ * El proveedor es el merchant of record — Valtech no procesa el pago.
39
+ */
40
+ openCoffee(): DonationActionResult;
41
+ /**
42
+ * Muestra un anuncio rewarded (opt-in). **Fase 0: no implementado** —
43
+ * placeholder hasta integrar el plugin de ads (AdMob/AdSense) + la
44
+ * categoría de consentimiento. Retorna `not-supported`.
45
+ */
46
+ showRewardedAd(): Promise<DonationActionResult>;
47
+ /**
48
+ * Registra la intención de aporte. **Fase 0: solo estado local.**
49
+ * Fase 4 — POST a un endpoint para una página de transparencia.
50
+ */
51
+ recordIntent(method: DonationMethod): void;
52
+ static ɵfac: i0.ɵɵFactoryDeclaration<DonationService, never>;
53
+ static ɵprov: i0.ɵɵInjectableDeclaration<DonationService>;
54
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Valtech Donation / Support Service
3
+ *
4
+ * Feature cross-app de aportes voluntarios. Patrón factory: cada app llama
5
+ * `provideValtechDonations(...)` y habilita los métodos que quiera
6
+ * (`ads` · `coffee` · `bank`).
7
+ *
8
+ * Importante (legal): el dinero recibido por una entidad con fines de lucro
9
+ * es ingreso afecto a impuesto, NO una donación deducible. La UI debe usar
10
+ * lenguaje de "aporte voluntario / apoyo", nunca "donación".
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * // main.ts
15
+ * provideValtechDonations({
16
+ * appId: 'showcase',
17
+ * methods: ['coffee', 'bank'],
18
+ * coffee: { provider: 'buymeacoffee', url: 'https://buymeacoffee.com/valtech' },
19
+ * bank: { accounts: [ ... ] },
20
+ * }),
21
+ * ```
22
+ */
23
+ export { VALTECH_DONATION_CONFIG, provideValtechDonations, DEFAULT_DONATION_CONFIG, } from './config';
24
+ export { DonationService } from './donation.service';
25
+ export { DonationMethod, CoffeeProvider, AdsProvider, BankAccount, CoffeeConfig, AdsConfig, ValtechDonationConfig, DonationActionResult, } from './types';
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Valtech Donation / Support — tipos.
3
+ *
4
+ * Nota legal importante: el dinero recibido por una entidad con fines de lucro
5
+ * NO es una "donación" deducible de impuestos — es ingreso afecto. Por eso la
6
+ * UI debe usar lenguaje de "aporte voluntario / apoyo", nunca "donación" en su
7
+ * sentido jurídico. Ver `docs/` (pendiente) y la vista Support del consumer.
8
+ */
9
+ /**
10
+ * Métodos de aporte soportados.
11
+ * - `ads` — el usuario ve publicidad rewarded (opt-in) para apoyar.
12
+ * - `coffee` — pago único vía proveedor externo (Buy Me a Coffee / Ko-fi).
13
+ * - `bank` — transferencia bancaria directa (se muestran los datos).
14
+ */
15
+ export type DonationMethod = 'ads' | 'coffee' | 'bank';
16
+ /** Proveedor del método "café". El merchant of record es el proveedor. */
17
+ export type CoffeeProvider = 'buymeacoffee' | 'kofi' | 'custom';
18
+ /** Proveedor de publicidad rewarded. */
19
+ export type AdsProvider = 'admob' | 'adsense' | 'custom';
20
+ /**
21
+ * Datos de una cuenta bancaria para transferencia. Pensado para ser
22
+ * por-país (el modelo factory es 1 producto = 1 dominio, con datos
23
+ * fiscales/bancarios distintos por mercado).
24
+ */
25
+ export interface BankAccount {
26
+ /** ISO country code — ej. 'CL', 'AR', 'MX'. */
27
+ country: string;
28
+ /** Nombre del banco. */
29
+ bank: string;
30
+ /** Tipo de cuenta — ej. 'Cuenta Corriente', 'Vista', 'Checking'. */
31
+ accountType: string;
32
+ /** Número de cuenta / IBAN / CBU. */
33
+ number: string;
34
+ /** Identificador fiscal del titular — RUT, CUIT, RFC, etc. */
35
+ taxId: string;
36
+ /** Nombre del titular de la cuenta. */
37
+ holder: string;
38
+ /** Email para enviar comprobante de transferencia. */
39
+ email?: string;
40
+ /** Moneda — ej. 'CLP', 'USD'. */
41
+ currency?: string;
42
+ }
43
+ /** Config del método "café". */
44
+ export interface CoffeeConfig {
45
+ provider: CoffeeProvider;
46
+ /** URL pública del perfil (BMC/Ko-fi) o checkout custom. */
47
+ url: string;
48
+ }
49
+ /** Config del método "ads" (rewarded, opt-in). */
50
+ export interface AdsConfig {
51
+ provider: AdsProvider;
52
+ /** ID de la unidad de anuncio rewarded. */
53
+ rewardedUnitId?: string;
54
+ }
55
+ /**
56
+ * Configuración del feature Donation/Support.
57
+ *
58
+ * Cada app del factory declara qué métodos habilita. Una vista heredada
59
+ * (`val-*` page) renderiza solo los métodos en `methods`.
60
+ */
61
+ export interface ValtechDonationConfig {
62
+ /** AppID — para tracking de intents (ej. analytics). */
63
+ appId?: string;
64
+ /** Métodos habilitados, en orden de aparición en la UI. */
65
+ methods: DonationMethod[];
66
+ /** Config del método café — requerido si `methods` incluye `'coffee'`. */
67
+ coffee?: CoffeeConfig;
68
+ /** Cuentas bancarias — requerido si `methods` incluye `'bank'`. */
69
+ bank?: {
70
+ accounts: BankAccount[];
71
+ };
72
+ /** Config de ads — requerido si `methods` incluye `'ads'`. */
73
+ ads?: AdsConfig;
74
+ }
75
+ /**
76
+ * Resultado de un intento de aporte. Devuelto por los métodos del servicio
77
+ * de forma descriptiva (no se lanzan excepciones para flujos esperables).
78
+ */
79
+ export interface DonationActionResult {
80
+ method: DonationMethod;
81
+ /** `true` si la acción se inició correctamente. */
82
+ ok: boolean;
83
+ /** Motivo cuando `ok` es `false` — ej. 'not-configured', 'not-supported'. */
84
+ reason?: string;
85
+ }
@@ -1,3 +1,11 @@
1
+ /**
2
+ * Firebase Service
3
+ *
4
+ * Servicio principal para la autenticación con Firebase usando Custom Tokens.
5
+ * Permite que usuarios autenticados con tu backend (Cognito, etc.) accedan
6
+ * a servicios de Firebase (Firestore, Storage, FCM) de manera segura.
7
+ */
8
+ import { Signal } from '@angular/core';
1
9
  import { Auth, UserCredential } from '@angular/fire/auth';
2
10
  import { Observable } from 'rxjs';
3
11
  import { FirebaseUser, MembershipInfo, OrganizationInfo, SessionState, ValtechFirebaseConfig } from './types';
@@ -46,7 +54,51 @@ export declare class FirebaseService {
46
54
  readonly user$: Observable<FirebaseUser | null>;
47
55
  /** Indica si el usuario está autenticado en Firebase */
48
56
  readonly isAuthenticated$: Observable<boolean>;
57
+ /**
58
+ * Signal interna que respalda `firebaseAuthReady`.
59
+ * `true` cuando hay un `firebase.User` activo y por tanto las reglas de
60
+ * Firestore evaluarán `request.auth != null` correctamente.
61
+ */
62
+ private readonly _firebaseAuthReady;
63
+ /**
64
+ * Indica si la sesión de **Firebase Auth** está establecida y lista para
65
+ * leer Firestore.
66
+ *
67
+ * IMPORTANTE: esto es distinto del JWT del backend. La sesión de Firebase
68
+ * Auth es una sesión separada que se establece vía `signInWithCustomToken`
69
+ * (ver `AuthService.signInWithFirebase`). En cold start de PWA, el JWT del
70
+ * backend puede estar listo varios cientos de ms antes de que Firebase Auth
71
+ * confirme su `User` — adjuntar un listener de Firestore en esa ventana
72
+ * produce `permission-denied`.
73
+ *
74
+ * Usar este signal (o `whenFirebaseAuthReady()`) como gate antes de
75
+ * suscribirse a cualquier query/listener de Firestore.
76
+ *
77
+ * @example
78
+ * ```typescript
79
+ * if (this.firebase.firebaseAuthReady()) {
80
+ * // seguro leer Firestore
81
+ * }
82
+ * ```
83
+ */
84
+ readonly firebaseAuthReady: Signal<boolean>;
85
+ /**
86
+ * Emite `true` una sola vez en cuanto la sesión de Firebase Auth está lista,
87
+ * y completa. Si ya está lista, emite inmediatamente.
88
+ *
89
+ * Pensado como gate compartido para abrir streams Firestore — espera la
90
+ * ventana de hidratación de Firebase Auth sin necesidad de retries.
91
+ */
92
+ readonly firebaseAuthReady$: Observable<boolean>;
49
93
  constructor(auth: Auth, config: ValtechFirebaseConfig);
94
+ /**
95
+ * Resuelve en cuanto la sesión de Firebase Auth está lista para leer
96
+ * Firestore. Si ya está lista, resuelve inmediatamente.
97
+ *
98
+ * Útil para gatear la primera suscripción a un listener de Firestore y así
99
+ * cerrar la ventana de `permission-denied` en cold start.
100
+ */
101
+ whenFirebaseAuthReady(): Promise<boolean>;
50
102
  /**
51
103
  * Autentica al usuario con un Custom Token generado por el backend.
52
104
  *
@@ -64,7 +64,16 @@ export declare class NotificationsService {
64
64
  private currentUserId;
65
65
  private collectionReady$;
66
66
  private authService;
67
+ private firebaseService;
67
68
  constructor(injector: Injector, collectionFactory: FirestoreCollectionFactory);
69
+ /**
70
+ * Gate de Firebase Auth: emite la colección lista SOLO cuando la sesión de
71
+ * Firebase Auth está confirmada. Sin FirebaseService disponible, no gatea
72
+ * (degrada al comportamiento previo). Espera a que `firebaseAuthReady$`
73
+ * emita antes de propagar la colección — así el listener Firestore nunca se
74
+ * adjunta antes de que `request.auth` esté disponible.
75
+ */
76
+ private collectionWhenAuthReady$;
68
77
  /**
69
78
  * Configura auto-inicialización observando el estado de AuthService.
70
79
  * Se ejecuta en el contexto del injector para poder usar effect().
@@ -90,6 +99,11 @@ export declare class NotificationsService {
90
99
  * Obtiene notificaciones ordenadas por fecha descendente (real-time).
91
100
  * Se actualiza automáticamente cuando cambian los datos.
92
101
  *
102
+ * El listener Firestore NO se adjunta hasta que la sesión de Firebase Auth
103
+ * esté confirmada (`FirebaseService.firebaseAuthReady`). Esto cierra la
104
+ * ventana de `permission-denied` en cold start de PWA, donde el JWT del
105
+ * backend puede estar listo antes que la sesión de Firebase Auth.
106
+ *
93
107
  * @param limit - Máximo de notificaciones a cargar (default: 50)
94
108
  */
95
109
  getAll(limit?: number): Observable<NotificationDocument[]>;
@@ -109,6 +123,9 @@ export declare class NotificationsService {
109
123
  /**
110
124
  * Cuenta notificaciones no leídas usando server-side aggregation query.
111
125
  * No descarga documentos — eficiente para badges en UI.
126
+ *
127
+ * Gateado por `firebaseAuthReady` — la aggregation query no se ejecuta hasta
128
+ * que la sesión de Firebase Auth esté lista.
112
129
  */
113
130
  getUnreadCount(): Observable<number>;
114
131
  /**
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Refreshable Stream
3
+ *
4
+ * Helper `createRefreshableStream` para vistas con datos Firestore.
5
+ * Soporta modo one-shot y real-time, con gate de `firebaseAuthReady`.
6
+ */
7
+ export { createRefreshableStream, type RefreshableStream, type RefreshableStreamOptions, } from './refreshable-stream';
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Refreshable Stream
3
+ *
4
+ * Helper estándar para vistas que consumen datos de Firestore. Soporta DOS
5
+ * modos — el caller decide cuál vía la `factory`:
6
+ *
7
+ * - **one-shot** (recomendado por defecto): `() => from(svc.getAllOnce())`.
8
+ * Carga una vez y completa. Más barato — sin listener vivo.
9
+ * - **real-time**: `() => svc.getAll()`. Listener Firestore que auto-actualiza.
10
+ * Solo donde importa (inbox de notificaciones, badges).
11
+ *
12
+ * En ambos modos la primera suscripción se gatea por `firebaseAuthReady`
13
+ * (sesión de Firebase Auth lista) — así el listener/lectura nunca se adjunta
14
+ * antes de que `request.auth` esté disponible en las reglas de Firestore, lo
15
+ * que cierra la ventana de `permission-denied` en cold start de PWA.
16
+ *
17
+ * El `retry` con backoff corto es solo red de seguridad para reconexiones
18
+ * transitorias (no para cubrir cold start — eso lo cubre el gate de auth).
19
+ */
20
+ import { Injector, Signal } from '@angular/core';
21
+ import { Observable } from 'rxjs';
22
+ /** Opciones de configuración para `createRefreshableStream`. */
23
+ export interface RefreshableStreamOptions<T> {
24
+ /**
25
+ * Valor emitido cuando la factory falla tras agotar los reintentos.
26
+ * Default: `null`.
27
+ */
28
+ fallback?: T;
29
+ /**
30
+ * Número máximo de reintentos ante un error de la factory.
31
+ * Backoff corto — NO cubre cold start (eso lo hace el gate de auth), solo
32
+ * reconexiones transitorias. Default: 4.
33
+ */
34
+ retryCount?: number;
35
+ /**
36
+ * Delay base del backoff (ms). El delay efectivo crece exponencialmente
37
+ * acotado: 500ms → 1s → 2s ... Default: 500.
38
+ */
39
+ retryBaseDelayMs?: number;
40
+ /**
41
+ * Si `false`, NO espera a `firebaseAuthReady` antes de la primera
42
+ * suscripción. Útil para streams que no leen Firestore. Default: `true`.
43
+ */
44
+ gateOnFirebaseAuth?: boolean;
45
+ /**
46
+ * Injector explícito. Solo necesario si `createRefreshableStream` se llama
47
+ * fuera de un injection context (raro). Por defecto usa `inject(Injector)`.
48
+ */
49
+ injector?: Injector;
50
+ }
51
+ /**
52
+ * Resultado de `createRefreshableStream`.
53
+ */
54
+ export interface RefreshableStream<T> {
55
+ /** Datos actuales. `null` mientras la primera emisión no ha llegado. */
56
+ readonly data: Signal<T | null>;
57
+ /** `true` mientras se espera la primera emisión de la suscripción actual. */
58
+ readonly loading: Signal<boolean>;
59
+ /** `true` si la factory falló tras agotar los reintentos. */
60
+ readonly error: Signal<boolean>;
61
+ /**
62
+ * Re-suscribe la factory desde cero (nuevo listener / nueva lectura).
63
+ * Es lo que registra el `PageRefreshService` como handler de pull-to-refresh,
64
+ * y también el "reconectar manual" si un listener murió.
65
+ */
66
+ reload: () => void;
67
+ }
68
+ /**
69
+ * Crea un stream refrescable a partir de una factory de Observable.
70
+ *
71
+ * DEBE llamarse en un injection context (field initializer o constructor) —
72
+ * usa `toSignal`/`toObservable`/`inject` internamente.
73
+ *
74
+ * @param factory - Función que produce el Observable a consumir. El caller
75
+ * decide el modo: `() => from(svc.getAllOnce())` (one-shot) o
76
+ * `() => svc.getAll()` (real-time).
77
+ * @param options - Configuración opcional (fallback, retry, gate).
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * // one-shot (recomendado por defecto)
82
+ * private readonly stream = createRefreshableStream(
83
+ * () => from(this.svc.getAllOnce()),
84
+ * );
85
+ *
86
+ * // real-time (inbox)
87
+ * private readonly stream = createRefreshableStream(
88
+ * () => this.notifs.getAll(50),
89
+ * );
90
+ *
91
+ * readonly items = this.stream.data;
92
+ * readonly loading = this.stream.loading;
93
+ *
94
+ * ionViewWillEnter() {
95
+ * this.pageRefresh.register(() => this.stream.reload());
96
+ * }
97
+ * ```
98
+ */
99
+ export declare function createRefreshableStream<T>(factory: () => Observable<T>, options?: RefreshableStreamOptions<T>): RefreshableStream<T>;
package/lib/version.d.ts CHANGED
@@ -2,4 +2,4 @@
2
2
  * Current version of valtech-components.
3
3
  * This is automatically updated during the publish process.
4
4
  */
5
- export declare const VERSION = "2.0.803";
5
+ export declare const VERSION = "2.0.805";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "valtech-components",
3
- "version": "2.0.803",
3
+ "version": "2.0.805",
4
4
  "private": false,
5
5
  "bin": {
6
6
  "valtech-firebase-config": "./src/lib/services/firebase/scripts/generate-sw-config.js"
package/public-api.d.ts CHANGED
@@ -263,6 +263,7 @@ export * from './lib/services/auth';
263
263
  export * from './lib/services/i18n';
264
264
  export * from './lib/services/preferences';
265
265
  export * from './lib/services/page-refresh/page-refresh.service';
266
+ export * from './lib/services/refreshable-stream';
266
267
  export * from './lib/services/app-config';
267
268
  export * from './lib/services/presets';
268
269
  export * from './lib/services/skeleton';
@@ -272,6 +273,7 @@ export * from './lib/services/ads';
272
273
  export * from './lib/components/molecules/ad-slot/ad-slot.component';
273
274
  export * from './lib/services/content';
274
275
  export * from './lib/services/feedback';
276
+ export * from './lib/services/donation';
275
277
  export * from './lib/components/molecules/feedback-form/feedback-form.component';
276
278
  export * from './lib/components/molecules/feedback-form/types';
277
279
  export * from './lib/components/molecules/content-reaction/content-reaction.component';