@rdlabo/ionic-angular-kit 0.0.15 → 0.0.17
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/README.md +65 -1
- package/fesm2022/rdlabo-ionic-angular-kit-auth-firebase-social.mjs +218 -0
- package/fesm2022/rdlabo-ionic-angular-kit-auth-firebase-social.mjs.map +1 -0
- package/fesm2022/rdlabo-ionic-angular-kit-auth-firebase.mjs +340 -0
- package/fesm2022/rdlabo-ionic-angular-kit-auth-firebase.mjs.map +1 -0
- package/fesm2022/rdlabo-ionic-angular-kit.mjs +182 -34
- package/fesm2022/rdlabo-ionic-angular-kit.mjs.map +1 -1
- package/package.json +26 -1
- package/types/rdlabo-ionic-angular-kit-auth-firebase-social.d.ts +104 -0
- package/types/rdlabo-ionic-angular-kit-auth-firebase.d.ts +266 -0
- package/types/rdlabo-ionic-angular-kit.d.ts +106 -28
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, Injectable, InjectionToken, makeEnvironmentProviders, ElementRef, Directive } from '@angular/core';
|
|
2
|
+
import { inject, Injectable, InjectionToken, makeEnvironmentProviders, ElementRef, Injector, input, computed, HostListener, Directive } from '@angular/core';
|
|
3
3
|
import { Storage } from '@ionic/storage-angular';
|
|
4
4
|
import { ModalController, PopoverController, ToastController, AlertController, LoadingController, NavController } from '@ionic/angular/standalone';
|
|
5
5
|
import { Capacitor } from '@capacitor/core';
|
|
6
6
|
import { Keyboard } from '@capacitor/keyboard';
|
|
7
7
|
import { ImpactStyle, Haptics } from '@capacitor/haptics';
|
|
8
|
+
import { FORM_FIELD } from '@angular/forms/signals';
|
|
8
9
|
import { Router } from '@angular/router';
|
|
9
10
|
import { map, mergeMap, catchError, timeout, tap } from 'rxjs/operators';
|
|
10
11
|
import { HttpErrorResponse, HttpResponse } from '@angular/common/http';
|
|
@@ -100,6 +101,62 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
|
|
|
100
101
|
}]
|
|
101
102
|
}] });
|
|
102
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Remember the email a user last entered on the sign-in form so the field can be prefilled next
|
|
106
|
+
* time (the password is never stored).
|
|
107
|
+
*
|
|
108
|
+
* @remarks
|
|
109
|
+
* Storage-agnostic: the helpers take a minimal async key/value store that {@link KitStorageService}
|
|
110
|
+
* satisfies structurally, so they stay pure and unit-testable without DI. `kitRememberEmail`
|
|
111
|
+
* **validates the address before persisting** — a malformed or partial string is silently ignored,
|
|
112
|
+
* so a garbage entry (or a fat-fingered attempt) never becomes the next prefill. A well-formed email
|
|
113
|
+
* that later fails to sign in is still remembered by design: the user simply re-enters/corrects it.
|
|
114
|
+
*
|
|
115
|
+
* These live in the main entry (next to {@link KitStorageService}) rather than in `auth-firebase`,
|
|
116
|
+
* so the `KitAutofillDirective` can consume them without the main entry depending on the Firebase
|
|
117
|
+
* entry.
|
|
118
|
+
*/
|
|
119
|
+
/** Storage key under which the last entered sign-in email is kept. */
|
|
120
|
+
const KIT_LAST_AUTH_EMAIL_KEY = 'kit:last-auth-email';
|
|
121
|
+
/**
|
|
122
|
+
* Angular's `Validators.email` pattern — a pragmatic, RFC-5322-inspired address check. Kept in sync
|
|
123
|
+
* with `@angular/forms` so a value we persist would also pass the form's own email validation.
|
|
124
|
+
*/
|
|
125
|
+
const EMAIL_REGEXP = /^(?=.{1,254}$)(?=.{1,64}@)[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+(?:\.[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+)*@[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$/;
|
|
126
|
+
/** Whether `email` is a well-formed address (matches `@angular/forms` `Validators.email`). */
|
|
127
|
+
const kitIsValidEmail = (email) => EMAIL_REGEXP.test(email);
|
|
128
|
+
/**
|
|
129
|
+
* Persist the entered email for next time — but only when it is a well-formed address.
|
|
130
|
+
*
|
|
131
|
+
* @param store - the app's storage (e.g. `KitStorageService`)
|
|
132
|
+
* @param email - the entered email; ignored (not stored) when malformed
|
|
133
|
+
* @returns `true` when the value passed validation and was stored, `false` when it was ignored
|
|
134
|
+
*/
|
|
135
|
+
const kitRememberEmail = async (store, email) => {
|
|
136
|
+
if (!kitIsValidEmail(email)) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
await store.set(KIT_LAST_AUTH_EMAIL_KEY, email);
|
|
140
|
+
return true;
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* Recall the last remembered email.
|
|
144
|
+
*
|
|
145
|
+
* @param store - the app's storage (e.g. `KitStorageService`)
|
|
146
|
+
* @returns the stored email, or `null` when none has been remembered
|
|
147
|
+
*/
|
|
148
|
+
const kitRecallEmail = (store) => store.get(KIT_LAST_AUTH_EMAIL_KEY);
|
|
149
|
+
/**
|
|
150
|
+
* Forget the remembered email.
|
|
151
|
+
*
|
|
152
|
+
* @remarks
|
|
153
|
+
* Called when the user intentionally clears or invalidates the field, so a stale prefill is not
|
|
154
|
+
* resurrected next time.
|
|
155
|
+
*
|
|
156
|
+
* @param store - the app's storage (e.g. `KitStorageService`)
|
|
157
|
+
*/
|
|
158
|
+
const kitForgetEmail = (store) => store.remove(KIT_LAST_AUTH_EMAIL_KEY);
|
|
159
|
+
|
|
103
160
|
/**
|
|
104
161
|
* Injection token carrying the {@link KitOverlayConfig} for `KitOverlayController`.
|
|
105
162
|
*
|
|
@@ -661,41 +718,73 @@ const kitPresentLanguageActionSheet = async (actionSheetCtrl, options) => {
|
|
|
661
718
|
};
|
|
662
719
|
|
|
663
720
|
/**
|
|
664
|
-
*
|
|
665
|
-
*
|
|
666
|
-
*
|
|
667
|
-
*
|
|
668
|
-
*
|
|
669
|
-
* the
|
|
670
|
-
*
|
|
671
|
-
*
|
|
672
|
-
*
|
|
673
|
-
* `
|
|
674
|
-
*
|
|
675
|
-
*
|
|
676
|
-
*
|
|
677
|
-
*
|
|
678
|
-
*
|
|
721
|
+
* Input conveniences for an `ion-input` in a sign-in / sign-up form, applied via the `kitAuthInput`
|
|
722
|
+
* attribute. The mode is a typed union so a typo is a compile-time error.
|
|
723
|
+
*
|
|
724
|
+
* 1. **iOS autofill propagation (always on, every mode).** On iOS, when the browser autofills an
|
|
725
|
+
* `ion-input` (e.g. a saved password) the value is written to the underlying native `<input>`
|
|
726
|
+
* but the `change` event is not forwarded to the host `ion-input`, so the Angular form model
|
|
727
|
+
* never sees it. This directive mirrors the first inner-input `change` back onto the host,
|
|
728
|
+
* restoring two-way binding. It is a no-op on every other platform.
|
|
729
|
+
*
|
|
730
|
+
* 2. **Remember / prefill the email (`'email'` and `'email-remember'`).**
|
|
731
|
+
* - `'email'` (sign-in) — on init recalls the last entered email and, *only while the field is
|
|
732
|
+
* still empty*, seeds it via the bound Signal Forms field, so a browser/OS autofill the user
|
|
733
|
+
* actually picks always wins over the prefill. On every committed change (`ionChange`) it
|
|
734
|
+
* remembers a well-formed address, or **forgets** the stored one when the field is cleared
|
|
735
|
+
* (empty after trim) or holds an invalid address — the user intentionally removing the prefill.
|
|
736
|
+
* - `'email-remember'` (sign-up) — remembers a well-formed address on change, but never prefills
|
|
737
|
+
* and never forgets. So the first-sign-up address is captured without pre-populating a
|
|
738
|
+
* stranger's email into a new-account form, and clearing the field does not wipe an email
|
|
739
|
+
* remembered elsewhere.
|
|
740
|
+
*
|
|
741
|
+
* A well-formed email is persisted even if it later fails to sign in — by design; the user simply
|
|
742
|
+
* re-enters it. A malformed/partial entry is never stored (see {@link kitRememberEmail}).
|
|
679
743
|
*
|
|
680
744
|
* @example
|
|
681
745
|
* ```html
|
|
682
|
-
*
|
|
746
|
+
* <!-- sign-in email: iOS autofill + prefill + remember/forget -->
|
|
747
|
+
* <ion-input type="email" autocomplete="email" kitAuthInput="email" [formField]="form.email" />
|
|
748
|
+
* <!-- sign-up email: remember only -->
|
|
749
|
+
* <ion-input type="email" autocomplete="email" kitAuthInput="email-remember" [formField]="form.email" />
|
|
750
|
+
* <!-- password: iOS autofill propagation only -->
|
|
751
|
+
* <ion-input type="password" autocomplete="current-password" kitAuthInput="autofill" [formField]="form.password" />
|
|
683
752
|
* ```
|
|
753
|
+
*
|
|
754
|
+
* @remarks
|
|
755
|
+
* Prefill writes through the Signal Forms `FORM_FIELD` bound on the same element; with no such field
|
|
756
|
+
* (e.g. `ngModel` / reactive forms) prefill is skipped — remember/forget still work off the DOM
|
|
757
|
+
* event. Storage is resolved lazily so the iOS mirror still works in apps without `@ionic/storage`.
|
|
684
758
|
*/
|
|
685
|
-
class
|
|
686
|
-
#el
|
|
687
|
-
|
|
759
|
+
class KitAuthInputDirective {
|
|
760
|
+
#el;
|
|
761
|
+
#injector;
|
|
762
|
+
/** The Signal Forms field bound on this same element, if any (used to seed the prefill). */
|
|
763
|
+
#field;
|
|
764
|
+
constructor() {
|
|
765
|
+
this.#el = inject(ElementRef);
|
|
766
|
+
this.#injector = inject(Injector);
|
|
767
|
+
/** The Signal Forms field bound on this same element, if any (used to seed the prefill). */
|
|
768
|
+
this.#field = inject(FORM_FIELD, { optional: true, self: true });
|
|
769
|
+
/** Mode selector; see {@link KitAuthInputMode}. */
|
|
770
|
+
this.kitAuthInput = input('autofill', ...(ngDevMode ? [{ debugName: "kitAuthInput" }] : /* istanbul ignore next */ []));
|
|
771
|
+
/** Whether this instance persists the entered email (`'email'` or `'email-remember'`). */
|
|
772
|
+
this.#remembers = computed(() => {
|
|
773
|
+
const mode = this.kitAuthInput();
|
|
774
|
+
return mode === 'email' || mode === 'email-remember';
|
|
775
|
+
}, ...(ngDevMode ? [{ debugName: "#remembers" }] : /* istanbul ignore next */ []));
|
|
776
|
+
/** Whether this instance prefills the field from storage (`'email'` only). */
|
|
777
|
+
this.#prefills = computed(() => this.kitAuthInput() === 'email', ...(ngDevMode ? [{ debugName: "#prefills" }] : /* istanbul ignore next */ []));
|
|
778
|
+
/** Whether clearing/invalidating the field forgets the stored email (`'email'` only). */
|
|
779
|
+
this.#forgetsOnClear = computed(() => this.kitAuthInput() === 'email', ...(ngDevMode ? [{ debugName: "#forgetsOnClear" }] : /* istanbul ignore next */ []));
|
|
780
|
+
}
|
|
688
781
|
/**
|
|
689
|
-
* Register the iOS autofill workaround
|
|
690
|
-
*
|
|
691
|
-
* Returns immediately on non-iOS platforms. On iOS, after a short delay it attaches a one-shot,
|
|
692
|
-
* passive `change` listener to the inner `<input>` element that `ion-input` renders, copying the
|
|
693
|
-
* autofilled value back onto the host element so the Angular form model stays in sync. Any error
|
|
694
|
-
* while locating the inner input (for example if the element is not yet present) is swallowed.
|
|
695
|
-
*
|
|
696
|
-
* @returns Nothing.
|
|
782
|
+
* Register the iOS autofill workaround and, in `'email'` mode, seed the field from storage.
|
|
697
783
|
*/
|
|
698
784
|
ngOnInit() {
|
|
785
|
+
if (this.#prefills()) {
|
|
786
|
+
void this.#prefillEmail();
|
|
787
|
+
}
|
|
699
788
|
if (Capacitor.getPlatform() !== 'ios') {
|
|
700
789
|
return;
|
|
701
790
|
}
|
|
@@ -714,16 +803,75 @@ class KitAutofillDirective {
|
|
|
714
803
|
}
|
|
715
804
|
}, 100); // Need some time for the ion-input to create the input element
|
|
716
805
|
}
|
|
717
|
-
|
|
718
|
-
|
|
806
|
+
/**
|
|
807
|
+
* On every committed change (`ionChange`): remember a well-formed email, or — only in the
|
|
808
|
+
* prefilling `'email'` (sign-in) mode — forget the stored one when the field is cleared or invalid,
|
|
809
|
+
* which is the user intentionally removing the prefill. `'email-remember'` never forgets, so
|
|
810
|
+
* editing the sign-up field cannot wipe an email remembered from sign-in.
|
|
811
|
+
*/
|
|
812
|
+
onIonChange(event) {
|
|
813
|
+
if (!this.#remembers()) {
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
const storage = this.#resolveStorage();
|
|
817
|
+
if (!storage) {
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
const value = event.detail?.value ?? '';
|
|
821
|
+
if (value.trim().length > 0 && kitIsValidEmail(value)) {
|
|
822
|
+
void kitRememberEmail(storage, value);
|
|
823
|
+
}
|
|
824
|
+
else if (this.#forgetsOnClear()) {
|
|
825
|
+
void kitForgetEmail(storage);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
/** Whether this instance persists the entered email (`'email'` or `'email-remember'`). */
|
|
829
|
+
#remembers;
|
|
830
|
+
/** Whether this instance prefills the field from storage (`'email'` only). */
|
|
831
|
+
#prefills;
|
|
832
|
+
/** Whether clearing/invalidating the field forgets the stored email (`'email'` only). */
|
|
833
|
+
#forgetsOnClear;
|
|
834
|
+
/** Resolve `KitStorageService` lazily; returns `null` when storage is not configured. */
|
|
835
|
+
#resolveStorage() {
|
|
836
|
+
try {
|
|
837
|
+
return this.#injector.get(KitStorageService);
|
|
838
|
+
}
|
|
839
|
+
catch {
|
|
840
|
+
return null;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* Seed the bound field with the remembered email, but only while it is still empty — so a value
|
|
845
|
+
* the browser/OS autofills during the async recall is not clobbered.
|
|
846
|
+
*/
|
|
847
|
+
async #prefillEmail() {
|
|
848
|
+
const field = this.#field;
|
|
849
|
+
if (!field) {
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
const storage = this.#resolveStorage();
|
|
853
|
+
if (!storage) {
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
const last = await kitRecallEmail(storage);
|
|
857
|
+
const state = field.state();
|
|
858
|
+
if (last && !state.value()) {
|
|
859
|
+
state.value.set(last);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: KitAuthInputDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
863
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.17", type: KitAuthInputDirective, isStandalone: true, selector: "[kitAuthInput]", inputs: { kitAuthInput: { classPropertyName: "kitAuthInput", publicName: "kitAuthInput", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "ionChange": "onIonChange($event)" } }, ngImport: i0 }); }
|
|
719
864
|
}
|
|
720
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type:
|
|
865
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: KitAuthInputDirective, decorators: [{
|
|
721
866
|
type: Directive,
|
|
722
867
|
args: [{
|
|
723
|
-
selector: '[
|
|
868
|
+
selector: '[kitAuthInput]',
|
|
724
869
|
standalone: true,
|
|
725
870
|
}]
|
|
726
|
-
}], ctorParameters: () => [] }
|
|
871
|
+
}], ctorParameters: () => [], propDecorators: { kitAuthInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "kitAuthInput", required: false }] }], onIonChange: [{
|
|
872
|
+
type: HostListener,
|
|
873
|
+
args: ['ionChange', ['$event']]
|
|
874
|
+
}] } });
|
|
727
875
|
|
|
728
876
|
const keyboardWillShow = (elementRef, type) => Keyboard.addListener('keyboardWillShow', (info) => {
|
|
729
877
|
if (Capacitor.getPlatform() === 'android') {
|
|
@@ -1386,5 +1534,5 @@ const kitCreateDidEnter = (el) => {
|
|
|
1386
1534
|
* Generated bundle index. Do not edit.
|
|
1387
1535
|
*/
|
|
1388
1536
|
|
|
1389
|
-
export { KIT_AUTH_CONFIG, KIT_HTTP_CONFIG, KIT_OVERLAY_CONFIG,
|
|
1537
|
+
export { KIT_AUTH_CONFIG, KIT_HTTP_CONFIG, KIT_LAST_AUTH_EMAIL_KEY, KIT_OVERLAY_CONFIG, KitAuthInputDirective, KitLoadingController, KitOverlayController, KitReloadAlertController, KitStorageService, arrayConcatById, disableHandler, kitAuthInterceptor, kitChangeEventDisabled, kitCreateDidEnter, kitForgetEmail, kitImpact, kitIsValidEmail, kitKeyboardInit, kitPresentAuthFailedAlert, kitPresentLanguageActionSheet, kitRecallEmail, kitRememberEmail, kitRequireAuthorizedGuard, kitRequireConfirmingGuard, kitRequiredUnauthorizedGuard, objectEqual, provideKitAuth, provideKitHttp, provideKitOverlay };
|
|
1390
1538
|
//# sourceMappingURL=rdlabo-ionic-angular-kit.mjs.map
|