@sneat/auth-ui 0.1.3 → 0.1.6

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.
Files changed (89) hide show
  1. package/esm2022/index.js +4 -0
  2. package/esm2022/index.js.map +1 -0
  3. package/esm2022/lib/components/auth-menu-item/auth-menu-item.component.js +82 -0
  4. package/esm2022/lib/components/auth-menu-item/auth-menu-item.component.js.map +1 -0
  5. package/esm2022/lib/components/index.js +4 -0
  6. package/esm2022/lib/components/index.js.map +1 -0
  7. package/esm2022/lib/components/user-auth-providers/user-auth-accounts.component.js +83 -0
  8. package/esm2022/lib/components/user-auth-providers/user-auth-accounts.component.js.map +1 -0
  9. package/esm2022/lib/components/user-auth-providers/user-auth-provider-status.js +118 -0
  10. package/esm2022/lib/components/user-auth-providers/user-auth-provider-status.js.map +1 -0
  11. package/esm2022/lib/components/user-required-fields/index.js +3 -0
  12. package/esm2022/lib/components/user-required-fields/index.js.map +1 -0
  13. package/esm2022/lib/components/user-required-fields/user-required-fields-modal.component.js +111 -0
  14. package/esm2022/lib/components/user-required-fields/user-required-fields-modal.component.js.map +1 -0
  15. package/esm2022/lib/components/user-required-fields/user-required-fields.service.js +29 -0
  16. package/esm2022/lib/components/user-required-fields/user-required-fields.service.js.map +1 -0
  17. package/esm2022/lib/pages/login-page/email-login-form/email-login-form.component.js +263 -0
  18. package/esm2022/lib/pages/login-page/email-login-form/email-login-form.component.js.map +1 -0
  19. package/esm2022/lib/pages/login-page/login-page.component.js +143 -0
  20. package/esm2022/lib/pages/login-page/login-page.component.js.map +1 -0
  21. package/esm2022/lib/pages/login-page/login-with-telegram.component.js +93 -0
  22. package/esm2022/lib/pages/login-page/login-with-telegram.component.js.map +1 -0
  23. package/esm2022/lib/pages/login-page/sneat-auth-with-telegram.service.js +31 -0
  24. package/esm2022/lib/pages/login-page/sneat-auth-with-telegram.service.js.map +1 -0
  25. package/esm2022/lib/pages/sign-in-from-email-link/sign-in-from-email-link-page.component.js +52 -0
  26. package/esm2022/lib/pages/sign-in-from-email-link/sign-in-from-email-link-page.component.js.map +1 -0
  27. package/esm2022/lib/pages/sneat-auth-routing.module.js +27 -0
  28. package/esm2022/lib/pages/sneat-auth-routing.module.js.map +1 -0
  29. package/esm2022/lib/pipes/person-names.pipe.js +35 -0
  30. package/esm2022/lib/pipes/person-names.pipe.js.map +1 -0
  31. package/esm2022/sneat-auth-ui.js +5 -0
  32. package/esm2022/sneat-auth-ui.js.map +1 -0
  33. package/lib/components/auth-menu-item/auth-menu-item.component.d.ts +24 -0
  34. package/lib/components/user-auth-providers/user-auth-accounts.component.d.ts +17 -0
  35. package/lib/components/user-auth-providers/user-auth-provider-status.d.ts +31 -0
  36. package/lib/components/user-required-fields/user-required-fields-modal.component.d.ts +31 -0
  37. package/lib/components/user-required-fields/user-required-fields.service.d.ts +7 -0
  38. package/lib/pages/login-page/email-login-form/email-login-form.component.d.ts +51 -0
  39. package/lib/pages/login-page/login-page.component.d.ts +30 -0
  40. package/lib/pages/login-page/login-with-telegram.component.d.ts +17 -0
  41. package/lib/pages/login-page/sneat-auth-with-telegram.service.d.ts +18 -0
  42. package/lib/pages/sign-in-from-email-link/sign-in-from-email-link-page.component.d.ts +13 -0
  43. package/lib/pages/sneat-auth-routing.module.d.ts +9 -0
  44. package/lib/pipes/person-names.pipe.d.ts +9 -0
  45. package/package.json +14 -2
  46. package/sneat-auth-ui.d.ts +5 -0
  47. package/tsconfig.lib.prod.tsbuildinfo +1 -0
  48. package/eslint.config.cjs +0 -7
  49. package/ng-package.json +0 -7
  50. package/project.json +0 -38
  51. package/src/lib/components/auth-menu-item/auth-menu-item.component.html +0 -59
  52. package/src/lib/components/auth-menu-item/auth-menu-item.component.spec.ts +0 -5
  53. package/src/lib/components/auth-menu-item/auth-menu-item.component.ts +0 -116
  54. package/src/lib/components/user-auth-providers/user-auth-accounts.component.html +0 -127
  55. package/src/lib/components/user-auth-providers/user-auth-accounts.component.spec.ts +0 -47
  56. package/src/lib/components/user-auth-providers/user-auth-accounts.component.ts +0 -109
  57. package/src/lib/components/user-auth-providers/user-auth-provider-status.html +0 -42
  58. package/src/lib/components/user-auth-providers/user-auth-provider-status.ts +0 -172
  59. package/src/lib/components/user-required-fields/user-required-fields-modal.component.html +0 -64
  60. package/src/lib/components/user-required-fields/user-required-fields-modal.component.spec.ts +0 -45
  61. package/src/lib/components/user-required-fields/user-required-fields-modal.component.ts +0 -141
  62. package/src/lib/components/user-required-fields/user-required-fields.service.spec.ts +0 -335
  63. package/src/lib/components/user-required-fields/user-required-fields.service.ts +0 -23
  64. package/src/lib/pages/login-page/email-login-form/email-login-form.component.html +0 -165
  65. package/src/lib/pages/login-page/email-login-form/email-login-form.component.spec.ts +0 -59
  66. package/src/lib/pages/login-page/email-login-form/email-login-form.component.ts +0 -356
  67. package/src/lib/pages/login-page/index.ts +0 -3
  68. package/src/lib/pages/login-page/login-page.component.html +0 -146
  69. package/src/lib/pages/login-page/login-page.component.spec.ts +0 -5
  70. package/src/lib/pages/login-page/login-page.component.ts +0 -209
  71. package/src/lib/pages/login-page/login-with-telegram.component.spec.ts +0 -42
  72. package/src/lib/pages/login-page/login-with-telegram.component.ts +0 -82
  73. package/src/lib/pages/login-page/sneat-auth-with-telegram.service.spec.ts +0 -31
  74. package/src/lib/pages/login-page/sneat-auth-with-telegram.service.ts +0 -56
  75. package/src/lib/pages/sign-in-from-email-link/sign-in-from-email-link-page.component.html +0 -35
  76. package/src/lib/pages/sign-in-from-email-link/sign-in-from-email-link-page.component.spec.ts +0 -43
  77. package/src/lib/pages/sign-in-from-email-link/sign-in-from-email-link-page.component.ts +0 -70
  78. package/src/lib/pages/sneat-auth-routing.module.ts +0 -25
  79. package/src/lib/pipes/person-names.pipe.spec.ts +0 -317
  80. package/src/lib/pipes/person-names.pipe.ts +0 -31
  81. package/src/test-setup.ts +0 -3
  82. package/tsconfig.json +0 -13
  83. package/tsconfig.lib.json +0 -19
  84. package/tsconfig.lib.prod.json +0 -7
  85. package/tsconfig.spec.json +0 -31
  86. package/vite.config.mts +0 -10
  87. /package/{src/index.ts → index.d.ts} +0 -0
  88. /package/{src/lib/components/index.ts → lib/components/index.d.ts} +0 -0
  89. /package/{src/lib/components/user-required-fields/index.ts → lib/components/user-required-fields/index.d.ts} +0 -0
@@ -0,0 +1,29 @@
1
+ import { Injectable, inject } from '@angular/core';
2
+ import { ModalController } from '@ionic/angular/standalone';
3
+ import { UserRequiredFieldsModalComponent } from './user-required-fields-modal.component';
4
+ import * as i0 from "@angular/core";
5
+ export class UserRequiredFieldsService {
6
+ constructor() {
7
+ this.modalController = inject(ModalController);
8
+ }
9
+ async open() {
10
+ const modal = await this.modalController.create({
11
+ component: UserRequiredFieldsModalComponent,
12
+ });
13
+ await modal.present();
14
+ return new Promise((resolve, reject) => {
15
+ modal
16
+ .onDidDismiss()
17
+ .then((value) => {
18
+ resolve(!!value);
19
+ })
20
+ .catch(reject);
21
+ });
22
+ }
23
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: UserRequiredFieldsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
24
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: UserRequiredFieldsService }); }
25
+ }
26
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: UserRequiredFieldsService, decorators: [{
27
+ type: Injectable
28
+ }] });
29
+ //# sourceMappingURL=user-required-fields.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-required-fields.service.js","sourceRoot":"","sources":["../../../../../../../../libs/auth/ui/src/lib/components/user-required-fields/user-required-fields.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,gCAAgC,EAAE,MAAM,wCAAwC,CAAC;;AAG1F,MAAM,OAAO,yBAAyB;IADtC;QAEmB,oBAAe,GAAG,MAAM,CAAC,eAAe,CAAC,CAAC;KAgB5D;IAdQ,KAAK,CAAC,IAAI;QACf,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC;YAC9C,SAAS,EAAE,gCAAgC;SAC5C,CAAC,CAAC;QACH,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC;QACtB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,KAAK;iBACF,YAAY,EAAE;iBACd,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;gBACd,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YACnB,CAAC,CAAC;iBACD,KAAK,CAAC,MAAM,CAAC,CAAC;QACnB,CAAC,CAAC,CAAC;IACL,CAAC;8GAhBU,yBAAyB;kHAAzB,yBAAyB;;2FAAzB,yBAAyB;kBADrC,UAAU","sourcesContent":["import { Injectable, inject } from '@angular/core';\nimport { ModalController } from '@ionic/angular/standalone';\nimport { UserRequiredFieldsModalComponent } from './user-required-fields-modal.component';\n\n@Injectable()\nexport class UserRequiredFieldsService {\n private readonly modalController = inject(ModalController);\n\n public async open(): Promise<boolean> {\n const modal = await this.modalController.create({\n component: UserRequiredFieldsModalComponent,\n });\n await modal.present();\n return new Promise((resolve, reject) => {\n modal\n .onDidDismiss()\n .then((value) => {\n resolve(!!value);\n })\n .catch(reject);\n });\n }\n}\n"]}
@@ -0,0 +1,263 @@
1
+ import { Component, EventEmitter, Output, ViewChild, inject, } from '@angular/core';
2
+ // import { getApp } from '@angular/fire/app';
3
+ // import { getAuth } from '@angular/fire/auth';
4
+ import { Auth, sendPasswordResetEmail, sendSignInLinkToEmail, } from '@angular/fire/auth';
5
+ import { FormsModule } from '@angular/forms';
6
+ import { ToastController, IonButton, IonButtons, IonCard, IonCardContent, IonIcon, IonInput, IonItem, IonItemDivider, IonLabel, IonSegment, IonSegmentButton, IonSpinner, IonText, } from '@ionic/angular/standalone';
7
+ import { SneatApiService } from '@sneat/api';
8
+ import { UserRecordService } from '@sneat/auth-core';
9
+ import { createSetFocusToInput } from '@sneat/ui';
10
+ import { AnalyticsService, APP_INFO, } from '@sneat/core';
11
+ import { ErrorLogger } from '@sneat/core';
12
+ import { RandomIdService } from '@sneat/random';
13
+ import { sendEmailVerification } from 'firebase/auth';
14
+ import { createUserWithEmailAndPassword, signInWithEmailAndPassword, } from 'firebase/auth';
15
+ import * as i0 from "@angular/core";
16
+ import * as i1 from "@angular/forms";
17
+ export class EmailLoginFormComponent {
18
+ constructor() {
19
+ this.appInfo = inject(APP_INFO);
20
+ this.analyticsService = inject(AnalyticsService);
21
+ this.errorLogger = inject(ErrorLogger);
22
+ this.toastController = inject(ToastController);
23
+ this.afAuth = inject(Auth);
24
+ this.randomIdService = inject(RandomIdService);
25
+ this.sneatApiService = inject(SneatApiService);
26
+ this.userRecordService = inject(UserRecordService);
27
+ this.sign = 'up'; // TODO: document here what 'in' & 'up' means
28
+ this.email = '';
29
+ this.password = '';
30
+ this.firstName = '';
31
+ this.lastName = '';
32
+ this.spaceTitle = '';
33
+ this.wrongPassword = false;
34
+ this.signingWithChange = new EventEmitter();
35
+ this.loggedIn = new EventEmitter();
36
+ this.setFocusToInput = createSetFocusToInput(this.errorLogger);
37
+ this.email = localStorage.getItem('emailForSignIn') || '';
38
+ if (this.email) {
39
+ this.sign = 'in';
40
+ }
41
+ }
42
+ get validEmail() {
43
+ const email = this.email, i = email?.indexOf('@');
44
+ return i > 0 && i < email.length - 1;
45
+ }
46
+ getFirebaseAuth() {
47
+ return this.afAuth; // TODO: pending https://github.com/angular/angularfire/pull/3402
48
+ }
49
+ async signUp() {
50
+ if (!this.firstName) {
51
+ // this.toaster.showToast('Full name is required');
52
+ return;
53
+ }
54
+ // this.signingWith = 'email';
55
+ this.email = this.email.trim();
56
+ const email = this.email;
57
+ const firstName = this.firstName.trim();
58
+ const lastName = this.lastName.trim();
59
+ if (!email) {
60
+ alert('Email is a required field');
61
+ return;
62
+ }
63
+ if (!firstName) {
64
+ alert('First name is a required field');
65
+ return;
66
+ }
67
+ if (!lastName) {
68
+ alert('Last name is a required field');
69
+ return;
70
+ }
71
+ this.spaceTitle = this.spaceTitle.trim();
72
+ const spaceTitle = this.spaceTitle;
73
+ if (this.appInfo.requiredSpaceType && !spaceTitle) {
74
+ alert('Company title is a required field');
75
+ this.setFocusToSpaceTitle();
76
+ return;
77
+ }
78
+ localStorage.setItem('emailForSignIn', email);
79
+ const password = this.randomIdService.newRandomId({ len: 9 });
80
+ this.setSigningWith('email');
81
+ try {
82
+ // const auth = getAuth(getApp());
83
+ const auth = this.getFirebaseAuth();
84
+ const userCredential = await createUserWithEmailAndPassword(auth, email, password);
85
+ this.sendVerificationEmail(userCredential);
86
+ userCredential.user
87
+ ?.getIdToken()
88
+ .then((token) => {
89
+ this.sneatApiService.setApiAuthToken(token);
90
+ const request = {
91
+ authProvider: 'password',
92
+ email,
93
+ ianaTimezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
94
+ names: { firstName, lastName },
95
+ space: this.appInfo.requiredSpaceType
96
+ ? {
97
+ type: this.appInfo.requiredSpaceType,
98
+ title: spaceTitle,
99
+ }
100
+ : undefined,
101
+ };
102
+ this.userRecordService.initUserRecord(request).subscribe({
103
+ next: () => this.onLoggedIn(userCredential),
104
+ error: (err) => {
105
+ this.analyticsService.logEvent('FailedToSetUserTitle');
106
+ this.errorLogger.logError(err, 'Failed to set user title', {
107
+ feedback: false,
108
+ });
109
+ this.onLoggedIn(userCredential);
110
+ },
111
+ });
112
+ })
113
+ .catch(this.errorHandler('Failed to get Firebase ID token', 'FirebaseGetIdTokenFailed'));
114
+ }
115
+ catch (e) {
116
+ this.handleError(e, 'Failed to sign up with email', 'FailedToSignUpWithEmail');
117
+ }
118
+ }
119
+ keyupEnter() {
120
+ switch (this.sign) {
121
+ case 'in':
122
+ this.signIn();
123
+ break;
124
+ case 'up':
125
+ this.signUp().catch(() => this.errorHandler('Failed to sign up'));
126
+ break;
127
+ }
128
+ }
129
+ signIn() {
130
+ this.setSigningWith('email');
131
+ this.email = this.email.trim();
132
+ this.wrongPassword = false;
133
+ this.saveEmailForReuse();
134
+ // const auth = getAuth(getApp());
135
+ const auth = this.getFirebaseAuth();
136
+ signInWithEmailAndPassword(auth, this.email, this.password)
137
+ .then((userCredential) => {
138
+ this.onLoggedIn(userCredential); // TODO: add analytics event
139
+ })
140
+ .catch(this.errorHandler('Failed to sign in with email & password', 'email'));
141
+ }
142
+ saveEmailForReuse() {
143
+ localStorage.setItem('emailForSignIn', this.email);
144
+ }
145
+ sendSignInLink() {
146
+ this.setSigningWith('emailLink');
147
+ this.email = this.email.trim();
148
+ this.saveEmailForReuse();
149
+ sendSignInLinkToEmail(this.afAuth, this.email, {
150
+ // url: 'https://dailyscrum.app/pwa/sign-in',
151
+ url: document.baseURI + 'sign-in-from-email-link',
152
+ handleCodeInApp: true,
153
+ })
154
+ .then(() => {
155
+ this.showToast(`Sign-in link has been sent to email: ${this.email}`);
156
+ this.setSigningWith(undefined);
157
+ })
158
+ .catch(this.errorHandler('Failed to send sign in link to email', 'FailedToSendSignInLinkToEmail'));
159
+ }
160
+ showToast(message) {
161
+ this.toastController
162
+ .create({
163
+ message,
164
+ position: 'middle',
165
+ keyboardClose: true,
166
+ duration: 3000,
167
+ color: 'tertiary',
168
+ icon: 'send-outline',
169
+ buttons: [{ icon: 'close', role: 'cancel' }],
170
+ })
171
+ .then((toast) => {
172
+ toast
173
+ .present()
174
+ .catch(this.errorLogger.logErrorHandler('Failed to present toast about password reset email sent success'));
175
+ });
176
+ }
177
+ resetPassword() {
178
+ this.setSigningWith('resetPassword');
179
+ sendPasswordResetEmail(this.afAuth, this.email)
180
+ .then(() => {
181
+ this.setSigningWith(undefined);
182
+ this.showToast(`Password reset link has been sent to email: ${this.email}`);
183
+ })
184
+ .catch(this.errorHandler('Failed to send password reset email', 'FailedToSendPasswordResetEmail'));
185
+ }
186
+ sendVerificationEmail(userCredential) {
187
+ setTimeout(async () => {
188
+ try {
189
+ await sendEmailVerification(userCredential.user);
190
+ }
191
+ catch (e) {
192
+ this.handleError(e, 'Failed to send verification email');
193
+ }
194
+ });
195
+ }
196
+ onLoggedIn(userCredential) {
197
+ this.loggedIn.emit(userCredential);
198
+ }
199
+ errorHandler(m, eventName, eventParams) {
200
+ return (err) => this.handleError(err, m, eventName, eventParams);
201
+ }
202
+ handleError(err, m, eventName, eventParams) {
203
+ this.setSigningWith(undefined);
204
+ if (eventName) {
205
+ this.analyticsService.logEvent(eventName, eventParams);
206
+ }
207
+ if (err.code === 'auth/wrong-password') {
208
+ this.wrongPassword = true;
209
+ return;
210
+ }
211
+ this.errorLogger.logError(err, m, {
212
+ report: !err.code,
213
+ });
214
+ }
215
+ setSigningWith(signingWith) {
216
+ this.signingWith = signingWith;
217
+ this.signingWithChange.emit(signingWith);
218
+ }
219
+ signChanged() {
220
+ this.setFocusToEmail();
221
+ }
222
+ ionViewDidEnter() {
223
+ this.setFocusToEmail();
224
+ }
225
+ setFocusToEmail() {
226
+ this.setFocusToInput(undefined /*this.emailInput*/);
227
+ }
228
+ setFocusToSpaceTitle() {
229
+ this.setFocusToInput(undefined /*this.spaceTitleInput*/);
230
+ }
231
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: EmailLoginFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
232
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: EmailLoginFormComponent, isStandalone: true, selector: "sneat-email-login-form", outputs: { signingWithChange: "signingWithChange", loggedIn: "loggedIn" }, viewQueries: [{ propertyName: "emailInput", first: true, predicate: ["emailInput"], descendants: true, static: true }, { propertyName: "spaceTitleInput", first: true, predicate: ["spaceTitleInput"], descendants: true }], ngImport: i0, template: "<div class=\"ion-padding-horizontal\"></div>\n<ion-card>\n <ion-item-divider color=\"light\" class=\"ion-no-padding\">\n <ion-segment [(ngModel)]=\"sign\" (ionChange)=\"signChanged()\">\n <ion-segment-button value=\"up\">\n <ion-icon name=\"person-add\" class=\"ion-margin-end\"></ion-icon>\n <ion-label>Sign up</ion-label>\n </ion-segment-button>\n <ion-segment-button value=\"in\">\n <ion-icon name=\"enter-outline\" />\n <ion-label>Sign in</ion-label>\n </ion-segment-button>\n </ion-segment>\n </ion-item-divider>\n <!--\t<ion-item-divider color=\"light\">-->\n <!--\t\t<ion-label color=\"medium\" style=\"text-align: center; width: 100%\">-->\n <!--\t\t\tOr login with email-->\n <!--\t\t</ion-label>-->\n <!--\t</ion-item-divider>-->\n <ion-card-content class=\"ion-no-padding\">\n <ion-item>\n <ion-label class=\"ion-margin-end\">\n <ion-icon name=\"mail-outline\" />\n </ion-label>\n <ion-input\n [disabled]=\"!!signingWith\"\n [(ngModel)]=\"email\"\n email\n type=\"email\"\n name=\"email\"\n id=\"email\"\n required\n placeholder=\"my@work-or-personal.email\"\n (keyup.enter)=\"keyupEnter()\"\n />\n </ion-item>\n @switch (sign) {\n @case (\"up\") {\n <ion-item>\n <ion-label class=\"ion-margin-end\">\n <ion-icon name=\"person-add-outline\" />\n </ion-label>\n <ion-input\n name=\"first_name\"\n placeholder=\"First name\"\n [(ngModel)]=\"firstName\"\n type=\"text\"\n required\n (keyup.enter)=\"keyupEnter()\"\n [disabled]=\"!!signingWith\"\n />\n <ion-input\n name=\"last_name\"\n placeholder=\"Last name\"\n [(ngModel)]=\"lastName\"\n type=\"text\"\n required\n (keyup.enter)=\"keyupEnter()\"\n [disabled]=\"!!signingWith\"\n />\n </ion-item>\n @if (appInfo.requiredSpaceType === \"company\") {\n <ion-item>\n <ion-label position=\"stacked\">Company name</ion-label>\n <ion-input\n #teamTitleInput\n placeholder=\"required\"\n required\n [(ngModel)]=\"spaceTitle\"\n />\n </ion-item>\n }\n }\n @case (\"in\") {\n <ion-item>\n <ion-label class=\"ion-margin-end\">\n <ion-icon name=\"key-outline\" />\n </ion-label>\n <ion-input\n [(ngModel)]=\"password\"\n placeholder=\"Password\"\n type=\"password\"\n required\n (keyup.enter)=\"keyupEnter()\"\n [disabled]=\"!!signingWith\"\n />\n <ion-buttons slot=\"end\">\n <ion-button\n color=\"danger\"\n [disabled]=\"!validEmail\"\n (click)=\"resetPassword()\"\n title=\"Send email with a link to reset password \"\n >\n <ion-label>Forgot? Reset!</ion-label>\n </ion-button>\n </ion-buttons>\n </ion-item>\n @if (wrongPassword) {\n <ion-item>\n <ion-label color=\"danger\">Wrong password</ion-label>\n </ion-item>\n }\n }\n }\n </ion-card-content>\n <ion-card-content>\n @switch (sign) {\n @case (\"up\") {\n <ion-button\n [disabled]=\"!!signingWith || !validEmail || (!firstName && !lastName)\"\n (click)=\"signUp()\"\n >\n <ion-icon name=\"enter-outline\" slot=\"start\" />\n @if (signingWith === \"email\") {\n <ion-label>Signing up...</ion-label>\n <ion-spinner\n class=\"ion-margin-start\"\n slot=\"end\"\n name=\"lines-small\"\n />\n } @else {\n <ion-label>Sign up</ion-label>\n }\n </ion-button>\n }\n @case (\"in\") {\n <ion-button\n [disabled]=\"!!signingWith || !validEmail || !password\"\n (click)=\"signIn()\"\n >\n <ion-icon name=\"enter-outline\" slot=\"start\" />\n @if (signingWith === \"email\") {\n <ion-label>Signing in...</ion-label>\n <ion-spinner\n class=\"ion-margin-start\"\n slot=\"end\"\n name=\"lines-small\"\n />\n } @else {\n <ion-label>\n <b>Sign in</b>&nbsp;<ion-text\n color=\"light\"\n style=\"text-transform: none\"\n >with password\n </ion-text>\n </ion-label>\n }\n </ion-button>\n <ion-button disabled=\"disabled\" fill=\"clear\" color=\"medium\">\n or\n </ion-button>\n <ion-button\n size=\"small\"\n [disabled]=\"!validEmail || signingWith\"\n (click)=\"sendSignInLink()\"\n fill=\"outline\"\n color=\"tertiary\"\n >\n <ion-icon name=\"mail-outline\" slot=\"start\" />\n <ion-label>Get sign-in link</ion-label>\n </ion-button>\n }\n }\n </ion-card-content>\n</ion-card>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.EmailValidator, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: ["email"] }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonItemDivider, selector: "ion-item-divider", inputs: ["color", "mode", "sticky"] }, { kind: "component", type: IonSegment, selector: "ion-segment", inputs: ["color", "disabled", "mode", "scrollable", "selectOnFocus", "swipeGesture", "value"] }, { kind: "component", type: IonSegmentButton, selector: "ion-segment-button", inputs: ["contentId", "disabled", "layout", "mode", "type", "value"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonInput, selector: "ion-input", inputs: ["accept", "autocapitalize", "autocomplete", "autocorrect", "autofocus", "clearInput", "clearOnEdit", "color", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "max", "maxlength", "min", "minlength", "mode", "multiple", "name", "pattern", "placeholder", "readonly", "required", "shape", "size", "spellcheck", "step", "type", "value"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }] }); }
233
+ }
234
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: EmailLoginFormComponent, decorators: [{
235
+ type: Component,
236
+ args: [{ selector: 'sneat-email-login-form', imports: [
237
+ FormsModule,
238
+ IonCard,
239
+ IonItemDivider,
240
+ IonSegment,
241
+ IonSegmentButton,
242
+ IonIcon,
243
+ IonLabel,
244
+ IonCardContent,
245
+ IonItem,
246
+ IonInput,
247
+ IonSpinner,
248
+ IonText,
249
+ IonButton,
250
+ IonButtons,
251
+ ], template: "<div class=\"ion-padding-horizontal\"></div>\n<ion-card>\n <ion-item-divider color=\"light\" class=\"ion-no-padding\">\n <ion-segment [(ngModel)]=\"sign\" (ionChange)=\"signChanged()\">\n <ion-segment-button value=\"up\">\n <ion-icon name=\"person-add\" class=\"ion-margin-end\"></ion-icon>\n <ion-label>Sign up</ion-label>\n </ion-segment-button>\n <ion-segment-button value=\"in\">\n <ion-icon name=\"enter-outline\" />\n <ion-label>Sign in</ion-label>\n </ion-segment-button>\n </ion-segment>\n </ion-item-divider>\n <!--\t<ion-item-divider color=\"light\">-->\n <!--\t\t<ion-label color=\"medium\" style=\"text-align: center; width: 100%\">-->\n <!--\t\t\tOr login with email-->\n <!--\t\t</ion-label>-->\n <!--\t</ion-item-divider>-->\n <ion-card-content class=\"ion-no-padding\">\n <ion-item>\n <ion-label class=\"ion-margin-end\">\n <ion-icon name=\"mail-outline\" />\n </ion-label>\n <ion-input\n [disabled]=\"!!signingWith\"\n [(ngModel)]=\"email\"\n email\n type=\"email\"\n name=\"email\"\n id=\"email\"\n required\n placeholder=\"my@work-or-personal.email\"\n (keyup.enter)=\"keyupEnter()\"\n />\n </ion-item>\n @switch (sign) {\n @case (\"up\") {\n <ion-item>\n <ion-label class=\"ion-margin-end\">\n <ion-icon name=\"person-add-outline\" />\n </ion-label>\n <ion-input\n name=\"first_name\"\n placeholder=\"First name\"\n [(ngModel)]=\"firstName\"\n type=\"text\"\n required\n (keyup.enter)=\"keyupEnter()\"\n [disabled]=\"!!signingWith\"\n />\n <ion-input\n name=\"last_name\"\n placeholder=\"Last name\"\n [(ngModel)]=\"lastName\"\n type=\"text\"\n required\n (keyup.enter)=\"keyupEnter()\"\n [disabled]=\"!!signingWith\"\n />\n </ion-item>\n @if (appInfo.requiredSpaceType === \"company\") {\n <ion-item>\n <ion-label position=\"stacked\">Company name</ion-label>\n <ion-input\n #teamTitleInput\n placeholder=\"required\"\n required\n [(ngModel)]=\"spaceTitle\"\n />\n </ion-item>\n }\n }\n @case (\"in\") {\n <ion-item>\n <ion-label class=\"ion-margin-end\">\n <ion-icon name=\"key-outline\" />\n </ion-label>\n <ion-input\n [(ngModel)]=\"password\"\n placeholder=\"Password\"\n type=\"password\"\n required\n (keyup.enter)=\"keyupEnter()\"\n [disabled]=\"!!signingWith\"\n />\n <ion-buttons slot=\"end\">\n <ion-button\n color=\"danger\"\n [disabled]=\"!validEmail\"\n (click)=\"resetPassword()\"\n title=\"Send email with a link to reset password \"\n >\n <ion-label>Forgot? Reset!</ion-label>\n </ion-button>\n </ion-buttons>\n </ion-item>\n @if (wrongPassword) {\n <ion-item>\n <ion-label color=\"danger\">Wrong password</ion-label>\n </ion-item>\n }\n }\n }\n </ion-card-content>\n <ion-card-content>\n @switch (sign) {\n @case (\"up\") {\n <ion-button\n [disabled]=\"!!signingWith || !validEmail || (!firstName && !lastName)\"\n (click)=\"signUp()\"\n >\n <ion-icon name=\"enter-outline\" slot=\"start\" />\n @if (signingWith === \"email\") {\n <ion-label>Signing up...</ion-label>\n <ion-spinner\n class=\"ion-margin-start\"\n slot=\"end\"\n name=\"lines-small\"\n />\n } @else {\n <ion-label>Sign up</ion-label>\n }\n </ion-button>\n }\n @case (\"in\") {\n <ion-button\n [disabled]=\"!!signingWith || !validEmail || !password\"\n (click)=\"signIn()\"\n >\n <ion-icon name=\"enter-outline\" slot=\"start\" />\n @if (signingWith === \"email\") {\n <ion-label>Signing in...</ion-label>\n <ion-spinner\n class=\"ion-margin-start\"\n slot=\"end\"\n name=\"lines-small\"\n />\n } @else {\n <ion-label>\n <b>Sign in</b>&nbsp;<ion-text\n color=\"light\"\n style=\"text-transform: none\"\n >with password\n </ion-text>\n </ion-label>\n }\n </ion-button>\n <ion-button disabled=\"disabled\" fill=\"clear\" color=\"medium\">\n or\n </ion-button>\n <ion-button\n size=\"small\"\n [disabled]=\"!validEmail || signingWith\"\n (click)=\"sendSignInLink()\"\n fill=\"outline\"\n color=\"tertiary\"\n >\n <ion-icon name=\"mail-outline\" slot=\"start\" />\n <ion-label>Get sign-in link</ion-label>\n </ion-button>\n }\n }\n </ion-card-content>\n</ion-card>\n" }]
252
+ }], ctorParameters: () => [], propDecorators: { signingWithChange: [{
253
+ type: Output
254
+ }], loggedIn: [{
255
+ type: Output
256
+ }], emailInput: [{
257
+ type: ViewChild,
258
+ args: ['emailInput', { static: true }]
259
+ }], spaceTitleInput: [{
260
+ type: ViewChild,
261
+ args: ['spaceTitleInput', { static: false }]
262
+ }] } });
263
+ //# sourceMappingURL=email-login-form.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email-login-form.component.js","sourceRoot":"","sources":["../../../../../../../../../libs/auth/ui/src/lib/pages/login-page/email-login-form/email-login-form.component.ts","../../../../../../../../../libs/auth/ui/src/lib/pages/login-page/email-login-form/email-login-form.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n EventEmitter,\n Output,\n ViewChild,\n inject,\n} from '@angular/core';\n// import { getApp } from '@angular/fire/app';\n// import { getAuth } from '@angular/fire/auth';\nimport {\n Auth,\n sendPasswordResetEmail,\n sendSignInLinkToEmail,\n} from '@angular/fire/auth';\nimport { FormsModule } from '@angular/forms';\nimport {\n ToastController,\n IonButton,\n IonButtons,\n IonCard,\n IonCardContent,\n IonIcon,\n IonInput,\n IonItem,\n IonItemDivider,\n IonLabel,\n IonSegment,\n IonSegmentButton,\n IonSpinner,\n IonText,\n} from '@ionic/angular/standalone';\nimport { SneatApiService } from '@sneat/api';\nimport { IInitUserRecordRequest, UserRecordService } from '@sneat/auth-core';\nimport { createSetFocusToInput } from '@sneat/ui';\nimport {\n AnalyticsService,\n APP_INFO,\n IAnalyticsService,\n IAppInfo,\n} from '@sneat/core';\nimport { ErrorLogger, IErrorLogger } from '@sneat/core';\nimport { RandomIdService } from '@sneat/random';\nimport { UserCredential, sendEmailVerification } from 'firebase/auth';\nimport { FirebaseError } from 'firebase/app';\n\nexport type EmailFormSigningWith = 'email' | 'emailLink' | 'resetPassword';\nimport {\n createUserWithEmailAndPassword,\n signInWithEmailAndPassword,\n} from 'firebase/auth';\n\n@Component({\n selector: 'sneat-email-login-form',\n templateUrl: 'email-login-form.component.html',\n imports: [\n FormsModule,\n IonCard,\n IonItemDivider,\n IonSegment,\n IonSegmentButton,\n IonIcon,\n IonLabel,\n IonCardContent,\n IonItem,\n IonInput,\n IonSpinner,\n IonText,\n IonButton,\n IonButtons,\n ],\n})\nexport class EmailLoginFormComponent {\n readonly appInfo = inject<IAppInfo>(APP_INFO);\n private readonly analyticsService =\n inject<IAnalyticsService>(AnalyticsService);\n private readonly errorLogger = inject<IErrorLogger>(ErrorLogger);\n private readonly toastController = inject(ToastController);\n private readonly afAuth = inject(Auth);\n private readonly randomIdService = inject(RandomIdService);\n private readonly sneatApiService = inject(SneatApiService);\n private readonly userRecordService = inject(UserRecordService);\n\n protected sign: 'in' | 'up' = 'up'; // TODO: document here what 'in' & 'up' means\n protected email = '';\n protected password = '';\n protected firstName = '';\n protected lastName = '';\n protected spaceTitle = '';\n protected wrongPassword = false;\n\n protected signingWith?: EmailFormSigningWith;\n\n @Output() readonly signingWithChange = new EventEmitter<\n EmailFormSigningWith | undefined\n >();\n @Output() readonly loggedIn = new EventEmitter<UserCredential>();\n\n @ViewChild('emailInput', { static: true }) emailInput?: IonInput;\n @ViewChild('spaceTitleInput', { static: false }) spaceTitleInput?: IonInput;\n\n public readonly setFocusToInput = createSetFocusToInput(this.errorLogger);\n\n constructor() {\n this.email = localStorage.getItem('emailForSignIn') || '';\n if (this.email) {\n this.sign = 'in';\n }\n }\n\n public get validEmail(): boolean {\n const email = this.email,\n i = email?.indexOf('@');\n return i > 0 && i < email.length - 1;\n }\n\n public getFirebaseAuth(): Auth {\n return this.afAuth as unknown as Auth; // TODO: pending https://github.com/angular/angularfire/pull/3402\n }\n\n public async signUp(): Promise<void> {\n if (!this.firstName) {\n // this.toaster.showToast('Full name is required');\n return;\n }\n // this.signingWith = 'email';\n this.email = this.email.trim();\n const email = this.email;\n const firstName = this.firstName.trim();\n const lastName = this.lastName.trim();\n if (!email) {\n alert('Email is a required field');\n return;\n }\n if (!firstName) {\n alert('First name is a required field');\n return;\n }\n if (!lastName) {\n alert('Last name is a required field');\n return;\n }\n this.spaceTitle = this.spaceTitle.trim();\n const spaceTitle = this.spaceTitle;\n if (this.appInfo.requiredSpaceType && !spaceTitle) {\n alert('Company title is a required field');\n this.setFocusToSpaceTitle();\n return;\n }\n localStorage.setItem('emailForSignIn', email);\n const password = this.randomIdService.newRandomId({ len: 9 });\n\n this.setSigningWith('email');\n try {\n // const auth = getAuth(getApp());\n const auth = this.getFirebaseAuth();\n const userCredential = await createUserWithEmailAndPassword(\n auth,\n email,\n password,\n );\n this.sendVerificationEmail(userCredential);\n userCredential.user\n ?.getIdToken()\n .then((token) => {\n this.sneatApiService.setApiAuthToken(token);\n const request: IInitUserRecordRequest = {\n authProvider: 'password',\n email,\n ianaTimezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n names: { firstName, lastName },\n space: this.appInfo.requiredSpaceType\n ? {\n type: this.appInfo.requiredSpaceType,\n title: spaceTitle,\n }\n : undefined,\n };\n this.userRecordService.initUserRecord(request).subscribe({\n next: () => this.onLoggedIn(userCredential),\n error: (err) => {\n this.analyticsService.logEvent('FailedToSetUserTitle');\n this.errorLogger.logError(err, 'Failed to set user title', {\n feedback: false,\n });\n this.onLoggedIn(userCredential);\n },\n });\n })\n .catch(\n this.errorHandler(\n 'Failed to get Firebase ID token',\n 'FirebaseGetIdTokenFailed',\n ),\n );\n } catch (e) {\n this.handleError(\n e,\n 'Failed to sign up with email',\n 'FailedToSignUpWithEmail',\n );\n }\n }\n\n public keyupEnter(): void {\n switch (this.sign) {\n case 'in':\n this.signIn();\n break;\n case 'up':\n this.signUp().catch(() => this.errorHandler('Failed to sign up'));\n break;\n }\n }\n\n public signIn(): void {\n this.setSigningWith('email');\n this.email = this.email.trim();\n this.wrongPassword = false;\n this.saveEmailForReuse();\n // const auth = getAuth(getApp());\n const auth = this.getFirebaseAuth();\n signInWithEmailAndPassword(auth, this.email, this.password)\n .then((userCredential) => {\n this.onLoggedIn(userCredential); // TODO: add analytics event\n })\n .catch(\n this.errorHandler('Failed to sign in with email & password', 'email'),\n );\n }\n\n private saveEmailForReuse(): void {\n localStorage.setItem('emailForSignIn', this.email);\n }\n\n public sendSignInLink(): void {\n this.setSigningWith('emailLink');\n this.email = this.email.trim();\n this.saveEmailForReuse();\n sendSignInLinkToEmail(this.afAuth, this.email, {\n // url: 'https://dailyscrum.app/pwa/sign-in',\n url: document.baseURI + 'sign-in-from-email-link',\n handleCodeInApp: true,\n })\n .then(() => {\n this.showToast(`Sign-in link has been sent to email: ${this.email}`);\n this.setSigningWith(undefined);\n })\n .catch(\n this.errorHandler(\n 'Failed to send sign in link to email',\n 'FailedToSendSignInLinkToEmail',\n ),\n );\n }\n\n private showToast(message: string): void {\n this.toastController\n .create({\n message,\n position: 'middle',\n keyboardClose: true,\n duration: 3000,\n color: 'tertiary',\n icon: 'send-outline',\n buttons: [{ icon: 'close', role: 'cancel' }],\n })\n .then((toast) => {\n toast\n .present()\n .catch(\n this.errorLogger.logErrorHandler(\n 'Failed to present toast about password reset email sent success',\n ),\n );\n });\n }\n\n public resetPassword(): void {\n this.setSigningWith('resetPassword');\n sendPasswordResetEmail(this.afAuth, this.email)\n .then(() => {\n this.setSigningWith(undefined);\n this.showToast(\n `Password reset link has been sent to email: ${this.email}`,\n );\n })\n .catch(\n this.errorHandler(\n 'Failed to send password reset email',\n 'FailedToSendPasswordResetEmail',\n ),\n );\n }\n\n private sendVerificationEmail(userCredential: UserCredential): void {\n setTimeout(async () => {\n try {\n await sendEmailVerification(userCredential.user);\n } catch (e) {\n this.handleError(e, 'Failed to send verification email');\n }\n });\n }\n\n private onLoggedIn(userCredential: UserCredential): void {\n this.loggedIn.emit(userCredential);\n }\n\n private errorHandler(\n m: string,\n eventName?: string,\n eventParams?: Record<string, string>,\n ): (err: unknown) => void {\n return (err) => this.handleError(err, m, eventName, eventParams);\n }\n\n private handleError(\n err: unknown,\n m: string,\n eventName?: string,\n eventParams?: Record<string, string>,\n ): void {\n this.setSigningWith(undefined);\n if (eventName) {\n this.analyticsService.logEvent(eventName, eventParams);\n }\n if ((err as FirebaseError).code === 'auth/wrong-password') {\n this.wrongPassword = true;\n return;\n }\n this.errorLogger.logError(err, m, {\n report: !(err as { code: unknown }).code,\n });\n }\n\n private setSigningWith(signingWith?: EmailFormSigningWith): void {\n this.signingWith = signingWith;\n this.signingWithChange.emit(signingWith);\n }\n\n signChanged(): void {\n this.setFocusToEmail();\n }\n\n ionViewDidEnter(): void {\n this.setFocusToEmail();\n }\n\n private setFocusToEmail(): void {\n this.setFocusToInput(undefined /*this.emailInput*/);\n }\n\n private setFocusToSpaceTitle(): void {\n this.setFocusToInput(undefined /*this.spaceTitleInput*/);\n }\n}\n","<div class=\"ion-padding-horizontal\"></div>\n<ion-card>\n <ion-item-divider color=\"light\" class=\"ion-no-padding\">\n <ion-segment [(ngModel)]=\"sign\" (ionChange)=\"signChanged()\">\n <ion-segment-button value=\"up\">\n <ion-icon name=\"person-add\" class=\"ion-margin-end\"></ion-icon>\n <ion-label>Sign up</ion-label>\n </ion-segment-button>\n <ion-segment-button value=\"in\">\n <ion-icon name=\"enter-outline\" />\n <ion-label>Sign in</ion-label>\n </ion-segment-button>\n </ion-segment>\n </ion-item-divider>\n <!--\t<ion-item-divider color=\"light\">-->\n <!--\t\t<ion-label color=\"medium\" style=\"text-align: center; width: 100%\">-->\n <!--\t\t\tOr login with email-->\n <!--\t\t</ion-label>-->\n <!--\t</ion-item-divider>-->\n <ion-card-content class=\"ion-no-padding\">\n <ion-item>\n <ion-label class=\"ion-margin-end\">\n <ion-icon name=\"mail-outline\" />\n </ion-label>\n <ion-input\n [disabled]=\"!!signingWith\"\n [(ngModel)]=\"email\"\n email\n type=\"email\"\n name=\"email\"\n id=\"email\"\n required\n placeholder=\"my@work-or-personal.email\"\n (keyup.enter)=\"keyupEnter()\"\n />\n </ion-item>\n @switch (sign) {\n @case (\"up\") {\n <ion-item>\n <ion-label class=\"ion-margin-end\">\n <ion-icon name=\"person-add-outline\" />\n </ion-label>\n <ion-input\n name=\"first_name\"\n placeholder=\"First name\"\n [(ngModel)]=\"firstName\"\n type=\"text\"\n required\n (keyup.enter)=\"keyupEnter()\"\n [disabled]=\"!!signingWith\"\n />\n <ion-input\n name=\"last_name\"\n placeholder=\"Last name\"\n [(ngModel)]=\"lastName\"\n type=\"text\"\n required\n (keyup.enter)=\"keyupEnter()\"\n [disabled]=\"!!signingWith\"\n />\n </ion-item>\n @if (appInfo.requiredSpaceType === \"company\") {\n <ion-item>\n <ion-label position=\"stacked\">Company name</ion-label>\n <ion-input\n #teamTitleInput\n placeholder=\"required\"\n required\n [(ngModel)]=\"spaceTitle\"\n />\n </ion-item>\n }\n }\n @case (\"in\") {\n <ion-item>\n <ion-label class=\"ion-margin-end\">\n <ion-icon name=\"key-outline\" />\n </ion-label>\n <ion-input\n [(ngModel)]=\"password\"\n placeholder=\"Password\"\n type=\"password\"\n required\n (keyup.enter)=\"keyupEnter()\"\n [disabled]=\"!!signingWith\"\n />\n <ion-buttons slot=\"end\">\n <ion-button\n color=\"danger\"\n [disabled]=\"!validEmail\"\n (click)=\"resetPassword()\"\n title=\"Send email with a link to reset password \"\n >\n <ion-label>Forgot? Reset!</ion-label>\n </ion-button>\n </ion-buttons>\n </ion-item>\n @if (wrongPassword) {\n <ion-item>\n <ion-label color=\"danger\">Wrong password</ion-label>\n </ion-item>\n }\n }\n }\n </ion-card-content>\n <ion-card-content>\n @switch (sign) {\n @case (\"up\") {\n <ion-button\n [disabled]=\"!!signingWith || !validEmail || (!firstName && !lastName)\"\n (click)=\"signUp()\"\n >\n <ion-icon name=\"enter-outline\" slot=\"start\" />\n @if (signingWith === \"email\") {\n <ion-label>Signing up...</ion-label>\n <ion-spinner\n class=\"ion-margin-start\"\n slot=\"end\"\n name=\"lines-small\"\n />\n } @else {\n <ion-label>Sign up</ion-label>\n }\n </ion-button>\n }\n @case (\"in\") {\n <ion-button\n [disabled]=\"!!signingWith || !validEmail || !password\"\n (click)=\"signIn()\"\n >\n <ion-icon name=\"enter-outline\" slot=\"start\" />\n @if (signingWith === \"email\") {\n <ion-label>Signing in...</ion-label>\n <ion-spinner\n class=\"ion-margin-start\"\n slot=\"end\"\n name=\"lines-small\"\n />\n } @else {\n <ion-label>\n <b>Sign in</b>&nbsp;<ion-text\n color=\"light\"\n style=\"text-transform: none\"\n >with password\n </ion-text>\n </ion-label>\n }\n </ion-button>\n <ion-button disabled=\"disabled\" fill=\"clear\" color=\"medium\">\n or\n </ion-button>\n <ion-button\n size=\"small\"\n [disabled]=\"!validEmail || signingWith\"\n (click)=\"sendSignInLink()\"\n fill=\"outline\"\n color=\"tertiary\"\n >\n <ion-icon name=\"mail-outline\" slot=\"start\" />\n <ion-label>Get sign-in link</ion-label>\n </ion-button>\n }\n }\n </ion-card-content>\n</ion-card>\n"]}
@@ -0,0 +1,143 @@
1
+ import { Component, signal, inject } from '@angular/core';
2
+ import { FormsModule } from '@angular/forms';
3
+ import { ActivatedRoute } from '@angular/router';
4
+ import { Capacitor } from '@capacitor/core';
5
+ import { NavController, IonBackButton, IonButtons, IonCard, IonCardContent, IonCol, IonContent, IonGrid, IonHeader, IonIcon, IonItem, IonItemDivider, IonLabel, IonList, IonRow, IonSpinner, IonText, IonTitle, IonToolbar, } from '@ionic/angular/standalone';
6
+ import { AuthStatuses, LoginEventsHandler, SneatAuthStateService, } from '@sneat/auth-core';
7
+ import { SneatUserService } from '@sneat/auth-core';
8
+ import { AnalyticsService, APP_INFO, } from '@sneat/core';
9
+ import { RandomIdService } from '@sneat/random';
10
+ import { ClassName, SneatBaseComponent } from '@sneat/ui';
11
+ import { Subject, takeUntil } from 'rxjs';
12
+ import { EmailLoginFormComponent, } from './email-login-form/email-login-form.component';
13
+ import { LoginWithTelegramComponent } from './login-with-telegram.component';
14
+ import * as i0 from "@angular/core";
15
+ export class LoginPageComponent extends SneatBaseComponent {
16
+ constructor() {
17
+ super();
18
+ this.analyticsService = inject(AnalyticsService);
19
+ this.route = inject(ActivatedRoute);
20
+ this.navController = inject(NavController);
21
+ this.userService = inject(SneatUserService);
22
+ this.authStateService = inject(SneatAuthStateService);
23
+ this.appInfo = inject(APP_INFO);
24
+ this.loginEventsHandler = inject(LoginEventsHandler, { optional: true });
25
+ this.signingWith = signal(undefined, ...(ngDevMode ? [{ debugName: "signingWith" }] : []));
26
+ this.isNativePlatform = Capacitor.isNativePlatform();
27
+ const appInfo = this.appInfo;
28
+ this.appTitle = appInfo.appTitle || 'Sneat.app';
29
+ if (location.hash.startsWith('#/')) {
30
+ this.redirectTo = location.hash.substring(1);
31
+ }
32
+ this.to = this.route.snapshot.queryParams['to']; // should we subscribe? I believe no.
33
+ const action = location.hash.match(/[#&]action=(\w+)/);
34
+ this.action = action?.[1];
35
+ const userRecordLoaded = new Subject();
36
+ this.userService.userState
37
+ .pipe(takeUntil(userRecordLoaded), this.takeUntilDestroyed())
38
+ .subscribe({
39
+ next: (userState) => {
40
+ if (userState.record) {
41
+ userRecordLoaded.next();
42
+ }
43
+ else {
44
+ return;
45
+ }
46
+ const redirectTo = this.redirectTo || '/'; // TODO: default one should be app specific.
47
+ this.navController
48
+ .navigateRoot(redirectTo)
49
+ .catch(this.errorLogger.logErrorHandler('Failed to navigate back to ' + redirectTo));
50
+ },
51
+ error: this.errorHandler('Failed to get user state after login'),
52
+ });
53
+ }
54
+ onEmailFormStatusChanged(signingWith) {
55
+ this.signingWith.set(signingWith);
56
+ }
57
+ async loginWith(provider) {
58
+ this.signingWith.set(provider);
59
+ try {
60
+ await this.authStateService.signInWith(provider);
61
+ // We do not reset this.signingWith in case of succesful sign in as we should redirect from login page
62
+ // and not to allow user to do a double sign-in.
63
+ }
64
+ catch (e) {
65
+ const errMsg = e.errorMessage;
66
+ if (errMsg !== 'The user canceled the sign-in flow.' &&
67
+ !errMsg?.includes('com.apple.AuthenticationServices.AuthorizationError error 1001.')) {
68
+ this.errorLogger.logError(e, `Failed to sign-in with ${provider}`);
69
+ }
70
+ this.signingWith.set(undefined);
71
+ }
72
+ }
73
+ onLoggedIn(userCredential) {
74
+ this.signingWith.set(undefined);
75
+ if (!userCredential.user) {
76
+ return;
77
+ }
78
+ if (userCredential.user.email) {
79
+ const prevEmail = localStorage.getItem('emailForSignIn') || '';
80
+ if (!prevEmail) {
81
+ localStorage.setItem('emailForSignIn', userCredential.user.email);
82
+ }
83
+ }
84
+ const authState = {
85
+ status: AuthStatuses.authenticated,
86
+ user: userCredential.user,
87
+ };
88
+ this.userService.onUserSignedIn(authState);
89
+ }
90
+ errorHandler(m, eventName, eventParams) {
91
+ return (err) => this.handleError(err, m, eventName, eventParams);
92
+ }
93
+ handleError(err, m, eventName, eventParams) {
94
+ if (eventName) {
95
+ this.analyticsService.logEvent(eventName, eventParams);
96
+ }
97
+ this.errorLogger.logError(err, m, {
98
+ report: !err.code,
99
+ });
100
+ this.signingWith.set(undefined);
101
+ }
102
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: LoginPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
103
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: LoginPageComponent, isStandalone: true, selector: "sneat-login", providers: [
104
+ {
105
+ provide: ClassName,
106
+ useValue: 'LoginPageComponent',
107
+ },
108
+ RandomIdService,
109
+ ], usesInheritance: true, ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"light\">\n <ion-buttons slot=\"start\">\n <ion-back-button />\n </ion-buttons>\n <ion-title>Login &#64; {{ appTitle }}</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content id=\"main-content\">\n @if (to) {\n <ion-card color=\"tertiary\">\n <ion-card-content>\n <p>Please sign in to join a team</p>\n </ion-card-content>\n </ion-card>\n }\n\n <p style=\"text-align: center; font-size: smaller\">\n <ion-text color=\"medium\">\n This app is free to use &\n <a target=\"_blank\" href=\"https://github.com/sneat-co\">open source</a>.\n </ion-text>\n </p>\n\n <sneat-email-login-form\n (signingWithChange)=\"onEmailFormStatusChanged()\"\n (loggedIn)=\"onLoggedIn($event)\"\n />\n\n <ion-card>\n <ion-item-divider color=\"light\">\n <ion-label\n color=\"medium\"\n style=\"text-align: center; width: 100%; font-weight: bold\"\n >\n Quick login\n </ion-label>\n </ion-item-divider>\n <ion-grid class=\"ion-grid-layout\">\n @if (!isNativePlatform) {\n <ion-row>\n <ion-col>\n <div class=\"ion-padding\" style=\"text-align: center\">\n <sneat-login-with-telegram />\n </div>\n </ion-col>\n </ion-row>\n }\n <ion-row>\n <ion-col size=\"12\" size-md=\"6\">\n <ion-list lines=\"none\">\n <ion-item\n (click)=\"loginWith('google.com')\"\n tappable\n [disabled]=\"!!signingWith()\"\n >\n @if (signingWith() === \"google.com\") {\n <ion-spinner slot=\"start\" name=\"lines-small\" />\n } @else {\n <ion-icon color=\"danger\" name=\"logo-google\" slot=\"start\" />\n }\n <ion-label color=\"danger\"> Login with Google</ion-label>\n </ion-item>\n </ion-list>\n </ion-col>\n <ion-col size=\"12\" size-md=\"6\">\n <ion-list lines=\"none\">\n <ion-item\n (click)=\"loginWith('apple.com')\"\n tappable\n [disabled]=\"!!signingWith()\"\n >\n @if (signingWith() === \"apple.com\") {\n <ion-spinner slot=\"start\" name=\"lines-small\" />\n } @else {\n <ion-icon color=\"dark\" name=\"logo-apple\" slot=\"start\" />\n }\n\n <ion-label color=\"dark\"> Login with Apple</ion-label>\n </ion-item>\n </ion-list>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col size=\"12\" size-md=\"6\">\n <ion-list lines=\"none\">\n <ion-item\n (click)=\"loginWith('microsoft.com')\"\n tappable\n [disabled]=\"!!signingWith()\"\n >\n @if (signingWith() === \"microsoft.com\") {\n <ion-spinner\n slot=\"start\"\n color=\"secondary\"\n name=\"lines-small\"\n />\n } @else {\n <ion-icon slot=\"start\" color=\"secondary\" name=\"logo-windows\" />\n }\n <ion-label color=\"secondary\">Login with Microsoft</ion-label>\n </ion-item>\n </ion-list>\n </ion-col>\n <ion-col size=\"12\" size-md=\"6\">\n <ion-list lines=\"none\">\n <ion-item\n (click)=\"loginWith('facebook.com')\"\n tappable\n [disabled]=\"!!signingWith()\"\n >\n @if (signingWith() === \"facebook.com\") {\n <ion-spinner slot=\"start\" color=\"primary\" name=\"lines-small\" />\n } @else {\n <ion-icon slot=\"start\" color=\"primary\" name=\"logo-facebook\" />\n }\n <ion-label color=\"primary\">Login with Facebook</ion-label>\n </ion-item>\n </ion-list>\n </ion-col>\n </ion-row>\n </ion-grid>\n <!--\t\t\t\t\t\t<ion-item-->\n <!--\t\t\t\t\t\t\t(click)=\"loginWith('github.com')\"-->\n <!--\t\t\t\t\t\t\ttappable-->\n <!--\t\t\t\t\t\t\t[disabled]=\"!!signingWith()\"-->\n <!--\t\t\t\t\t\t>-->\n <!--\t\t\t\t\t\t\t@if (signingWith() === \"github.com\") {-->\n <!--\t\t\t\t\t\t\t\t<ion-spinner slot=\"start\" color=\"tertiary\" name=\"lines-small\" />-->\n <!--\t\t\t\t\t\t\t} @else {-->\n <!--\t\t\t\t\t\t\t\t<ion-icon slot=\"start\" color=\"tertiary\" name=\"logo-github\" />-->\n <!--\t\t\t\t\t\t\t}-->\n <!--\t\t\t\t\t\t\t<ion-label color=\"tertiary\">Login with GitHub</ion-label>-->\n <!--\t\t\t\t\t\t</ion-item>-->\n </ion-card>\n\n <p style=\"text-align: center; font-size: smaller\">\n <ion-text color=\"medium\">\n If any issues get\n <a href=\"mailto:help@sneat.app?subject=Problem+with+login+at+Sneat.app\"\n >help&#64;sneat.app</a\n >\n </ion-text>\n </p>\n</ion-content>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "component", type: LoginWithTelegramComponent, selector: "sneat-login-with-telegram", inputs: ["isUserAuthenticated", "botID", "size", "requestAccess", "userPic"] }, { kind: "component", type: EmailLoginFormComponent, selector: "sneat-email-login-form", outputs: ["signingWithChange", "loggedIn"] }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonBackButton, selector: "ion-back-button" }, { kind: "component", type: IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonItemDivider, selector: "ion-item-divider", inputs: ["color", "mode", "sticky"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonRow, selector: "ion-row" }, { kind: "component", type: IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: IonGrid, selector: "ion-grid", inputs: ["fixed"] }] }); }
110
+ }
111
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: LoginPageComponent, decorators: [{
112
+ type: Component,
113
+ args: [{ selector: 'sneat-login', imports: [
114
+ FormsModule,
115
+ LoginWithTelegramComponent,
116
+ EmailLoginFormComponent,
117
+ IonHeader,
118
+ IonToolbar,
119
+ IonButtons,
120
+ IonBackButton,
121
+ IonTitle,
122
+ IonContent,
123
+ IonCardContent,
124
+ IonText,
125
+ IonCard,
126
+ IonItemDivider,
127
+ IonLabel,
128
+ IonRow,
129
+ IonCol,
130
+ IonItem,
131
+ IonSpinner,
132
+ IonIcon,
133
+ IonList,
134
+ IonGrid,
135
+ ], providers: [
136
+ {
137
+ provide: ClassName,
138
+ useValue: 'LoginPageComponent',
139
+ },
140
+ RandomIdService,
141
+ ], template: "<ion-header>\n <ion-toolbar color=\"light\">\n <ion-buttons slot=\"start\">\n <ion-back-button />\n </ion-buttons>\n <ion-title>Login &#64; {{ appTitle }}</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content id=\"main-content\">\n @if (to) {\n <ion-card color=\"tertiary\">\n <ion-card-content>\n <p>Please sign in to join a team</p>\n </ion-card-content>\n </ion-card>\n }\n\n <p style=\"text-align: center; font-size: smaller\">\n <ion-text color=\"medium\">\n This app is free to use &\n <a target=\"_blank\" href=\"https://github.com/sneat-co\">open source</a>.\n </ion-text>\n </p>\n\n <sneat-email-login-form\n (signingWithChange)=\"onEmailFormStatusChanged()\"\n (loggedIn)=\"onLoggedIn($event)\"\n />\n\n <ion-card>\n <ion-item-divider color=\"light\">\n <ion-label\n color=\"medium\"\n style=\"text-align: center; width: 100%; font-weight: bold\"\n >\n Quick login\n </ion-label>\n </ion-item-divider>\n <ion-grid class=\"ion-grid-layout\">\n @if (!isNativePlatform) {\n <ion-row>\n <ion-col>\n <div class=\"ion-padding\" style=\"text-align: center\">\n <sneat-login-with-telegram />\n </div>\n </ion-col>\n </ion-row>\n }\n <ion-row>\n <ion-col size=\"12\" size-md=\"6\">\n <ion-list lines=\"none\">\n <ion-item\n (click)=\"loginWith('google.com')\"\n tappable\n [disabled]=\"!!signingWith()\"\n >\n @if (signingWith() === \"google.com\") {\n <ion-spinner slot=\"start\" name=\"lines-small\" />\n } @else {\n <ion-icon color=\"danger\" name=\"logo-google\" slot=\"start\" />\n }\n <ion-label color=\"danger\"> Login with Google</ion-label>\n </ion-item>\n </ion-list>\n </ion-col>\n <ion-col size=\"12\" size-md=\"6\">\n <ion-list lines=\"none\">\n <ion-item\n (click)=\"loginWith('apple.com')\"\n tappable\n [disabled]=\"!!signingWith()\"\n >\n @if (signingWith() === \"apple.com\") {\n <ion-spinner slot=\"start\" name=\"lines-small\" />\n } @else {\n <ion-icon color=\"dark\" name=\"logo-apple\" slot=\"start\" />\n }\n\n <ion-label color=\"dark\"> Login with Apple</ion-label>\n </ion-item>\n </ion-list>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col size=\"12\" size-md=\"6\">\n <ion-list lines=\"none\">\n <ion-item\n (click)=\"loginWith('microsoft.com')\"\n tappable\n [disabled]=\"!!signingWith()\"\n >\n @if (signingWith() === \"microsoft.com\") {\n <ion-spinner\n slot=\"start\"\n color=\"secondary\"\n name=\"lines-small\"\n />\n } @else {\n <ion-icon slot=\"start\" color=\"secondary\" name=\"logo-windows\" />\n }\n <ion-label color=\"secondary\">Login with Microsoft</ion-label>\n </ion-item>\n </ion-list>\n </ion-col>\n <ion-col size=\"12\" size-md=\"6\">\n <ion-list lines=\"none\">\n <ion-item\n (click)=\"loginWith('facebook.com')\"\n tappable\n [disabled]=\"!!signingWith()\"\n >\n @if (signingWith() === \"facebook.com\") {\n <ion-spinner slot=\"start\" color=\"primary\" name=\"lines-small\" />\n } @else {\n <ion-icon slot=\"start\" color=\"primary\" name=\"logo-facebook\" />\n }\n <ion-label color=\"primary\">Login with Facebook</ion-label>\n </ion-item>\n </ion-list>\n </ion-col>\n </ion-row>\n </ion-grid>\n <!--\t\t\t\t\t\t<ion-item-->\n <!--\t\t\t\t\t\t\t(click)=\"loginWith('github.com')\"-->\n <!--\t\t\t\t\t\t\ttappable-->\n <!--\t\t\t\t\t\t\t[disabled]=\"!!signingWith()\"-->\n <!--\t\t\t\t\t\t>-->\n <!--\t\t\t\t\t\t\t@if (signingWith() === \"github.com\") {-->\n <!--\t\t\t\t\t\t\t\t<ion-spinner slot=\"start\" color=\"tertiary\" name=\"lines-small\" />-->\n <!--\t\t\t\t\t\t\t} @else {-->\n <!--\t\t\t\t\t\t\t\t<ion-icon slot=\"start\" color=\"tertiary\" name=\"logo-github\" />-->\n <!--\t\t\t\t\t\t\t}-->\n <!--\t\t\t\t\t\t\t<ion-label color=\"tertiary\">Login with GitHub</ion-label>-->\n <!--\t\t\t\t\t\t</ion-item>-->\n </ion-card>\n\n <p style=\"text-align: center; font-size: smaller\">\n <ion-text color=\"medium\">\n If any issues get\n <a href=\"mailto:help@sneat.app?subject=Problem+with+login+at+Sneat.app\"\n >help&#64;sneat.app</a\n >\n </ion-text>\n </p>\n</ion-content>\n" }]
142
+ }], ctorParameters: () => [] });
143
+ //# sourceMappingURL=login-page.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login-page.component.js","sourceRoot":"","sources":["../../../../../../../../libs/auth/ui/src/lib/pages/login-page/login-page.component.ts","../../../../../../../../libs/auth/ui/src/lib/pages/login-page/login-page.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, signal, inject } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { ActivatedRoute } from '@angular/router';\nimport { Capacitor } from '@capacitor/core';\nimport {\n NavController,\n IonBackButton,\n IonButtons,\n IonCard,\n IonCardContent,\n IonCol,\n IonContent,\n IonGrid,\n IonHeader,\n IonIcon,\n IonItem,\n IonItemDivider,\n IonLabel,\n IonList,\n IonRow,\n IonSpinner,\n IonText,\n IonTitle,\n IonToolbar,\n} from '@ionic/angular/standalone';\nimport {\n AuthProviderID,\n AuthStatuses,\n ILoginEventsHandler,\n ISneatAuthState,\n LoginEventsHandler,\n SneatAuthStateService,\n} from '@sneat/auth-core';\nimport { SneatUserService } from '@sneat/auth-core';\nimport {\n AnalyticsService,\n APP_INFO,\n IAnalyticsService,\n IAppInfo,\n} from '@sneat/core';\nimport { RandomIdService } from '@sneat/random';\nimport { ClassName, SneatBaseComponent } from '@sneat/ui';\nimport { Subject, takeUntil } from 'rxjs';\nimport {\n EmailFormSigningWith,\n EmailLoginFormComponent,\n} from './email-login-form/email-login-form.component';\nimport { UserCredential } from 'firebase/auth';\nimport { LoginWithTelegramComponent } from './login-with-telegram.component';\n\ntype Action = 'join' | 'refuse'; // TODO: inject provider for action descriptions/messages.\n\n@Component({\n selector: 'sneat-login',\n templateUrl: './login-page.component.html',\n imports: [\n FormsModule,\n LoginWithTelegramComponent,\n EmailLoginFormComponent,\n IonHeader,\n IonToolbar,\n IonButtons,\n IonBackButton,\n IonTitle,\n IonContent,\n IonCardContent,\n IonText,\n IonCard,\n IonItemDivider,\n IonLabel,\n IonRow,\n IonCol,\n IonItem,\n IonSpinner,\n IonIcon,\n IonList,\n IonGrid,\n ],\n providers: [\n {\n provide: ClassName,\n useValue: 'LoginPageComponent',\n },\n RandomIdService,\n ],\n})\nexport class LoginPageComponent extends SneatBaseComponent {\n private readonly analyticsService =\n inject<IAnalyticsService>(AnalyticsService);\n private readonly route = inject(ActivatedRoute);\n private readonly navController = inject(NavController);\n private readonly userService = inject(SneatUserService);\n private readonly authStateService = inject(SneatAuthStateService);\n private appInfo = inject<IAppInfo>(APP_INFO);\n private readonly loginEventsHandler = inject<ILoginEventsHandler>(\n LoginEventsHandler,\n { optional: true },\n );\n\n protected readonly signingWith = signal<AuthProviderID | undefined>(\n undefined,\n );\n private readonly redirectTo?: string;\n protected readonly to?: string;\n protected readonly action?: Action; // TODO: document possible values?\n\n protected readonly isNativePlatform = Capacitor.isNativePlatform();\n\n protected readonly appTitle: string;\n\n constructor() {\n super();\n const appInfo = this.appInfo;\n this.appTitle = appInfo.appTitle || 'Sneat.app';\n if (location.hash.startsWith('#/')) {\n this.redirectTo = location.hash.substring(1);\n }\n this.to = this.route.snapshot.queryParams['to']; // should we subscribe? I believe no.\n const action = location.hash.match(/[#&]action=(\\w+)/);\n this.action = action?.[1] as Action;\n\n const userRecordLoaded = new Subject<void>();\n this.userService.userState\n .pipe(takeUntil(userRecordLoaded), this.takeUntilDestroyed())\n .subscribe({\n next: (userState) => {\n if (userState.record) {\n userRecordLoaded.next();\n } else {\n return;\n }\n const redirectTo = this.redirectTo || '/'; // TODO: default one should be app specific.\n this.navController\n .navigateRoot(redirectTo)\n .catch(\n this.errorLogger.logErrorHandler(\n 'Failed to navigate back to ' + redirectTo,\n ),\n );\n },\n error: this.errorHandler('Failed to get user state after login'),\n });\n }\n\n protected onEmailFormStatusChanged(signingWith?: EmailFormSigningWith): void {\n this.signingWith.set(signingWith as AuthProviderID);\n }\n\n protected async loginWith(provider: AuthProviderID) {\n this.signingWith.set(provider);\n try {\n await this.authStateService.signInWith(provider);\n // We do not reset this.signingWith in case of succesful sign in as we should redirect from login page\n // and not to allow user to do a double sign-in.\n } catch (e) {\n const errMsg = (e as { errorMessage?: string }).errorMessage;\n if (\n errMsg !== 'The user canceled the sign-in flow.' &&\n !errMsg?.includes(\n 'com.apple.AuthenticationServices.AuthorizationError error 1001.',\n )\n ) {\n this.errorLogger.logError(e, `Failed to sign-in with ${provider}`);\n }\n this.signingWith.set(undefined);\n }\n }\n\n protected onLoggedIn(userCredential: UserCredential): void {\n this.signingWith.set(undefined);\n if (!userCredential.user) {\n return;\n }\n if (userCredential.user.email) {\n const prevEmail = localStorage.getItem('emailForSignIn') || '';\n if (!prevEmail) {\n localStorage.setItem('emailForSignIn', userCredential.user.email);\n }\n }\n const authState: ISneatAuthState = {\n status: AuthStatuses.authenticated,\n user: userCredential.user,\n };\n this.userService.onUserSignedIn(authState);\n }\n\n private errorHandler(\n m: string,\n eventName?: string,\n eventParams?: Record<string, string>,\n ): (err: unknown) => void {\n return (err) => this.handleError(err, m, eventName, eventParams);\n }\n\n private handleError(\n err: unknown,\n m: string,\n eventName?: string,\n eventParams?: Record<string, string>,\n ): void {\n if (eventName) {\n this.analyticsService.logEvent(eventName, eventParams);\n }\n this.errorLogger.logError(err, m, {\n report: !(err as { code: unknown }).code,\n });\n this.signingWith.set(undefined);\n }\n}\n","<ion-header>\n <ion-toolbar color=\"light\">\n <ion-buttons slot=\"start\">\n <ion-back-button />\n </ion-buttons>\n <ion-title>Login &#64; {{ appTitle }}</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content id=\"main-content\">\n @if (to) {\n <ion-card color=\"tertiary\">\n <ion-card-content>\n <p>Please sign in to join a team</p>\n </ion-card-content>\n </ion-card>\n }\n\n <p style=\"text-align: center; font-size: smaller\">\n <ion-text color=\"medium\">\n This app is free to use &\n <a target=\"_blank\" href=\"https://github.com/sneat-co\">open source</a>.\n </ion-text>\n </p>\n\n <sneat-email-login-form\n (signingWithChange)=\"onEmailFormStatusChanged()\"\n (loggedIn)=\"onLoggedIn($event)\"\n />\n\n <ion-card>\n <ion-item-divider color=\"light\">\n <ion-label\n color=\"medium\"\n style=\"text-align: center; width: 100%; font-weight: bold\"\n >\n Quick login\n </ion-label>\n </ion-item-divider>\n <ion-grid class=\"ion-grid-layout\">\n @if (!isNativePlatform) {\n <ion-row>\n <ion-col>\n <div class=\"ion-padding\" style=\"text-align: center\">\n <sneat-login-with-telegram />\n </div>\n </ion-col>\n </ion-row>\n }\n <ion-row>\n <ion-col size=\"12\" size-md=\"6\">\n <ion-list lines=\"none\">\n <ion-item\n (click)=\"loginWith('google.com')\"\n tappable\n [disabled]=\"!!signingWith()\"\n >\n @if (signingWith() === \"google.com\") {\n <ion-spinner slot=\"start\" name=\"lines-small\" />\n } @else {\n <ion-icon color=\"danger\" name=\"logo-google\" slot=\"start\" />\n }\n <ion-label color=\"danger\"> Login with Google</ion-label>\n </ion-item>\n </ion-list>\n </ion-col>\n <ion-col size=\"12\" size-md=\"6\">\n <ion-list lines=\"none\">\n <ion-item\n (click)=\"loginWith('apple.com')\"\n tappable\n [disabled]=\"!!signingWith()\"\n >\n @if (signingWith() === \"apple.com\") {\n <ion-spinner slot=\"start\" name=\"lines-small\" />\n } @else {\n <ion-icon color=\"dark\" name=\"logo-apple\" slot=\"start\" />\n }\n\n <ion-label color=\"dark\"> Login with Apple</ion-label>\n </ion-item>\n </ion-list>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col size=\"12\" size-md=\"6\">\n <ion-list lines=\"none\">\n <ion-item\n (click)=\"loginWith('microsoft.com')\"\n tappable\n [disabled]=\"!!signingWith()\"\n >\n @if (signingWith() === \"microsoft.com\") {\n <ion-spinner\n slot=\"start\"\n color=\"secondary\"\n name=\"lines-small\"\n />\n } @else {\n <ion-icon slot=\"start\" color=\"secondary\" name=\"logo-windows\" />\n }\n <ion-label color=\"secondary\">Login with Microsoft</ion-label>\n </ion-item>\n </ion-list>\n </ion-col>\n <ion-col size=\"12\" size-md=\"6\">\n <ion-list lines=\"none\">\n <ion-item\n (click)=\"loginWith('facebook.com')\"\n tappable\n [disabled]=\"!!signingWith()\"\n >\n @if (signingWith() === \"facebook.com\") {\n <ion-spinner slot=\"start\" color=\"primary\" name=\"lines-small\" />\n } @else {\n <ion-icon slot=\"start\" color=\"primary\" name=\"logo-facebook\" />\n }\n <ion-label color=\"primary\">Login with Facebook</ion-label>\n </ion-item>\n </ion-list>\n </ion-col>\n </ion-row>\n </ion-grid>\n <!--\t\t\t\t\t\t<ion-item-->\n <!--\t\t\t\t\t\t\t(click)=\"loginWith('github.com')\"-->\n <!--\t\t\t\t\t\t\ttappable-->\n <!--\t\t\t\t\t\t\t[disabled]=\"!!signingWith()\"-->\n <!--\t\t\t\t\t\t>-->\n <!--\t\t\t\t\t\t\t@if (signingWith() === \"github.com\") {-->\n <!--\t\t\t\t\t\t\t\t<ion-spinner slot=\"start\" color=\"tertiary\" name=\"lines-small\" />-->\n <!--\t\t\t\t\t\t\t} @else {-->\n <!--\t\t\t\t\t\t\t\t<ion-icon slot=\"start\" color=\"tertiary\" name=\"logo-github\" />-->\n <!--\t\t\t\t\t\t\t}-->\n <!--\t\t\t\t\t\t\t<ion-label color=\"tertiary\">Login with GitHub</ion-label>-->\n <!--\t\t\t\t\t\t</ion-item>-->\n </ion-card>\n\n <p style=\"text-align: center; font-size: smaller\">\n <ion-text color=\"medium\">\n If any issues get\n <a href=\"mailto:help@sneat.app?subject=Problem+with+login+at+Sneat.app\"\n >help&#64;sneat.app</a\n >\n </ion-text>\n </p>\n</ion-content>\n"]}