@dereekb/dbx-firebase 13.5.0 → 13.5.1

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.
@@ -2,7 +2,7 @@ import * as i0 from '@angular/core';
2
2
  import { inject, Injectable, provideAppInitializer, makeEnvironmentProviders, InjectionToken, Component, model, computed, ChangeDetectionStrategy, signal, Directive, EventEmitter, input, output, Injector, viewChild, HostListener, NgModule, ElementRef, forwardRef, effect, DestroyRef, Inject, Optional, Pipe } from '@angular/core';
3
3
  import { DbxAnalyticsService } from '@dereekb/dbx-analytics';
4
4
  import { asObservable, timeoutStartWith, filterMaybe, isNot, SubscriptionObject, lazyFrom, switchMapWhileTrue, loadingStateFromObs, distinctUntilKeysChange, cleanupDestroyable, iterationHasNextAndCanLoadMore, pageItemAccumulatorCurrentPage, accumulatorFlattenPageListLoadingState, useFirst, itemAccumulatorNextPageUntilResultsCount, iteratorNextPageUntilPage, iteratorNextPageUntilMaxPageLoadLimit, pageLoadingStateFromObs, useAsObservable, filterMaybeArray, mapEachAsync, invertObservableDecision, filterItemsWithObservableDecision, switchMapMaybe, beginLoading, mapLoadingStateValueWithOperator, valueFromFinishedLoadingState, skipInitialMaybe, distinctUntilModelKeyChange, successResult, errorResult, isLoadingStateLoading, cleanup, mapLoadingState, throwErrorFromLoadingStateError, maybeValueFromObservableOrValue, distinctUntilHasDifferentValues, MultiSubscriptionObject, startWithBeginLoading, skipAllInitialMaybe } from '@dereekb/rxjs';
5
- import { switchMap, of, shareReplay, map, distinctUntilChanged, EMPTY, catchError, firstValueFrom, BehaviorSubject, combineLatest, first, from, tap, interval, exhaustMap, filter, take, startWith, Subject, throttleTime, NEVER, defaultIfEmpty, combineLatestWith, mergeMap, Observable } from 'rxjs';
5
+ import { switchMap, of, shareReplay, map, Subject, merge, distinctUntilChanged, EMPTY, catchError, firstValueFrom, tap, BehaviorSubject, combineLatest, first, from, interval, exhaustMap, filter, take, startWith, throttleTime, NEVER, defaultIfEmpty, combineLatestWith, mergeMap, Observable } from 'rxjs';
6
6
  import * as i2 from '@dereekb/dbx-core';
7
7
  import { loggedInObsFromIsLoggedIn, loggedOutObsFromIsLoggedIn, authUserIdentifier, DbxInjectionContext, DBX_INJECTION_COMPONENT_DATA, DbxInjectionComponent, AbstractForwardDbxInjectionContextDirective, DbxInjectionContextDirective, DbxAuthService, DbxActionButtonDirective, completeOnDestroy, cleanSubscription, DbxActionDirective, DbxActionEnforceModifiedDirective, DbxActionHandlerDirective, DbxActionAutoTriggerDirective, clean, cleanLoadingContext, provideDbxRouteModelIdDirectiveDelegate, provideDbxRouteModelKeyDirectiveDelegate, AbstractIfDirective, LockSetComponentStore, newWithInjector, CutTextPipe, DbxActionContextStoreSourceInstance, DbxActionHandlerInstance, cleanSubscriptionWithLockSet, SimpleStorageAccessorFactory, dbxRouteParamReaderInstance, DbxRouteParamDefaultRedirectInstance } from '@dereekb/dbx-core';
8
8
  import { Auth, authState, idToken, signInWithPopup, linkWithPopup, linkWithCredential, unlink, createUserWithEmailAndPassword, signInWithEmailAndPassword, signInAnonymously, reauthenticateWithPopup, OAuthProvider, FacebookAuthProvider, GithubAuthProvider, GoogleAuthProvider, TwitterAuthProvider, provideAuth, getAuth, connectAuthEmulator } from '@angular/fire/auth';
@@ -141,7 +141,14 @@ class DbxFirebaseAuthService {
141
141
  firebaseAuth = inject(Auth);
142
142
  delegate = inject(DbxFirebaseAuthServiceDelegate, { optional: true }) ?? DEFAULT_DBX_FIREBASE_AUTH_SERVICE_DELEGATE;
143
143
  _authState$ = authState(this.firebaseAuth);
144
- currentAuthUser$ = this._authState$.pipe(timeoutStartWith(null, 1000), distinctUntilChanged(), shareReplay(1));
144
+ /**
145
+ * Subject that triggers a re-emission of the current auth user.
146
+ *
147
+ * Useful after operations that mutate the {@link User} object in place (e.g., linking/unlinking providers)
148
+ * without triggering a new {@link authState} emission.
149
+ */
150
+ _authUpdate$ = new Subject();
151
+ currentAuthUser$ = merge(this._authState$, this._authUpdate$.pipe(map(() => this.firebaseAuth.currentUser))).pipe(timeoutStartWith(null, 1000), shareReplay(1));
145
152
  currentAuthUserInfo$ = this.currentAuthUser$.pipe(map((x) => (x ? authUserInfoFromAuthUser(x) : undefined)));
146
153
  authUser$ = this.currentAuthUser$.pipe(filterMaybe());
147
154
  authUserInfo$ = this.authUser$.pipe(map(authUserInfoFromAuthUser));
@@ -250,7 +257,7 @@ class DbxFirebaseAuthService {
250
257
  else {
251
258
  throw new Error('User is not logged in currently.');
252
259
  }
253
- })));
260
+ }), tap(() => this._authUpdate$.next())));
254
261
  }
255
262
  /**
256
263
  * Links a credential to the current user. Useful for merging accounts
@@ -272,7 +279,7 @@ class DbxFirebaseAuthService {
272
279
  else {
273
280
  throw new Error('User is not logged in currently.');
274
281
  }
275
- })));
282
+ }), tap(() => this._authUpdate$.next())));
276
283
  }
277
284
  /**
278
285
  * Unlinks an authentication provider from the current user.
@@ -293,7 +300,7 @@ class DbxFirebaseAuthService {
293
300
  else {
294
301
  throw new Error('User is not logged in currently.');
295
302
  }
296
- })));
303
+ }), tap(() => this._authUpdate$.next())));
297
304
  }
298
305
  registerWithEmailAndPassword(email, password) {
299
306
  return createUserWithEmailAndPassword(this.firebaseAuth, email, password);
@@ -726,6 +733,73 @@ const OAUTH_FIREBASE_LOGIN_METHOD_CATEGORY = 'oauth';
726
733
  class DbxFirebaseLoginContext extends DbxInjectionContext {
727
734
  }
728
735
 
736
+ /**
737
+ * Map of Firebase provider IDs to known login method types.
738
+ *
739
+ * @example
740
+ * ```ts
741
+ * FIREBASE_PROVIDER_ID_TO_LOGIN_METHOD_TYPE_MAP['google.com']; // 'google'
742
+ * ```
743
+ */
744
+ const FIREBASE_PROVIDER_ID_TO_LOGIN_METHOD_TYPE_MAP = {
745
+ 'google.com': 'google',
746
+ 'facebook.com': 'facebook',
747
+ 'github.com': 'github',
748
+ 'twitter.com': 'twitter',
749
+ 'apple.com': 'apple',
750
+ 'microsoft.com': 'microsoft',
751
+ phone: 'phone',
752
+ password: 'email'
753
+ };
754
+ /**
755
+ * Map of known login method types to Firebase provider IDs.
756
+ *
757
+ * @example
758
+ * ```ts
759
+ * LOGIN_METHOD_TYPE_TO_FIREBASE_PROVIDER_ID_MAP['google']; // 'google.com'
760
+ * ```
761
+ */
762
+ const LOGIN_METHOD_TYPE_TO_FIREBASE_PROVIDER_ID_MAP = {
763
+ google: 'google.com',
764
+ facebook: 'facebook.com',
765
+ github: 'github.com',
766
+ twitter: 'twitter.com',
767
+ apple: 'apple.com',
768
+ microsoft: 'microsoft.com',
769
+ phone: 'phone',
770
+ email: 'password'
771
+ };
772
+ /**
773
+ * Converts a Firebase provider ID (e.g., 'google.com') to its corresponding login method type (e.g., 'google').
774
+ *
775
+ * @param providerId - The Firebase provider ID.
776
+ * @returns The matching login method type, or undefined if unknown.
777
+ *
778
+ * @example
779
+ * ```ts
780
+ * firebaseProviderIdToLoginMethodType('google.com'); // 'google'
781
+ * firebaseProviderIdToLoginMethodType('unknown.com'); // undefined
782
+ * ```
783
+ */
784
+ function firebaseProviderIdToLoginMethodType(providerId) {
785
+ return FIREBASE_PROVIDER_ID_TO_LOGIN_METHOD_TYPE_MAP[providerId];
786
+ }
787
+ /**
788
+ * Converts a login method type (e.g., 'google') to its corresponding Firebase provider ID (e.g., 'google.com').
789
+ *
790
+ * @param type - The login method type.
791
+ * @returns The matching Firebase provider ID, or undefined if unknown.
792
+ *
793
+ * @example
794
+ * ```ts
795
+ * loginMethodTypeToFirebaseProviderId('google'); // 'google.com'
796
+ * loginMethodTypeToFirebaseProviderId('unknown'); // undefined
797
+ * ```
798
+ */
799
+ function loginMethodTypeToFirebaseProviderId(type) {
800
+ return LOGIN_METHOD_TYPE_TO_FIREBASE_PROVIDER_ID_MAP[type];
801
+ }
802
+
729
803
  /**
730
804
  * Renders a styled login button that triggers a login action handler on click.
731
805
  *
@@ -737,8 +811,10 @@ class DbxFirebaseLoginButtonComponent {
737
811
  iconSignal = computed(() => this.config()?.icon, ...(ngDevMode ? [{ debugName: "iconSignal" }] : /* istanbul ignore next */ []));
738
812
  iconFilterSignal = computed(() => this.config()?.iconFilter, ...(ngDevMode ? [{ debugName: "iconFilterSignal" }] : /* istanbul ignore next */ []));
739
813
  textSignal = computed(() => this.config()?.text ?? '', ...(ngDevMode ? [{ debugName: "textSignal" }] : /* istanbul ignore next */ []));
740
- buttonColorSignal = computed(() => this.config()?.buttonColor ?? 'transparent', ...(ngDevMode ? [{ debugName: "buttonColorSignal" }] : /* istanbul ignore next */ []));
814
+ buttonColorSignal = computed(() => this.config()?.buttonColor ?? (this.config()?.color ? undefined : 'transparent'), ...(ngDevMode ? [{ debugName: "buttonColorSignal" }] : /* istanbul ignore next */ []));
741
815
  buttonTextColorSignal = computed(() => this.config()?.buttonTextColor, ...(ngDevMode ? [{ debugName: "buttonTextColorSignal" }] : /* istanbul ignore next */ []));
816
+ colorSignal = computed(() => this.config()?.color, ...(ngDevMode ? [{ debugName: "colorSignal" }] : /* istanbul ignore next */ []));
817
+ confirmConfigSignal = computed(() => this.config()?.confirmConfig, ...(ngDevMode ? [{ debugName: "confirmConfigSignal" }] : /* istanbul ignore next */ []));
742
818
  setConfig(config) {
743
819
  this.config.set(config);
744
820
  }
@@ -757,8 +833,8 @@ class DbxFirebaseLoginButtonComponent {
757
833
  };
758
834
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: DbxFirebaseLoginButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
759
835
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.3", type: DbxFirebaseLoginButtonComponent, isStandalone: true, selector: "dbx-firebase-login-button", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { config: "configChange" }, host: { classAttribute: "dbx-firebase-login-button" }, ngImport: i0, template: `
760
- <ng-container dbxAction [dbxActionHandler]="handleAction" dbxActionValue [dbxActionSuccessHandler]="onActionSuccess">
761
- <dbx-button dbxActionButton [customTextColor]="buttonTextColorSignal()" [customButtonColor]="buttonColorSignal()" [raised]="true">
836
+ <ng-container dbxAction [dbxActionHandler]="handleAction" [dbxActionSuccessHandler]="onActionSuccess" [dbxActionConfirm]="confirmConfigSignal()" [dbxActionConfirmSkip]="!confirmConfigSignal()">
837
+ <dbx-button dbxActionButton [customTextColor]="buttonTextColorSignal()" [customButtonColor]="buttonColorSignal()" [raised]="true" [color]="colorSignal()">
762
838
  <div class="dbx-firebase-login-button-content">
763
839
  <span class="dbx-firebase-login-button-icon dbx-icon-spacer">
764
840
  @if (iconUrlSignal()) {
@@ -772,15 +848,15 @@ class DbxFirebaseLoginButtonComponent {
772
848
  </div>
773
849
  </dbx-button>
774
850
  </ng-container>
775
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: DbxActionModule }, { kind: "directive", type: i2.DbxActionDirective, selector: "dbx-action,[dbxAction]", exportAs: ["action", "dbxAction"] }, { kind: "directive", type: i2.DbxActionHandlerDirective, selector: "[dbxActionHandler]", inputs: ["dbxActionHandler"] }, { kind: "directive", type: i2.DbxActionValueDirective, selector: "dbxActionValue,[dbxActionValue]", inputs: ["dbxActionValue"] }, { kind: "directive", type: i2.DbxActionSuccessHandlerDirective, selector: "[dbxActionSuccessHandler]", inputs: ["dbxActionSuccessHandler"] }, { kind: "directive", type: i2.DbxActionButtonDirective, selector: "[dbxActionButton]" }, { kind: "ngmodule", type: DbxButtonModule }, { kind: "component", type: i1$1.DbxButtonComponent, selector: "dbx-button", inputs: ["bar", "type", "buttonStyle", "color", "spinnerColor", "customButtonColor", "customTextColor", "customSpinnerColor", "basic", "tonal", "raised", "stroked", "flat", "iconOnly", "fab", "allowClickPropagation", "mode"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
851
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: DbxActionModule }, { kind: "directive", type: i2.DbxActionDirective, selector: "dbx-action,[dbxAction]", exportAs: ["action", "dbxAction"] }, { kind: "directive", type: i2.DbxActionHandlerDirective, selector: "[dbxActionHandler]", inputs: ["dbxActionHandler"] }, { kind: "directive", type: i2.DbxActionSuccessHandlerDirective, selector: "[dbxActionSuccessHandler]", inputs: ["dbxActionSuccessHandler"] }, { kind: "directive", type: i2.DbxActionButtonDirective, selector: "[dbxActionButton]" }, { kind: "directive", type: i1$1.DbxActionConfirmDirective, selector: "[dbxActionConfirm]", inputs: ["dbxActionConfirm", "dbxActionConfirmSkip"] }, { kind: "ngmodule", type: DbxButtonModule }, { kind: "component", type: i1$1.DbxButtonComponent, selector: "dbx-button", inputs: ["bar", "type", "buttonStyle", "color", "spinnerColor", "customButtonColor", "customTextColor", "customSpinnerColor", "basic", "tonal", "raised", "stroked", "flat", "iconOnly", "fab", "allowClickPropagation", "mode"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
776
852
  }
777
853
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: DbxFirebaseLoginButtonComponent, decorators: [{
778
854
  type: Component,
779
855
  args: [{
780
856
  selector: 'dbx-firebase-login-button',
781
857
  template: `
782
- <ng-container dbxAction [dbxActionHandler]="handleAction" dbxActionValue [dbxActionSuccessHandler]="onActionSuccess">
783
- <dbx-button dbxActionButton [customTextColor]="buttonTextColorSignal()" [customButtonColor]="buttonColorSignal()" [raised]="true">
858
+ <ng-container dbxAction [dbxActionHandler]="handleAction" [dbxActionSuccessHandler]="onActionSuccess" [dbxActionConfirm]="confirmConfigSignal()" [dbxActionConfirmSkip]="!confirmConfigSignal()">
859
+ <dbx-button dbxActionButton [customTextColor]="buttonTextColorSignal()" [customButtonColor]="buttonColorSignal()" [raised]="true" [color]="colorSignal()">
784
860
  <div class="dbx-firebase-login-button-content">
785
861
  <span class="dbx-firebase-login-button-icon dbx-icon-spacer">
786
862
  @if (iconUrlSignal()) {
@@ -876,13 +952,16 @@ class AbstractConfiguredDbxFirebaseLoginButtonDirective {
876
952
  ngOnInit() {
877
953
  const assets = this.assetConfig;
878
954
  const text = this._textForMode(assets);
955
+ const isUnlink = this.effectiveLoginMode === 'unlink';
879
956
  this._config.set({
880
957
  text,
881
- icon: assets.loginIcon,
882
- iconUrl: assets.logoUrl,
883
- iconFilter: assets.logoFilter,
884
- buttonColor: assets.backgroundColor,
885
- buttonTextColor: assets.textColor,
958
+ icon: isUnlink ? 'link_off' : assets.loginIcon,
959
+ iconUrl: isUnlink ? undefined : assets.logoUrl,
960
+ iconFilter: isUnlink ? undefined : assets.logoFilter,
961
+ buttonColor: isUnlink ? undefined : assets.backgroundColor,
962
+ buttonTextColor: isUnlink ? undefined : assets.textColor,
963
+ color: isUnlink ? 'warn' : undefined,
964
+ confirmConfig: isUnlink ? { title: `Disconnect ${assets.providerName ?? 'Provider'}`, prompt: `Are you sure you want to disconnect ${assets.providerName ?? 'this provider'}?` } : undefined,
886
965
  handleLogin: () => this._handleAction()
887
966
  });
888
967
  }
@@ -895,6 +974,19 @@ class AbstractConfiguredDbxFirebaseLoginButtonDirective {
895
974
  handleLink() {
896
975
  throw new Error(`Linking is not supported for the "${this.loginProvider}" provider.`);
897
976
  }
977
+ /**
978
+ * Handles the unlink action by removing the provider from the current user.
979
+ * Uses the {@link LOGIN_METHOD_TYPE_TO_FIREBASE_PROVIDER_ID_MAP} to resolve the Firebase provider ID.
980
+ *
981
+ * @returns A promise that resolves when the unlink action completes.
982
+ */
983
+ handleUnlink() {
984
+ const providerId = loginMethodTypeToFirebaseProviderId(this.loginProvider);
985
+ if (!providerId) {
986
+ throw new Error(`Unknown provider ID for login method type "${this.loginProvider}".`);
987
+ }
988
+ return this.dbxFirebaseAuthService.unlinkProvider(providerId);
989
+ }
898
990
  get providerConfig() {
899
991
  return this.dbxFirebaseAuthLoginService.getLoginProvider(this.loginProvider);
900
992
  }
@@ -906,21 +998,31 @@ class AbstractConfiguredDbxFirebaseLoginButtonDirective {
906
998
  }
907
999
  _textForMode(assets) {
908
1000
  let text;
909
- if (this.effectiveLoginMode === 'link') {
910
- text = assets.linkText ?? (assets.providerName ? `Connect ${assets.providerName}` : '<linkText not configured>');
911
- }
912
- else {
913
- text = assets.loginText ?? '<loginText not configured>';
1001
+ switch (this.effectiveLoginMode) {
1002
+ case 'link':
1003
+ text = assets.linkText ?? (assets.providerName ? `Connect ${assets.providerName}` : '<linkText not configured>');
1004
+ break;
1005
+ case 'unlink':
1006
+ text = assets.unlinkText ?? (assets.providerName ? `Disconnect ${assets.providerName}` : '<unlinkText not configured>');
1007
+ break;
1008
+ default:
1009
+ text = assets.loginText ?? '<loginText not configured>';
1010
+ break;
914
1011
  }
915
1012
  return text;
916
1013
  }
917
1014
  _handleAction() {
918
1015
  let promise;
919
- if (this.effectiveLoginMode === 'link') {
920
- promise = this.handleLink();
921
- }
922
- else {
923
- promise = this.handleLogin();
1016
+ switch (this.effectiveLoginMode) {
1017
+ case 'link':
1018
+ promise = this.handleLink();
1019
+ break;
1020
+ case 'unlink':
1021
+ promise = this.handleUnlink();
1022
+ break;
1023
+ default:
1024
+ promise = this.handleLogin();
1025
+ break;
924
1026
  }
925
1027
  return promise.catch((error) => {
926
1028
  throw firebaseAuthErrorToReadableError(error);
@@ -1432,6 +1534,14 @@ function provideDbxFirebaseLogin(config) {
1432
1534
  */
1433
1535
  class DbxFirebaseLoginListComponent {
1434
1536
  dbxFirebaseAuthLoginService = inject(DbxFirebaseAuthLoginService);
1537
+ dbxFirebaseAuthService = inject(DbxFirebaseAuthService);
1538
+ _linkedProviderIds = toSignal(this.dbxFirebaseAuthService.currentLinkedProviderIds$, { initialValue: [] });
1539
+ /**
1540
+ * The login method types currently linked to the authenticated user.
1541
+ */
1542
+ linkedMethodTypesSignal = computed(() => {
1543
+ return filterMaybeArrayValues(this._linkedProviderIds().map(firebaseProviderIdToLoginMethodType));
1544
+ }, ...(ngDevMode ? [{ debugName: "linkedMethodTypesSignal" }] : /* istanbul ignore next */ []));
1435
1545
  loginMode = input('login', ...(ngDevMode ? [{ debugName: "loginMode" }] : /* istanbul ignore next */ []));
1436
1546
  providerTypes = input(...(ngDevMode ? [undefined, { debugName: "providerTypes" }] : /* istanbul ignore next */ []));
1437
1547
  omitProviderTypes = input(...(ngDevMode ? [undefined, { debugName: "omitProviderTypes" }] : /* istanbul ignore next */ []));
@@ -1439,7 +1549,15 @@ class DbxFirebaseLoginListComponent {
1439
1549
  providerTypesSignal = computed(() => {
1440
1550
  const providerTypes = this.providerTypes();
1441
1551
  const omitProviderTypes = this.omitProviderTypes();
1442
- const baseTypes = providerTypes ? asArray(providerTypes) : this.dbxFirebaseAuthLoginService.getEnabledTypes();
1552
+ const loginMode = this.loginMode();
1553
+ let baseTypes;
1554
+ if (loginMode === 'unlink') {
1555
+ // In unlink mode, show only currently linked providers
1556
+ baseTypes = this.linkedMethodTypesSignal();
1557
+ }
1558
+ else {
1559
+ baseTypes = providerTypes ? asArray(providerTypes) : this.dbxFirebaseAuthLoginService.getEnabledTypes();
1560
+ }
1443
1561
  return omitProviderTypes ? excludeValuesFromArray(baseTypes, asArray(omitProviderTypes)) : baseTypes;
1444
1562
  }, ...(ngDevMode ? [{ debugName: "providerTypesSignal" }] : /* istanbul ignore next */ []));
1445
1563
  providersSignal = computed(() => {
@@ -1464,6 +1582,10 @@ class DbxFirebaseLoginListComponent {
1464
1582
  providers = providers.filter((x) => x.allowLinking !== false);
1465
1583
  mapFn = (x) => ({ componentClass: x.componentClass, loginMethodType: x.loginMethodType, data: { loginMode } });
1466
1584
  break;
1585
+ case 'unlink':
1586
+ providers = providers.filter((x) => x.allowLinking !== false);
1587
+ mapFn = (x) => ({ componentClass: x.componentClass, loginMethodType: x.loginMethodType, data: { loginMode } });
1588
+ break;
1467
1589
  default:
1468
1590
  mapFn = (x) => ({ componentClass: x.componentClass, loginMethodType: x.loginMethodType, data: { loginMode } });
1469
1591
  break;
@@ -1471,7 +1593,7 @@ class DbxFirebaseLoginListComponent {
1471
1593
  return providers.map(mapFn);
1472
1594
  }, ...(ngDevMode ? [{ debugName: "providersInjectionConfigsSignal" }] : /* istanbul ignore next */ []));
1473
1595
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: DbxFirebaseLoginListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1474
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.3", type: DbxFirebaseLoginListComponent, isStandalone: true, selector: "dbx-firebase-login-list", inputs: { loginMode: { classPropertyName: "loginMode", publicName: "loginMode", isSignal: true, isRequired: false, transformFunction: null }, providerTypes: { classPropertyName: "providerTypes", publicName: "providerTypes", isSignal: true, isRequired: false, transformFunction: null }, omitProviderTypes: { classPropertyName: "omitProviderTypes", publicName: "omitProviderTypes", isSignal: true, isRequired: false, transformFunction: null }, providerCategories: { classPropertyName: "providerCategories", publicName: "providerCategories", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "dbx-firebase-login-list" }, ngImport: i0, template: `
1596
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.3", type: DbxFirebaseLoginListComponent, isStandalone: true, selector: "dbx-firebase-login-list", inputs: { loginMode: { classPropertyName: "loginMode", publicName: "loginMode", isSignal: true, isRequired: false, transformFunction: null }, providerTypes: { classPropertyName: "providerTypes", publicName: "providerTypes", isSignal: true, isRequired: false, transformFunction: null }, omitProviderTypes: { classPropertyName: "omitProviderTypes", publicName: "omitProviderTypes", isSignal: true, isRequired: false, transformFunction: null }, providerCategories: { classPropertyName: "providerCategories", publicName: "providerCategories", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "dbx-firebase-login-list dbx-button-column" }, ngImport: i0, template: `
1475
1597
  @for (config of providersInjectionConfigsSignal(); track config.loginMethodType) {
1476
1598
  <div class="dbx-firebase-login-item">
1477
1599
  <dbx-injection [config]="config"></dbx-injection>
@@ -1491,7 +1613,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.3", ngImpor
1491
1613
  }
1492
1614
  `,
1493
1615
  host: {
1494
- class: 'dbx-firebase-login-list'
1616
+ class: 'dbx-firebase-login-list dbx-button-column'
1495
1617
  },
1496
1618
  standalone: true,
1497
1619
  imports: [DbxInjectionComponent]
@@ -1638,73 +1760,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.3", ngImpor
1638
1760
  }]
1639
1761
  }] });
1640
1762
 
1641
- /**
1642
- * Map of Firebase provider IDs to known login method types.
1643
- *
1644
- * @example
1645
- * ```ts
1646
- * FIREBASE_PROVIDER_ID_TO_LOGIN_METHOD_TYPE_MAP['google.com']; // 'google'
1647
- * ```
1648
- */
1649
- const FIREBASE_PROVIDER_ID_TO_LOGIN_METHOD_TYPE_MAP = {
1650
- 'google.com': 'google',
1651
- 'facebook.com': 'facebook',
1652
- 'github.com': 'github',
1653
- 'twitter.com': 'twitter',
1654
- 'apple.com': 'apple',
1655
- 'microsoft.com': 'microsoft',
1656
- phone: 'phone',
1657
- password: 'email'
1658
- };
1659
- /**
1660
- * Map of known login method types to Firebase provider IDs.
1661
- *
1662
- * @example
1663
- * ```ts
1664
- * LOGIN_METHOD_TYPE_TO_FIREBASE_PROVIDER_ID_MAP['google']; // 'google.com'
1665
- * ```
1666
- */
1667
- const LOGIN_METHOD_TYPE_TO_FIREBASE_PROVIDER_ID_MAP = {
1668
- google: 'google.com',
1669
- facebook: 'facebook.com',
1670
- github: 'github.com',
1671
- twitter: 'twitter.com',
1672
- apple: 'apple.com',
1673
- microsoft: 'microsoft.com',
1674
- phone: 'phone',
1675
- email: 'password'
1676
- };
1677
- /**
1678
- * Converts a Firebase provider ID (e.g., 'google.com') to its corresponding login method type (e.g., 'google').
1679
- *
1680
- * @param providerId - The Firebase provider ID.
1681
- * @returns The matching login method type, or undefined if unknown.
1682
- *
1683
- * @example
1684
- * ```ts
1685
- * firebaseProviderIdToLoginMethodType('google.com'); // 'google'
1686
- * firebaseProviderIdToLoginMethodType('unknown.com'); // undefined
1687
- * ```
1688
- */
1689
- function firebaseProviderIdToLoginMethodType(providerId) {
1690
- return FIREBASE_PROVIDER_ID_TO_LOGIN_METHOD_TYPE_MAP[providerId];
1691
- }
1692
- /**
1693
- * Converts a login method type (e.g., 'google') to its corresponding Firebase provider ID (e.g., 'google.com').
1694
- *
1695
- * @param type - The login method type.
1696
- * @returns The matching Firebase provider ID, or undefined if unknown.
1697
- *
1698
- * @example
1699
- * ```ts
1700
- * loginMethodTypeToFirebaseProviderId('google'); // 'google.com'
1701
- * loginMethodTypeToFirebaseProviderId('unknown'); // undefined
1702
- * ```
1703
- */
1704
- function loginMethodTypeToFirebaseProviderId(type) {
1705
- return LOGIN_METHOD_TYPE_TO_FIREBASE_PROVIDER_ID_MAP[type];
1706
- }
1707
-
1708
1763
  /**
1709
1764
  * Component for managing linked authentication providers on a user account.
1710
1765
  *
@@ -1721,47 +1776,19 @@ class DbxFirebaseManageAuthProvidersComponent {
1721
1776
  dbxFirebaseAuthService = inject(DbxFirebaseAuthService);
1722
1777
  dbxFirebaseAuthLoginService = inject(DbxFirebaseAuthLoginService);
1723
1778
  _linkedProviderIds = toSignal(this.dbxFirebaseAuthService.currentLinkedProviderIds$, { initialValue: [] });
1724
- linkedProvidersSignal = computed(() => {
1725
- const providerIds = this._linkedProviderIds();
1726
- return providerIds.map((providerId) => {
1727
- const loginMethodType = firebaseProviderIdToLoginMethodType(providerId);
1728
- const assets = loginMethodType ? this.dbxFirebaseAuthLoginService.getProviderAssets(loginMethodType) : undefined;
1729
- const providerName = assets?.providerName ?? providerId;
1730
- const unlinkText = assets?.unlinkText ?? `Disconnect ${providerName}`;
1731
- return { providerId, loginMethodType, providerName, unlinkText, assets };
1732
- });
1733
- }, ...(ngDevMode ? [{ debugName: "linkedProvidersSignal" }] : /* istanbul ignore next */ []));
1734
1779
  linkedMethodTypesSignal = computed(() => {
1735
- return filterMaybeArrayValues(this.linkedProvidersSignal().map((p) => p.loginMethodType));
1780
+ return filterMaybeArrayValues(this._linkedProviderIds().map(firebaseProviderIdToLoginMethodType));
1736
1781
  }, ...(ngDevMode ? [{ debugName: "linkedMethodTypesSignal" }] : /* istanbul ignore next */ []));
1737
1782
  showLinkSectionSignal = computed(() => {
1738
1783
  const linkedTypes = new Set(this.linkedMethodTypesSignal());
1739
1784
  const oauthProviders = this.dbxFirebaseAuthLoginService.getLinkProviders(this.dbxFirebaseAuthLoginService.getEnabledTypes());
1740
1785
  return oauthProviders.some((p) => p.category === OAUTH_FIREBASE_LOGIN_METHOD_CATEGORY && !linkedTypes.has(p.loginMethodType));
1741
1786
  }, ...(ngDevMode ? [{ debugName: "showLinkSectionSignal" }] : /* istanbul ignore next */ []));
1742
- /**
1743
- * Creates a work handler for unlinking a specific provider.
1744
- *
1745
- * @param providerId - The Firebase provider ID to unlink (e.g., 'google.com').
1746
- * @returns A {@link WorkUsingContext} handler that unlinking the provider on execution.
1747
- */
1748
- makeUnlinkHandler(providerId) {
1749
- return (_, context) => {
1750
- const promise = this.dbxFirebaseAuthService.unlinkProvider(providerId);
1751
- context.startWorkingWithPromise(promise);
1752
- };
1753
- }
1754
1787
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: DbxFirebaseManageAuthProvidersComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1755
1788
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.3", type: DbxFirebaseManageAuthProvidersComponent, isStandalone: true, selector: "dbx-firebase-manage-auth-providers", host: { classAttribute: "d-block dbx-firebase-manage-auth-providers" }, ngImport: i0, template: `
1756
- @if (linkedProvidersSignal().length) {
1789
+ @if (linkedMethodTypesSignal().length) {
1757
1790
  <dbx-section header="Connected Providers">
1758
- @for (provider of linkedProvidersSignal(); track provider.providerId) {
1759
- <div class="dbx-firebase-manage-provider-item">
1760
- <ng-container dbxAction [dbxActionHandler]="makeUnlinkHandler(provider.providerId)" dbxActionValue>
1761
- <dbx-button dbxActionButton [text]="provider.unlinkText" icon="link_off" color="warn"></dbx-button>
1762
- </ng-container>
1763
- </div>
1764
- }
1791
+ <dbx-firebase-login loginMode="unlink" providerCategories="oauth"></dbx-firebase-login>
1765
1792
  </dbx-section>
1766
1793
  }
1767
1794
  @if (showLinkSectionSignal()) {
@@ -1769,24 +1796,18 @@ class DbxFirebaseManageAuthProvidersComponent {
1769
1796
  <dbx-firebase-login loginMode="link" [omitProviderTypes]="linkedMethodTypesSignal()" providerCategories="oauth"></dbx-firebase-login>
1770
1797
  </dbx-section>
1771
1798
  }
1772
- `, isInline: true, dependencies: [{ kind: "component", type: DbxFirebaseLoginComponent, selector: "dbx-firebase-login", inputs: ["loginMode", "providerTypes", "omitProviderTypes", "providerCategories"] }, { kind: "component", type: DbxSectionComponent, selector: "dbx-section", inputs: ["elevate"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: DbxActionModule }, { kind: "directive", type: i2.DbxActionDirective, selector: "dbx-action,[dbxAction]", exportAs: ["action", "dbxAction"] }, { kind: "directive", type: i2.DbxActionHandlerDirective, selector: "[dbxActionHandler]", inputs: ["dbxActionHandler"] }, { kind: "directive", type: i2.DbxActionValueDirective, selector: "dbxActionValue,[dbxActionValue]", inputs: ["dbxActionValue"] }, { kind: "directive", type: i2.DbxActionButtonDirective, selector: "[dbxActionButton]" }, { kind: "ngmodule", type: DbxButtonModule }, { kind: "component", type: i1$1.DbxButtonComponent, selector: "dbx-button", inputs: ["bar", "type", "buttonStyle", "color", "spinnerColor", "customButtonColor", "customTextColor", "customSpinnerColor", "basic", "tonal", "raised", "stroked", "flat", "iconOnly", "fab", "allowClickPropagation", "mode"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1799
+ `, isInline: true, dependencies: [{ kind: "component", type: DbxFirebaseLoginComponent, selector: "dbx-firebase-login", inputs: ["loginMode", "providerTypes", "omitProviderTypes", "providerCategories"] }, { kind: "component", type: DbxSectionComponent, selector: "dbx-section", inputs: ["elevate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1773
1800
  }
1774
1801
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: DbxFirebaseManageAuthProvidersComponent, decorators: [{
1775
1802
  type: Component,
1776
1803
  args: [{
1777
1804
  selector: 'dbx-firebase-manage-auth-providers',
1778
1805
  standalone: true,
1779
- imports: [DbxFirebaseLoginComponent, DbxSectionComponent, MatIconModule, DbxActionModule, DbxButtonModule],
1806
+ imports: [DbxFirebaseLoginComponent, DbxSectionComponent],
1780
1807
  template: `
1781
- @if (linkedProvidersSignal().length) {
1808
+ @if (linkedMethodTypesSignal().length) {
1782
1809
  <dbx-section header="Connected Providers">
1783
- @for (provider of linkedProvidersSignal(); track provider.providerId) {
1784
- <div class="dbx-firebase-manage-provider-item">
1785
- <ng-container dbxAction [dbxActionHandler]="makeUnlinkHandler(provider.providerId)" dbxActionValue>
1786
- <dbx-button dbxActionButton [text]="provider.unlinkText" icon="link_off" color="warn"></dbx-button>
1787
- </ng-container>
1788
- </div>
1789
- }
1810
+ <dbx-firebase-login loginMode="unlink" providerCategories="oauth"></dbx-firebase-login>
1790
1811
  </dbx-section>
1791
1812
  }
1792
1813
  @if (showLinkSectionSignal()) {
@@ -4196,7 +4217,7 @@ class DbxFirebaseDocumentStoreContextStore extends ComponentStore {
4196
4217
  lastStoresChangeAt$ = this.select((state) => state.lastStoresChangeAt).pipe(distinctUntilChanged(isSameDate), shareReplay(1));
4197
4218
  entriesGroupedByIdentity$ = this.stores$.pipe(switchMap((stores) => {
4198
4219
  let entriesObs;
4199
- const allEntries = [...stores.values()];
4220
+ const allEntries = Array.from(stores.values());
4200
4221
  const { included: hasIdentity, excluded: noIdentity } = separateValues(allEntries, (x) => x.modelIdentity != null);
4201
4222
  if (noIdentity.length > 0) {
4202
4223
  entriesObs = combineLatest(noIdentity.map((entryWithoutCachedIdentity) => entryWithoutCachedIdentity.store.modelIdentity$.pipe(first(), map((z) => {