@sneat/auth-ui 0.5.4 → 0.6.0
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.
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { Component, signal, inject } from '@angular/core';
|
|
1
|
+
import { Component, computed, signal, inject } from '@angular/core';
|
|
2
|
+
import { toSignal } from '@angular/core/rxjs-interop';
|
|
2
3
|
import { FormsModule } from '@angular/forms';
|
|
3
4
|
import { ActivatedRoute } from '@angular/router';
|
|
4
5
|
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 { NavController, IonBackButton, IonButton, IonButtons, IonCard, IonCardContent, IonCol, IonContent, IonGrid, IonHeader, IonIcon, IonItem, IonItemDivider, IonLabel, IonList, IonRow, IonSpinner, IonText, IonTitle, IonToolbar, } from '@ionic/angular/standalone';
|
|
6
7
|
import { AuthStatuses, LoginEventsHandler, SneatAuthStateService, } from '@sneat/auth-core';
|
|
7
8
|
import { SneatUserService } from '@sneat/auth-core';
|
|
8
9
|
import { AnalyticsService, APP_INFO, currentSpacePath, } from '@sneat/core';
|
|
@@ -24,6 +25,18 @@ export class LoginPageComponent extends SneatBaseComponent {
|
|
|
24
25
|
this.loginEventsHandler = inject(LoginEventsHandler, { optional: true });
|
|
25
26
|
this.signingWith = signal(undefined, ...(ngDevMode ? [{ debugName: "signingWith" }] : []));
|
|
26
27
|
this.isNativePlatform = Capacitor.isNativePlatform();
|
|
28
|
+
// Surfaces the Firebase auth state on the login page itself: if the user is
|
|
29
|
+
// already signed in (e.g. the app failed to navigate them onward) we show a
|
|
30
|
+
// "you are already signed in as X" panel instead of the sign-in form, so an
|
|
31
|
+
// authenticated-but-stuck state is obvious rather than looking like sign-in
|
|
32
|
+
// is broken.
|
|
33
|
+
this.isAuthenticated = computed(() => this.authStatus() === 'authenticated', ...(ngDevMode ? [{ debugName: "isAuthenticated" }] : []));
|
|
34
|
+
this.authStatus = toSignal(this.authStateService.authStatus);
|
|
35
|
+
this.authUser = toSignal(this.authStateService.authUser);
|
|
36
|
+
this.signedInAs = computed(() => {
|
|
37
|
+
const u = this.authUser();
|
|
38
|
+
return u?.displayName || u?.email || u?.uid || '';
|
|
39
|
+
}, ...(ngDevMode ? [{ debugName: "signedInAs" }] : []));
|
|
27
40
|
const appInfo = this.appInfo;
|
|
28
41
|
this.appTitle = appInfo.appTitle || 'Sneat.app';
|
|
29
42
|
if (location.hash.startsWith('#/')) {
|
|
@@ -55,6 +68,19 @@ export class LoginPageComponent extends SneatBaseComponent {
|
|
|
55
68
|
onEmailFormStatusChanged(signingWith) {
|
|
56
69
|
this.signingWith.set(signingWith);
|
|
57
70
|
}
|
|
71
|
+
// Proceed into the app from the "already signed in" panel.
|
|
72
|
+
continueToApp() {
|
|
73
|
+
const redirectTo = this.redirectTo || currentSpacePath() || '/';
|
|
74
|
+
this.navController
|
|
75
|
+
.navigateRoot(redirectTo)
|
|
76
|
+
.catch(this.errorLogger.logErrorHandler('Failed to navigate to ' + redirectTo));
|
|
77
|
+
}
|
|
78
|
+
// Sign out so the sign-in form is shown again (e.g. to log in as someone else).
|
|
79
|
+
reLogin() {
|
|
80
|
+
this.authStateService
|
|
81
|
+
.signOut()
|
|
82
|
+
.catch(this.errorLogger.logErrorHandler('Failed to sign out for re-login'));
|
|
83
|
+
}
|
|
58
84
|
async loginWith(provider) {
|
|
59
85
|
this.signingWith.set(provider);
|
|
60
86
|
try {
|
|
@@ -107,7 +133,7 @@ export class LoginPageComponent extends SneatBaseComponent {
|
|
|
107
133
|
useValue: 'LoginPageComponent',
|
|
108
134
|
},
|
|
109
135
|
RandomIdService,
|
|
110
|
-
], 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 @ {{ 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
|
|
136
|
+
], 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 @ {{ 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 @if (isAuthenticated()) {\n <ion-card color=\"success\">\n <ion-card-content>\n <p>\n @if (signedInAs()) {\n You are already signed in as <strong>{{ signedInAs() }}</strong>.\n } @else {\n You are already signed in.\n }\n </p>\n <ion-button expand=\"block\" (click)=\"continueToApp()\">Continue</ion-button>\n <ion-button\n expand=\"block\"\n fill=\"outline\"\n color=\"medium\"\n (click)=\"reLogin()\"\n >\n Re-login as someone else\n </ion-button>\n </ion-card-content>\n </ion-card>\n } @else {\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-row>\n <ion-col size=\"12\" size-md=\"6\">\n <ion-list lines=\"none\">\n <ion-item\n (click)=\"loginWith('github.com')\"\n tappable\n [disabled]=\"!!signingWith()\"\n >\n @if (signingWith() === \"github.com\") {\n <ion-spinner slot=\"start\" color=\"tertiary\" name=\"lines-small\" />\n } @else {\n <ion-icon slot=\"start\" color=\"tertiary\" name=\"logo-github\" />\n }\n <ion-label color=\"tertiary\">Login with GitHub</ion-label>\n </ion-item>\n </ion-list>\n </ion-col>\n </ion-row>\n </ion-grid>\n </ion-card>\n }\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@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: 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"] }, { 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"] }] }); }
|
|
111
137
|
}
|
|
112
138
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: LoginPageComponent, decorators: [{
|
|
113
139
|
type: Component,
|
|
@@ -117,6 +143,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
|
|
|
117
143
|
EmailLoginFormComponent,
|
|
118
144
|
IonHeader,
|
|
119
145
|
IonToolbar,
|
|
146
|
+
IonButton,
|
|
120
147
|
IonButtons,
|
|
121
148
|
IonBackButton,
|
|
122
149
|
IonTitle,
|
|
@@ -139,6 +166,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
|
|
|
139
166
|
useValue: 'LoginPageComponent',
|
|
140
167
|
},
|
|
141
168
|
RandomIdService,
|
|
142
|
-
], template: "<ion-header>\n <ion-toolbar color=\"light\">\n <ion-buttons slot=\"start\">\n <ion-back-button />\n </ion-buttons>\n <ion-title>Login @ {{ 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
|
|
169
|
+
], template: "<ion-header>\n <ion-toolbar color=\"light\">\n <ion-buttons slot=\"start\">\n <ion-back-button />\n </ion-buttons>\n <ion-title>Login @ {{ 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 @if (isAuthenticated()) {\n <ion-card color=\"success\">\n <ion-card-content>\n <p>\n @if (signedInAs()) {\n You are already signed in as <strong>{{ signedInAs() }}</strong>.\n } @else {\n You are already signed in.\n }\n </p>\n <ion-button expand=\"block\" (click)=\"continueToApp()\">Continue</ion-button>\n <ion-button\n expand=\"block\"\n fill=\"outline\"\n color=\"medium\"\n (click)=\"reLogin()\"\n >\n Re-login as someone else\n </ion-button>\n </ion-card-content>\n </ion-card>\n } @else {\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-row>\n <ion-col size=\"12\" size-md=\"6\">\n <ion-list lines=\"none\">\n <ion-item\n (click)=\"loginWith('github.com')\"\n tappable\n [disabled]=\"!!signingWith()\"\n >\n @if (signingWith() === \"github.com\") {\n <ion-spinner slot=\"start\" color=\"tertiary\" name=\"lines-small\" />\n } @else {\n <ion-icon slot=\"start\" color=\"tertiary\" name=\"logo-github\" />\n }\n <ion-label color=\"tertiary\">Login with GitHub</ion-label>\n </ion-item>\n </ion-list>\n </ion-col>\n </ion-row>\n </ion-grid>\n </ion-card>\n }\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@sneat.app</a\n >\n </ion-text>\n </p>\n</ion-content>\n" }]
|
|
143
170
|
}], ctorParameters: () => [] });
|
|
144
171
|
//# sourceMappingURL=login-page.component.js.map
|
|
@@ -1 +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 currentSpacePath,\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 // Fall back to the persisted current space so it is restored after login.\n const redirectTo = this.redirectTo || currentSpacePath() || '/';\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 @ {{ 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-row>\n <ion-col size=\"12\" size-md=\"6\">\n <ion-list lines=\"none\">\n <ion-item\n (click)=\"loginWith('github.com')\"\n tappable\n [disabled]=\"!!signingWith()\"\n >\n @if (signingWith() === \"github.com\") {\n <ion-spinner slot=\"start\" color=\"tertiary\" name=\"lines-small\" />\n } @else {\n <ion-icon slot=\"start\" color=\"tertiary\" name=\"logo-github\" />\n }\n <ion-label color=\"tertiary\">Login with GitHub</ion-label>\n </ion-item>\n </ion-list>\n </ion-col>\n </ion-row>\n </ion-grid>\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@sneat.app</a\n >\n </ion-text>\n </p>\n</ion-content>\n"]}
|
|
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, computed, signal, inject } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { FormsModule } from '@angular/forms';\nimport { ActivatedRoute } from '@angular/router';\nimport { Capacitor } from '@capacitor/core';\nimport {\n NavController,\n IonBackButton,\n IonButton,\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 currentSpacePath,\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 IonButton,\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 // Surfaces the Firebase auth state on the login page itself: if the user is\n // already signed in (e.g. the app failed to navigate them onward) we show a\n // \"you are already signed in as X\" panel instead of the sign-in form, so an\n // authenticated-but-stuck state is obvious rather than looking like sign-in\n // is broken.\n protected readonly isAuthenticated = computed(\n () => this.authStatus() === 'authenticated',\n );\n private readonly authStatus = toSignal(this.authStateService.authStatus);\n private readonly authUser = toSignal(this.authStateService.authUser);\n protected readonly signedInAs = computed(() => {\n const u = this.authUser();\n return u?.displayName || u?.email || u?.uid || '';\n });\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 // Fall back to the persisted current space so it is restored after login.\n const redirectTo = this.redirectTo || currentSpacePath() || '/';\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 // Proceed into the app from the \"already signed in\" panel.\n protected continueToApp(): void {\n const redirectTo = this.redirectTo || currentSpacePath() || '/';\n this.navController\n .navigateRoot(redirectTo)\n .catch(this.errorLogger.logErrorHandler('Failed to navigate to ' + redirectTo));\n }\n\n // Sign out so the sign-in form is shown again (e.g. to log in as someone else).\n protected reLogin(): void {\n this.authStateService\n .signOut()\n .catch(this.errorLogger.logErrorHandler('Failed to sign out for re-login'));\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 @ {{ 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 @if (isAuthenticated()) {\n <ion-card color=\"success\">\n <ion-card-content>\n <p>\n @if (signedInAs()) {\n You are already signed in as <strong>{{ signedInAs() }}</strong>.\n } @else {\n You are already signed in.\n }\n </p>\n <ion-button expand=\"block\" (click)=\"continueToApp()\">Continue</ion-button>\n <ion-button\n expand=\"block\"\n fill=\"outline\"\n color=\"medium\"\n (click)=\"reLogin()\"\n >\n Re-login as someone else\n </ion-button>\n </ion-card-content>\n </ion-card>\n } @else {\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-row>\n <ion-col size=\"12\" size-md=\"6\">\n <ion-list lines=\"none\">\n <ion-item\n (click)=\"loginWith('github.com')\"\n tappable\n [disabled]=\"!!signingWith()\"\n >\n @if (signingWith() === \"github.com\") {\n <ion-spinner slot=\"start\" color=\"tertiary\" name=\"lines-small\" />\n } @else {\n <ion-icon slot=\"start\" color=\"tertiary\" name=\"logo-github\" />\n }\n <ion-label color=\"tertiary\">Login with GitHub</ion-label>\n </ion-item>\n </ion-list>\n </ion-col>\n </ion-row>\n </ion-grid>\n </ion-card>\n }\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@sneat.app</a\n >\n </ion-text>\n </p>\n</ion-content>\n"]}
|
|
@@ -18,8 +18,14 @@ export declare class LoginPageComponent extends SneatBaseComponent {
|
|
|
18
18
|
protected readonly action?: Action;
|
|
19
19
|
protected readonly isNativePlatform: boolean;
|
|
20
20
|
protected readonly appTitle: string;
|
|
21
|
+
protected readonly isAuthenticated: import("@angular/core").Signal<boolean>;
|
|
22
|
+
private readonly authStatus;
|
|
23
|
+
private readonly authUser;
|
|
24
|
+
protected readonly signedInAs: import("@angular/core").Signal<string>;
|
|
21
25
|
constructor();
|
|
22
26
|
protected onEmailFormStatusChanged(signingWith?: EmailFormSigningWith): void;
|
|
27
|
+
protected continueToApp(): void;
|
|
28
|
+
protected reLogin(): void;
|
|
23
29
|
protected loginWith(provider: AuthProviderID): Promise<void>;
|
|
24
30
|
protected onLoggedIn(userCredential: UserCredential): void;
|
|
25
31
|
private errorHandler;
|