@sneat/auth-core 0.1.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.
@@ -0,0 +1,39 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ ILoginEventsHandler,
4
+ LoginEventsHandler,
5
+ } from './sneat-auth.interface';
6
+
7
+ describe('sneat-auth.interface', () => {
8
+ describe('LoginEventsHandler', () => {
9
+ it('should be an InjectionToken', () => {
10
+ expect(LoginEventsHandler).toBeDefined();
11
+ expect(LoginEventsHandler.toString()).toContain('ILoginEventsHandler');
12
+ });
13
+ });
14
+
15
+ describe('ILoginEventsHandler interface', () => {
16
+ it('should be implemented by objects with onLoggedIn method', () => {
17
+ const handler: ILoginEventsHandler = {
18
+ onLoggedIn: () => {
19
+ // Implementation
20
+ },
21
+ };
22
+
23
+ expect(handler).toBeDefined();
24
+ expect(typeof handler.onLoggedIn).toBe('function');
25
+ });
26
+
27
+ it('should allow onLoggedIn to be called', () => {
28
+ let called = false;
29
+ const handler: ILoginEventsHandler = {
30
+ onLoggedIn: () => {
31
+ called = true;
32
+ },
33
+ };
34
+
35
+ handler.onLoggedIn();
36
+ expect(called).toBe(true);
37
+ });
38
+ });
39
+ });
@@ -0,0 +1,7 @@
1
+ import { InjectionToken } from '@angular/core';
2
+
3
+ export interface ILoginEventsHandler {
4
+ onLoggedIn(): void;
5
+ }
6
+
7
+ export const LoginEventsHandler = new InjectionToken('ILoginEventsHandler');
@@ -0,0 +1,186 @@
1
+ import { TestBed } from '@angular/core/testing';
2
+ import { SneatApiService } from '@sneat/api';
3
+ import { ErrorLogger } from '@sneat/core';
4
+ import { SneatAuthStateService } from './sneat-auth-state-service';
5
+ import { TelegramAuthService } from './telegram-auth.service';
6
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
7
+ import { of, throwError } from 'rxjs';
8
+
9
+ describe('TelegramAuthService', () => {
10
+ let service: TelegramAuthService;
11
+ let sneatApiServiceMock: { postAsAnonymous: ReturnType<typeof vi.fn> };
12
+ let authStateServiceMock: { signInWithToken: ReturnType<typeof vi.fn> };
13
+ let errorLoggerMock: { logErrorHandler: ReturnType<typeof vi.fn> };
14
+
15
+ beforeEach(() => {
16
+ sneatApiServiceMock = {
17
+ postAsAnonymous: vi.fn(),
18
+ };
19
+
20
+ authStateServiceMock = {
21
+ signInWithToken: vi.fn().mockResolvedValue({}),
22
+ };
23
+
24
+ errorLoggerMock = {
25
+ logErrorHandler: vi.fn().mockReturnValue((err: unknown) => {
26
+ console.error(err);
27
+ }),
28
+ };
29
+
30
+ TestBed.configureTestingModule({
31
+ providers: [
32
+ TelegramAuthService,
33
+ {
34
+ provide: ErrorLogger,
35
+ useValue: errorLoggerMock,
36
+ },
37
+ {
38
+ provide: SneatAuthStateService,
39
+ useValue: authStateServiceMock,
40
+ },
41
+ {
42
+ provide: SneatApiService,
43
+ useValue: sneatApiServiceMock,
44
+ },
45
+ ],
46
+ });
47
+ service = TestBed.inject(TelegramAuthService);
48
+ });
49
+
50
+ it('should be created', () => {
51
+ expect(service).toBeTruthy();
52
+ });
53
+
54
+ it('should not authenticate if Telegram WebApp is not available', () => {
55
+ delete (window as unknown as { Telegram?: unknown }).Telegram;
56
+
57
+ service.authenticateIfTelegramWebApp();
58
+
59
+ expect(sneatApiServiceMock.postAsAnonymous).not.toHaveBeenCalled();
60
+ });
61
+
62
+ it('should not authenticate if Telegram WebApp initData is missing', () => {
63
+ (
64
+ window as unknown as { Telegram: { WebApp: { initData?: unknown } } }
65
+ ).Telegram = {
66
+ WebApp: {},
67
+ };
68
+
69
+ service.authenticateIfTelegramWebApp();
70
+
71
+ expect(sneatApiServiceMock.postAsAnonymous).not.toHaveBeenCalled();
72
+ });
73
+
74
+ it('should authenticate with Telegram WebApp', () => {
75
+ const mockInitData = 'test-init-data';
76
+ const mockToken = 'firebase-token-123';
77
+ const mockReady = vi.fn();
78
+
79
+ (
80
+ window as unknown as {
81
+ Telegram: { WebApp: { initData: string; ready: () => void } };
82
+ }
83
+ ).Telegram = {
84
+ WebApp: {
85
+ initData: mockInitData,
86
+ ready: mockReady,
87
+ },
88
+ };
89
+
90
+ sneatApiServiceMock.postAsAnonymous.mockReturnValue(
91
+ of({ token: mockToken }),
92
+ );
93
+
94
+ service.authenticateIfTelegramWebApp();
95
+
96
+ expect(sneatApiServiceMock.postAsAnonymous).toHaveBeenCalledWith(
97
+ 'auth/login-from-telegram-miniapp',
98
+ mockInitData,
99
+ );
100
+ });
101
+
102
+ it('should call signInWithToken on successful API response', async () => {
103
+ const mockInitData = 'test-init-data';
104
+ const mockToken = 'firebase-token-123';
105
+ const mockReady = vi.fn();
106
+
107
+ (
108
+ window as unknown as {
109
+ Telegram: { WebApp: { initData: string; ready: () => void } };
110
+ }
111
+ ).Telegram = {
112
+ WebApp: {
113
+ initData: mockInitData,
114
+ ready: mockReady,
115
+ },
116
+ };
117
+
118
+ sneatApiServiceMock.postAsAnonymous.mockReturnValue(
119
+ of({ token: mockToken }),
120
+ );
121
+
122
+ service.authenticateIfTelegramWebApp();
123
+
124
+ await new Promise((resolve) => setTimeout(resolve, 10));
125
+
126
+ expect(authStateServiceMock.signInWithToken).toHaveBeenCalledWith(
127
+ mockToken,
128
+ );
129
+ expect(mockReady).toHaveBeenCalled();
130
+ });
131
+
132
+ it('should handle API error gracefully', () => {
133
+ const mockInitData = 'test-init-data';
134
+
135
+ (
136
+ window as unknown as { Telegram: { WebApp: { initData: string } } }
137
+ ).Telegram = {
138
+ WebApp: {
139
+ initData: mockInitData,
140
+ },
141
+ };
142
+
143
+ sneatApiServiceMock.postAsAnonymous.mockReturnValue(
144
+ throwError(() => new Error('API Error')),
145
+ );
146
+
147
+ service.authenticateIfTelegramWebApp();
148
+
149
+ expect(sneatApiServiceMock.postAsAnonymous).toHaveBeenCalled();
150
+ expect(errorLoggerMock.logErrorHandler).toHaveBeenCalledWith(
151
+ 'Failed to get Firebase auth token issued using telegram web-app credentials',
152
+ );
153
+ });
154
+
155
+ it('should handle signInWithToken error gracefully', async () => {
156
+ const mockInitData = 'test-init-data';
157
+ const mockToken = 'firebase-token-123';
158
+
159
+ (
160
+ window as unknown as {
161
+ Telegram: { WebApp: { initData: string; ready: () => void } };
162
+ }
163
+ ).Telegram = {
164
+ WebApp: {
165
+ initData: mockInitData,
166
+ ready: vi.fn(),
167
+ },
168
+ };
169
+
170
+ sneatApiServiceMock.postAsAnonymous.mockReturnValue(
171
+ of({ token: mockToken }),
172
+ );
173
+
174
+ authStateServiceMock.signInWithToken.mockRejectedValue(
175
+ new Error('Sign in error'),
176
+ );
177
+
178
+ service.authenticateIfTelegramWebApp();
179
+
180
+ await new Promise((resolve) => setTimeout(resolve, 10));
181
+
182
+ expect(errorLoggerMock.logErrorHandler).toHaveBeenCalledWith(
183
+ 'Failed to sign-in to Firebase auth with a token issued for telegram web-app credentials',
184
+ );
185
+ });
186
+ });
@@ -0,0 +1,49 @@
1
+ import { Injectable, inject } from '@angular/core';
2
+ import { SneatApiService } from '@sneat/api';
3
+ import { SneatAuthStateService } from './sneat-auth-state-service';
4
+ import { ErrorLogger, IErrorLogger } from '@sneat/core';
5
+
6
+ @Injectable({
7
+ providedIn: 'root',
8
+ })
9
+ export class TelegramAuthService {
10
+ private readonly errorLogger = inject<IErrorLogger>(ErrorLogger);
11
+ private readonly authStateService = inject(SneatAuthStateService);
12
+ private readonly sneatApiService = inject(SneatApiService);
13
+
14
+ public authenticateIfTelegramWebApp() {
15
+ const telegramWebApp = (
16
+ window as unknown as {
17
+ Telegram?: {
18
+ WebApp?: {
19
+ initData: unknown;
20
+ ready: () => void;
21
+ };
22
+ };
23
+ }
24
+ ).Telegram?.WebApp;
25
+ if (telegramWebApp?.initData) {
26
+ this.sneatApiService
27
+ .postAsAnonymous<{
28
+ token: string;
29
+ }>('auth/login-from-telegram-miniapp', telegramWebApp?.initData)
30
+ .subscribe({
31
+ next: (response) => {
32
+ this.authStateService
33
+ .signInWithToken(response.token)
34
+ .then(() => {
35
+ telegramWebApp.ready();
36
+ })
37
+ .catch(
38
+ this.errorLogger.logErrorHandler(
39
+ 'Failed to sign-in to Firebase auth with a token issued for telegram web-app credentials',
40
+ ),
41
+ );
42
+ },
43
+ error: this.errorLogger.logErrorHandler(
44
+ 'Failed to get Firebase auth token issued using telegram web-app credentials',
45
+ ),
46
+ });
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,2 @@
1
+ export * from './user-record.service';
2
+ export * from './sneat-user.service';
@@ -0,0 +1,151 @@
1
+ import { TestBed } from '@angular/core/testing';
2
+ import {
3
+ Firestore as AngularFirestore,
4
+ CollectionReference,
5
+ } from '@angular/fire/firestore';
6
+ import { SneatApiService } from '@sneat/api';
7
+ import { ErrorLogger } from '@sneat/core';
8
+ import {
9
+ SneatAuthStateService,
10
+ ISneatAuthState,
11
+ } from '../sneat-auth-state-service';
12
+ import { UserRecordService } from './user-record.service';
13
+ import { SneatUserService } from './sneat-user.service';
14
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
15
+ import { of, Subject, firstValueFrom } from 'rxjs';
16
+
17
+ // Mock firestore functions
18
+ vi.mock('@angular/fire/firestore', async () => {
19
+ const actual = await vi.importActual('@angular/fire/firestore');
20
+ return {
21
+ ...actual,
22
+ collection: vi
23
+ .fn()
24
+ .mockReturnValue({ id: 'users' } as unknown as CollectionReference),
25
+ doc: vi.fn(),
26
+ onSnapshot: vi.fn(),
27
+ };
28
+ });
29
+
30
+ describe('SneatUserService', () => {
31
+ let service: SneatUserService;
32
+ let authStateSubject: Subject<ISneatAuthState>;
33
+ let sneatApiServiceMock: { post: ReturnType<typeof vi.fn> };
34
+ let userRecordServiceMock: { initUserRecord: ReturnType<typeof vi.fn> };
35
+ let firestoreMock: Record<string, unknown>;
36
+
37
+ beforeEach(() => {
38
+ authStateSubject = new Subject<ISneatAuthState>();
39
+
40
+ sneatApiServiceMock = {
41
+ post: vi.fn().mockReturnValue(of(void 0)),
42
+ };
43
+
44
+ userRecordServiceMock = {
45
+ initUserRecord: vi
46
+ .fn()
47
+ .mockReturnValue(of({ id: 'user123', title: 'Test User' })),
48
+ };
49
+
50
+ firestoreMock = {
51
+ app: {},
52
+ type: 'firestore',
53
+ };
54
+
55
+ TestBed.configureTestingModule({
56
+ providers: [
57
+ SneatUserService,
58
+ {
59
+ provide: AngularFirestore,
60
+ useValue: firestoreMock,
61
+ },
62
+ {
63
+ provide: ErrorLogger,
64
+ useValue: {
65
+ logError: vi.fn(),
66
+ logErrorHandler: vi.fn().mockReturnValue(() => undefined),
67
+ },
68
+ },
69
+ {
70
+ provide: SneatAuthStateService,
71
+ useValue: {
72
+ authState: authStateSubject.asObservable(),
73
+ },
74
+ },
75
+ {
76
+ provide: SneatApiService,
77
+ useValue: sneatApiServiceMock,
78
+ },
79
+ {
80
+ provide: UserRecordService,
81
+ useValue: userRecordServiceMock,
82
+ },
83
+ ],
84
+ });
85
+
86
+ service = TestBed.inject(SneatUserService);
87
+ });
88
+
89
+ it('should be created', () => {
90
+ expect(service).toBeTruthy();
91
+ });
92
+
93
+ it('should have currentUserID as undefined initially', () => {
94
+ expect(service.currentUserID).toBeUndefined();
95
+ });
96
+
97
+ it('should call setUserCountry with correct parameters', async () => {
98
+ const countryID = 'US';
99
+ await firstValueFrom(service.setUserCountry(countryID));
100
+ expect(sneatApiServiceMock.post).toHaveBeenCalledWith(
101
+ 'users/set_user_country',
102
+ { countryID },
103
+ );
104
+ });
105
+
106
+ it('should update currentUserID when user signs in', () => {
107
+ const authState: ISneatAuthState = {
108
+ status: 'authenticated',
109
+ user: {
110
+ uid: 'test-uid-123',
111
+ email: 'test@example.com',
112
+ emailVerified: true,
113
+ displayName: 'Test User',
114
+ providerId: 'google.com',
115
+ isAnonymous: false,
116
+ providerData: [],
117
+ photoURL: null,
118
+ phoneNumber: null,
119
+ },
120
+ };
121
+
122
+ service.onUserSignedIn(authState);
123
+
124
+ expect(service.currentUserID).toBe('test-uid-123');
125
+ });
126
+
127
+ it('should not change currentUserID if uid is the same', () => {
128
+ const authState: ISneatAuthState = {
129
+ status: 'authenticated',
130
+ user: {
131
+ uid: 'test-uid-123',
132
+ email: 'test@example.com',
133
+ emailVerified: true,
134
+ displayName: 'Test User',
135
+ providerId: 'google.com',
136
+ isAnonymous: false,
137
+ providerData: [],
138
+ photoURL: null,
139
+ phoneNumber: null,
140
+ },
141
+ };
142
+
143
+ service.onUserSignedIn(authState);
144
+ const firstUserID = service.currentUserID;
145
+
146
+ service.onUserSignedIn(authState); // Second call with same uid
147
+
148
+ expect(service.currentUserID).toBe(firstUserID);
149
+ expect(service.currentUserID).toBe('test-uid-123');
150
+ });
151
+ });
@@ -0,0 +1,266 @@
1
+ import {
2
+ Injectable,
3
+ inject,
4
+ Injector,
5
+ runInInjectionContext,
6
+ } from '@angular/core';
7
+ import {
8
+ // Action,
9
+ Firestore as AngularFirestore,
10
+ CollectionReference,
11
+ DocumentSnapshot,
12
+ collection,
13
+ doc,
14
+ onSnapshot,
15
+ Unsubscribe,
16
+ } from '@angular/fire/firestore';
17
+ import { SneatApiService } from '@sneat/api';
18
+ import { IUserRecord } from '@sneat/auth-models';
19
+ import { ErrorLogger, IErrorLogger } from '@sneat/core';
20
+ import {
21
+ initialSneatAuthState,
22
+ ISneatAuthState,
23
+ ISneatAuthUser,
24
+ SneatAuthStateService,
25
+ } from '../sneat-auth-state-service';
26
+ import { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';
27
+ import {
28
+ IInitUserRecordRequest,
29
+ UserRecordService,
30
+ } from './user-record.service';
31
+
32
+ export interface ISneatUserState extends ISneatAuthState {
33
+ record?: IUserRecord | null; // undefined => not loaded yet, null = does not exists
34
+ }
35
+
36
+ const UsersCollection = 'users';
37
+
38
+ @Injectable({ providedIn: 'root' }) // TODO: lazy loading
39
+ export class SneatUserService {
40
+ private readonly errorLogger = inject<IErrorLogger>(ErrorLogger);
41
+ private readonly sneatApiService = inject(SneatApiService);
42
+ private readonly userRecordService = inject(UserRecordService);
43
+
44
+ // private userDocSubscription?: Subscription;
45
+ private readonly injector = inject(Injector);
46
+ private readonly userCollection: CollectionReference<IUserRecord>;
47
+ private readonly userDocRef = (uid: string) =>
48
+ runInInjectionContext(this.injector, () => doc(this.userCollection, uid));
49
+
50
+ private uid?: string;
51
+ private $userTitle?: string;
52
+
53
+ private readonly userChanged$ = new ReplaySubject<string | undefined>(1);
54
+ public readonly userChanged = this.userChanged$.asObservable();
55
+
56
+ private readonly userState$ = new BehaviorSubject<ISneatUserState>(
57
+ initialSneatAuthState,
58
+ );
59
+
60
+ public readonly userState = this.userState$.asObservable();
61
+
62
+ private _unsubscribeFromUserDoc?: Unsubscribe;
63
+
64
+ private unsubscribeFromUserDoc(_from: string) {
65
+ if (this._unsubscribeFromUserDoc) {
66
+ // console.log(
67
+ // 'SneatUserService.unsubscribeFromUserDoc() called from ' + _from,
68
+ // );
69
+ this._unsubscribeFromUserDoc();
70
+ this._unsubscribeFromUserDoc = undefined;
71
+ }
72
+ }
73
+
74
+ constructor() {
75
+ const afs = inject(AngularFirestore);
76
+ const sneatAuthStateService = inject(SneatAuthStateService);
77
+ this.userCollection = collection(
78
+ afs,
79
+ UsersCollection,
80
+ ) as CollectionReference<IUserRecord>;
81
+ sneatAuthStateService.authState.subscribe({
82
+ next: this.onAuthStateChanged,
83
+ error: this.errorLogger.logErrorHandler('failed to get sneat auth state'),
84
+ });
85
+ }
86
+
87
+ public get currentUserID(): string | undefined {
88
+ return this.uid;
89
+ }
90
+
91
+ // public get userTitle(): string | undefined {
92
+ // return this.$userTitle;
93
+ // }
94
+
95
+ public setUserCountry(countryID: string): Observable<void> {
96
+ return this.sneatApiService.post('users/set_user_country', { countryID });
97
+ }
98
+
99
+ public onUserSignedIn(authState: ISneatAuthState): void {
100
+ const authUser = authState.user;
101
+ // afUser.getIdToken().then(idToken => {
102
+ // console.log('Firebase idToken:', idToken);
103
+ // }).catch(err => this.errorLoggerService.logError(err, 'Failed to get Firebase ID token'));
104
+ if (authUser?.email && authUser.emailVerified) {
105
+ this.$userTitle = authUser.email;
106
+ }
107
+ if (this.uid === authUser?.uid) {
108
+ return;
109
+ }
110
+ this.unsubscribeFromUserDoc('onUserSignedIn()');
111
+ if (!authUser) {
112
+ if (this.userState$.value?.record !== null) {
113
+ this.userState$.next({ ...this.userState$.value });
114
+ }
115
+ return;
116
+ }
117
+ const { uid } = authUser;
118
+ this.uid = uid;
119
+ this.userState$.next({
120
+ ...authState,
121
+ });
122
+ this.userChanged$.next(uid);
123
+ this.watchUserRecord(uid, authState);
124
+ }
125
+
126
+ private watchUserRecord(uid: string, authState: ISneatAuthState): void {
127
+ // console.log(
128
+ // `SneatUserService.watchUserRecord(uid=${uid}): Loading user record...`,
129
+ // );
130
+ this.unsubscribeFromUserDoc('whatUserRecord()');
131
+
132
+ // TODO: Remove - setTimeout() not needed but trying to troubleshoot user record issue
133
+ setTimeout(() => {
134
+ try {
135
+ const userDocRef = this.userDocRef(uid);
136
+ this._unsubscribeFromUserDoc = runInInjectionContext(
137
+ this.injector,
138
+ () =>
139
+ onSnapshot(userDocRef, {
140
+ next: (userDocSnapshot) => {
141
+ // console.log(
142
+ // `SneatUserService.watchUserRecord(uid=${uid}) => userDocSnapshot:`,
143
+ // userDocSnapshot,
144
+ // );
145
+ this.onAuthStateChanged(authState);
146
+ this.userDocChanged(userDocSnapshot, authState);
147
+ },
148
+ error: (err) => {
149
+ console.error(
150
+ `SneatUserService.watchUserRecord(uid=${uid}) => failed:`,
151
+ err,
152
+ );
153
+ },
154
+ }),
155
+ );
156
+ } catch (err) {
157
+ console.error(
158
+ `SneatUserService.watchUserRecord(uid=${uid}) => Failed to setup watcher for user record::`,
159
+ err,
160
+ );
161
+ return;
162
+ }
163
+ }, 100);
164
+ }
165
+
166
+ private onAuthStateChanged = (authState: ISneatAuthState): void => {
167
+ // console.log('SneatUserService => authState changed:', authState);
168
+ if (authState.user) {
169
+ this.onUserSignedIn(authState);
170
+ } else {
171
+ this.userState$.next(authState);
172
+ this.userChanged$.next(undefined);
173
+ this.onUserSignedOut();
174
+ }
175
+ };
176
+
177
+ private userDocChanged(
178
+ userDocSnapshot: DocumentSnapshot<IUserRecord>,
179
+ authState: ISneatAuthState,
180
+ ): void {
181
+ // console.log(
182
+ // 'SneatUserService.userDocChanged() => userDocSnapshot.exists:',
183
+ // userDocSnapshot.exists(),
184
+ // 'authState:',
185
+ // authState,
186
+ // 'userDocSnapshot:',
187
+ // userDocSnapshot,
188
+ // );
189
+ if (userDocSnapshot.ref.id !== this.uid) {
190
+ console.error(
191
+ 'userDocSnapshot.ref.id !== this.uid - Should always be equal as we unsubscribe if uid changes',
192
+ );
193
+ return;
194
+ }
195
+ // console.log('SneatUserService => userDocSnapshot.exists:', userDocSnapshot.exists)
196
+ const authUser = authState.user;
197
+ if (authUser && !userDocSnapshot.exists()) {
198
+ this.initUserRecordFromAuthUser(authUser);
199
+ }
200
+ const userRecord: IUserRecord | null = userDocSnapshot.exists()
201
+ ? (userDocSnapshot.data() as IUserRecord)
202
+ : authUser
203
+ ? { title: authUser.displayName || authUser.email || authUser.uid }
204
+ : null;
205
+
206
+ this.userState$.next({
207
+ ...authState,
208
+ record: userRecord,
209
+ });
210
+ }
211
+
212
+ private initUserRecordFromAuthUser(authUser: ISneatAuthUser): void {
213
+ let request: IInitUserRecordRequest = {
214
+ email: authUser.email || undefined,
215
+ emailIsVerified: authUser.emailVerified,
216
+ authProvider: authUser?.providerId,
217
+ };
218
+ if (authUser?.displayName) {
219
+ request = { ...request, names: { fullName: authUser.displayName } };
220
+ }
221
+ this.userRecordService.initUserRecord(request).subscribe({
222
+ next: (_userDto) => {
223
+ // User record created successfully - no additional action needed
224
+ },
225
+ error: this.errorLogger.logErrorHandler('failed to create user record'),
226
+ });
227
+ }
228
+
229
+ private onUserSignedOut(): void {
230
+ this.uid = undefined;
231
+ this.unsubscribeFromUserDoc('onUserSignedOut()');
232
+ }
233
+
234
+ // private createUserRecord(userDocRef: DocumentReference, authUser: ISneatAuthUser): void {
235
+ // if (this.userState$.value) {
236
+ // return;
237
+ // }
238
+ // this.db.firestore.runTransaction(async tx => {
239
+ // if (this.userState$.value) {
240
+ // return undefined;
241
+ // }
242
+ // const u = await tx.get(userDocRef);
243
+ // if (!u.exists) {
244
+ // const title = authUser.displayName || authUser.email || authUser.uid;
245
+ // const user: IUserRecord = authUser.email
246
+ // ? {title, email: authUser.email, emailVerified: authUser.emailVerified}
247
+ // : {title};
248
+ // await tx.set(userDocRef, user);
249
+ // return user;
250
+ // }
251
+ // return undefined;
252
+ // }).then(user => {
253
+ // if (user) {
254
+ // console.log('user record created:', user);
255
+ // }
256
+ // if (!this.userState$.value) {
257
+ // const userState: ISneatUserState = {
258
+ // status: AuthStatuses.authenticated,
259
+ // record: user,
260
+ // user: authUser,
261
+ // };
262
+ // this.userState$.next(userState);
263
+ // }
264
+ // }).catch(this.errorLogger.logErrorHandler('failed to create user record'));
265
+ // }
266
+ }