@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,4 @@
1
+ export * from './lib/pages/sneat-auth-routing.module';
2
+ export * from './lib/components';
3
+ export * from './lib/pipes/person-names.pipe';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../libs/auth/ui/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,uCAAuC,CAAC;AACtD,cAAc,kBAAkB,CAAC;AACjC,cAAc,+BAA+B,CAAC","sourcesContent":["export * from './lib/pages/sneat-auth-routing.module';\nexport * from './lib/components';\nexport * from './lib/pipes/person-names.pipe';\n"]}
@@ -0,0 +1,82 @@
1
+ import { JsonPipe } from '@angular/common';
2
+ import { ChangeDetectionStrategy, Component, computed, signal, inject, } from '@angular/core';
3
+ import { RouterModule } from '@angular/router';
4
+ import { IonButton, IonButtons, IonIcon, IonItem, IonItemDivider, IonLabel, MenuController, NavController, } from '@ionic/angular/standalone';
5
+ import { SneatAuthStateService, SneatUserService, } from '@sneat/auth-core';
6
+ import { ErrorLogger } from '@sneat/core';
7
+ import { Subject, takeUntil } from 'rxjs';
8
+ import { PersonNamesPipe, personNames } from '../../pipes/person-names.pipe';
9
+ import * as i0 from "@angular/core";
10
+ import * as i1 from "@angular/router";
11
+ export class AuthMenuItemComponent {
12
+ ngOnDestroy() {
13
+ this.$destroyed.next();
14
+ this.$destroyed.complete();
15
+ }
16
+ constructor() {
17
+ this.errorLogger = inject(ErrorLogger);
18
+ this.navCtrl = inject(NavController);
19
+ this.authStateService = inject(SneatAuthStateService);
20
+ this.menuController = inject(MenuController);
21
+ this.$user = signal(undefined, ...(ngDevMode ? [{ debugName: "$user" }] : []));
22
+ this.$err = signal(undefined, ...(ngDevMode ? [{ debugName: "$err" }] : []));
23
+ this.$authState = signal(undefined, ...(ngDevMode ? [{ debugName: "$authState" }] : []));
24
+ this.$authStatus = computed(() => this.$authState()?.status, ...(ngDevMode ? [{ debugName: "$authStatus" }] : []));
25
+ this.$isAuthenticating = computed(() => this.$authStatus() === 'authenticating', ...(ngDevMode ? [{ debugName: "$isAuthenticating" }] : []));
26
+ this.$destroyed = new Subject();
27
+ this.personName = personNames;
28
+ const errorLogger = this.errorLogger;
29
+ const authStateService = this.authStateService;
30
+ const userService = inject(SneatUserService);
31
+ userService.userState
32
+ .pipe(takeUntil(this.$destroyed))
33
+ .subscribe(this.$user.set);
34
+ authStateService.authState.pipe(takeUntil(this.$destroyed)).subscribe({
35
+ next: this.$authState.set,
36
+ error: (err) => {
37
+ this.$err.set(err);
38
+ errorLogger.logError('failed to get auth state');
39
+ },
40
+ });
41
+ }
42
+ closeMenu() {
43
+ this.menuController
44
+ .close()
45
+ .catch(this.errorLogger.logErrorHandler('Failed to close menu'));
46
+ }
47
+ logout(event) {
48
+ event.stopPropagation();
49
+ event.preventDefault();
50
+ try {
51
+ this.authStateService
52
+ .signOut()
53
+ .then(() => {
54
+ this.navCtrl
55
+ .navigateBack('/signed-out')
56
+ .catch(this.errorLogger.logErrorHandler('Failed to navigate to signed out page'));
57
+ })
58
+ .catch(this.errorLogger.logErrorHandler('Failed to sign out'));
59
+ }
60
+ catch (e) {
61
+ this.errorLogger.logError(e, 'Failed to logout');
62
+ }
63
+ return false;
64
+ }
65
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: AuthMenuItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
66
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: AuthMenuItemComponent, isStandalone: true, selector: "sneat-auth-menu-item", ngImport: i0, template: "<ion-item-divider color=\"light\">\n @if ($authStatus() === \"authenticated\") {\n <ion-label>Signed in as</ion-label>\n } @else {\n <ion-label>Authentication</ion-label>\n }\n</ion-item-divider>\n@if (!$authState()) {\n <ion-item>\n <ion-label>Loading....</ion-label>\n </ion-item>\n} @else if ($isAuthenticating()) {\n <ion-item routerLink=\"login\">\n <ion-label>Authenticating....</ion-label>\n </ion-item>\n}\n@if ($authStatus() === \"notAuthenticated\") {\n <ion-item routerLink=\"/login\" tappable=\"true\">\n <ion-icon slot=\"start\" name=\"person-circle-outline\" />\n <ion-label color=\"primary\"> Please sign in</ion-label>\n </ion-item>\n}\n@if ($authStatus() === \"authenticated\") {\n <ion-item\n tappable=\"true\"\n routerLink=\"/my\"\n routerDirection=\"forward\"\n (click)=\"closeMenu()\"\n >\n <!-- <ion-buttons slot=\"start\" class=\"ion-no-margin\">-->\n <!-- <ion-button disabled color=\"medium\" >-->\n <!-- <ion-icon slot=\"start\" name=\"person-circle-outline\"></ion-icon>-->\n <!-- </ion-button>-->\n <!-- </ion-buttons>-->\n <ion-icon slot=\"start\" name=\"person-circle-outline\" />\n\n <ion-label color=\"medium\">\n @if ($user()?.record?.names; as names) {\n {{ names | personNames }}\n } @else {\n @if ($authState()?.user; as user) {\n {{ user.displayName || user.email || user.phoneNumber || user.uid }}\n }\n }\n </ion-label>\n\n <ion-buttons slot=\"end\">\n <!--\t\t<ion-button color=\"medium\" title=\"Settings\">-->\n <!--\t\t\t<ion-icon name=\"settings-outline\"></ion-icon>-->\n <!--\t\t</ion-button>-->\n <ion-button color=\"medium\" title=\"Sign-out\" (click)=\"logout($event)\">\n <ion-icon name=\"log-out-outline\" />\n </ion-button>\n </ion-buttons>\n </ion-item>\n}\n@if ($err()) {\n <ion-item> ERROR: {{ $err() | json }}</ion-item>\n}\n", dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { 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: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { 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: "pipe", type: PersonNamesPipe, name: "personNames" }, { kind: "pipe", type: JsonPipe, name: "json" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
67
+ }
68
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: AuthMenuItemComponent, decorators: [{
69
+ type: Component,
70
+ args: [{ selector: 'sneat-auth-menu-item', imports: [
71
+ RouterModule,
72
+ PersonNamesPipe,
73
+ IonItemDivider,
74
+ IonLabel,
75
+ IonItem,
76
+ IonIcon,
77
+ IonButtons,
78
+ IonButton,
79
+ JsonPipe,
80
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ion-item-divider color=\"light\">\n @if ($authStatus() === \"authenticated\") {\n <ion-label>Signed in as</ion-label>\n } @else {\n <ion-label>Authentication</ion-label>\n }\n</ion-item-divider>\n@if (!$authState()) {\n <ion-item>\n <ion-label>Loading....</ion-label>\n </ion-item>\n} @else if ($isAuthenticating()) {\n <ion-item routerLink=\"login\">\n <ion-label>Authenticating....</ion-label>\n </ion-item>\n}\n@if ($authStatus() === \"notAuthenticated\") {\n <ion-item routerLink=\"/login\" tappable=\"true\">\n <ion-icon slot=\"start\" name=\"person-circle-outline\" />\n <ion-label color=\"primary\"> Please sign in</ion-label>\n </ion-item>\n}\n@if ($authStatus() === \"authenticated\") {\n <ion-item\n tappable=\"true\"\n routerLink=\"/my\"\n routerDirection=\"forward\"\n (click)=\"closeMenu()\"\n >\n <!-- <ion-buttons slot=\"start\" class=\"ion-no-margin\">-->\n <!-- <ion-button disabled color=\"medium\" >-->\n <!-- <ion-icon slot=\"start\" name=\"person-circle-outline\"></ion-icon>-->\n <!-- </ion-button>-->\n <!-- </ion-buttons>-->\n <ion-icon slot=\"start\" name=\"person-circle-outline\" />\n\n <ion-label color=\"medium\">\n @if ($user()?.record?.names; as names) {\n {{ names | personNames }}\n } @else {\n @if ($authState()?.user; as user) {\n {{ user.displayName || user.email || user.phoneNumber || user.uid }}\n }\n }\n </ion-label>\n\n <ion-buttons slot=\"end\">\n <!--\t\t<ion-button color=\"medium\" title=\"Settings\">-->\n <!--\t\t\t<ion-icon name=\"settings-outline\"></ion-icon>-->\n <!--\t\t</ion-button>-->\n <ion-button color=\"medium\" title=\"Sign-out\" (click)=\"logout($event)\">\n <ion-icon name=\"log-out-outline\" />\n </ion-button>\n </ion-buttons>\n </ion-item>\n}\n@if ($err()) {\n <ion-item> ERROR: {{ $err() | json }}</ion-item>\n}\n" }]
81
+ }], ctorParameters: () => [] });
82
+ //# sourceMappingURL=auth-menu-item.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-menu-item.component.js","sourceRoot":"","sources":["../../../../../../../../libs/auth/ui/src/lib/components/auth-menu-item/auth-menu-item.component.ts","../../../../../../../../libs/auth/ui/src/lib/components/auth-menu-item/auth-menu-item.component.html"],"names":[],"mappings":"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","sourcesContent":["import { JsonPipe } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n computed,\n signal,\n OnDestroy,\n inject,\n} from '@angular/core';\nimport { RouterModule } from '@angular/router';\nimport {\n IonButton,\n IonButtons,\n IonIcon,\n IonItem,\n IonItemDivider,\n IonLabel,\n MenuController,\n NavController,\n} from '@ionic/angular/standalone';\nimport {\n ISneatAuthState,\n ISneatUserState,\n SneatAuthStateService,\n SneatUserService,\n} from '@sneat/auth-core';\nimport { ErrorLogger, IErrorLogger } from '@sneat/core';\nimport { Subject, takeUntil } from 'rxjs';\nimport { PersonNamesPipe, personNames } from '../../pipes/person-names.pipe';\n@Component({\n selector: 'sneat-auth-menu-item',\n templateUrl: './auth-menu-item.component.html',\n imports: [\n RouterModule,\n PersonNamesPipe,\n IonItemDivider,\n IonLabel,\n IonItem,\n IonIcon,\n IonButtons,\n IonButton,\n JsonPipe,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AuthMenuItemComponent implements OnDestroy {\n private readonly errorLogger = inject<IErrorLogger>(ErrorLogger);\n private readonly navCtrl = inject(NavController);\n private readonly authStateService = inject(SneatAuthStateService);\n private readonly menuController = inject(MenuController);\n\n protected readonly $user = signal<ISneatUserState | undefined>(undefined);\n protected $err = signal<unknown>(undefined);\n\n protected readonly $authState = signal<ISneatAuthState | undefined>(\n undefined,\n );\n protected readonly $authStatus = computed(() => this.$authState()?.status);\n protected readonly $isAuthenticating = computed(\n () => this.$authStatus() === 'authenticating',\n );\n\n protected readonly $destroyed = new Subject<void>();\n\n public ngOnDestroy(): void {\n this.$destroyed.next();\n this.$destroyed.complete();\n }\n\n constructor() {\n const errorLogger = this.errorLogger;\n const authStateService = this.authStateService;\n const userService = inject(SneatUserService);\n\n userService.userState\n .pipe(takeUntil(this.$destroyed))\n .subscribe(this.$user.set);\n authStateService.authState.pipe(takeUntil(this.$destroyed)).subscribe({\n next: this.$authState.set,\n error: (err) => {\n this.$err.set(err);\n errorLogger.logError('failed to get auth state');\n },\n });\n }\n\n public closeMenu(): void {\n this.menuController\n .close()\n .catch(this.errorLogger.logErrorHandler('Failed to close menu'));\n }\n\n public logout(event: Event): boolean {\n event.stopPropagation();\n event.preventDefault();\n try {\n this.authStateService\n .signOut()\n .then(() => {\n this.navCtrl\n .navigateBack('/signed-out')\n .catch(\n this.errorLogger.logErrorHandler(\n 'Failed to navigate to signed out page',\n ),\n );\n })\n .catch(this.errorLogger.logErrorHandler('Failed to sign out'));\n } catch (e) {\n this.errorLogger.logError(e, 'Failed to logout');\n }\n return false;\n }\n\n protected readonly personName = personNames;\n}\n","<ion-item-divider color=\"light\">\n @if ($authStatus() === \"authenticated\") {\n <ion-label>Signed in as</ion-label>\n } @else {\n <ion-label>Authentication</ion-label>\n }\n</ion-item-divider>\n@if (!$authState()) {\n <ion-item>\n <ion-label>Loading....</ion-label>\n </ion-item>\n} @else if ($isAuthenticating()) {\n <ion-item routerLink=\"login\">\n <ion-label>Authenticating....</ion-label>\n </ion-item>\n}\n@if ($authStatus() === \"notAuthenticated\") {\n <ion-item routerLink=\"/login\" tappable=\"true\">\n <ion-icon slot=\"start\" name=\"person-circle-outline\" />\n <ion-label color=\"primary\"> Please sign in</ion-label>\n </ion-item>\n}\n@if ($authStatus() === \"authenticated\") {\n <ion-item\n tappable=\"true\"\n routerLink=\"/my\"\n routerDirection=\"forward\"\n (click)=\"closeMenu()\"\n >\n <!-- <ion-buttons slot=\"start\" class=\"ion-no-margin\">-->\n <!-- <ion-button disabled color=\"medium\" >-->\n <!-- <ion-icon slot=\"start\" name=\"person-circle-outline\"></ion-icon>-->\n <!-- </ion-button>-->\n <!-- </ion-buttons>-->\n <ion-icon slot=\"start\" name=\"person-circle-outline\" />\n\n <ion-label color=\"medium\">\n @if ($user()?.record?.names; as names) {\n {{ names | personNames }}\n } @else {\n @if ($authState()?.user; as user) {\n {{ user.displayName || user.email || user.phoneNumber || user.uid }}\n }\n }\n </ion-label>\n\n <ion-buttons slot=\"end\">\n <!--\t\t<ion-button color=\"medium\" title=\"Settings\">-->\n <!--\t\t\t<ion-icon name=\"settings-outline\"></ion-icon>-->\n <!--\t\t</ion-button>-->\n <ion-button color=\"medium\" title=\"Sign-out\" (click)=\"logout($event)\">\n <ion-icon name=\"log-out-outline\" />\n </ion-button>\n </ion-buttons>\n </ion-item>\n}\n@if ($err()) {\n <ion-item> ERROR: {{ $err() | json }}</ion-item>\n}\n"]}
@@ -0,0 +1,4 @@
1
+ export * from './user-required-fields';
2
+ export * from './user-auth-providers/user-auth-accounts.component';
3
+ export * from './auth-menu-item/auth-menu-item.component';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../../libs/auth/ui/src/lib/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,oDAAoD,CAAC;AACnE,cAAc,2CAA2C,CAAC","sourcesContent":["export * from './user-required-fields';\nexport * from './user-auth-providers/user-auth-accounts.component';\nexport * from './auth-menu-item/auth-menu-item.component';\n"]}
@@ -0,0 +1,83 @@
1
+ import { ChangeDetectionStrategy, Component, signal, inject, } from '@angular/core';
2
+ import { FormsModule } from '@angular/forms';
3
+ import { IonButton, IonButtons, IonCard, IonCardContent, IonIcon, IonItem, IonItemDivider, IonLabel, IonList, IonText, } from '@ionic/angular/standalone';
4
+ import { SneatApiService } from '@sneat/api';
5
+ import { SneatUserService } from '@sneat/auth-core';
6
+ import { ClassName, SneatBaseComponent } from '@sneat/ui';
7
+ import { LoginWithTelegramComponent } from '../../pages/login-page/login-with-telegram.component';
8
+ import { UserAuthAProviderStatusComponent } from './user-auth-provider-status';
9
+ import * as i0 from "@angular/core";
10
+ export class UserAuthAccountsComponent extends SneatBaseComponent {
11
+ constructor() {
12
+ super();
13
+ this.sneatUserService = inject(SneatUserService);
14
+ this.sneatApiService = inject(SneatApiService);
15
+ this.$userRecord = signal(undefined, ...(ngDevMode ? [{ debugName: "$userRecord" }] : []));
16
+ this.signingInWith = signal(undefined, ...(ngDevMode ? [{ debugName: "signingInWith" }] : []));
17
+ this.sneatUserService.userState.pipe(this.takeUntilDestroyed()).subscribe({
18
+ next: (user) => {
19
+ this.$userRecord.set(user.record || undefined);
20
+ },
21
+ });
22
+ }
23
+ hasAccount(provider) {
24
+ provider = provider + ':';
25
+ for (const account of this.$userRecord()?.accounts || []) {
26
+ if (account.startsWith(provider)) {
27
+ return true;
28
+ }
29
+ }
30
+ return false;
31
+ }
32
+ getAccountID(provider) {
33
+ const prefix = provider + '::';
34
+ const a = this.$userRecord()?.accounts?.find((a) => a.startsWith(prefix));
35
+ return a?.replace(prefix, '') || '';
36
+ }
37
+ disconnect(provider) {
38
+ if (!confirm('Are you sure you want to disconnect Telegram login from your account?')) {
39
+ return;
40
+ }
41
+ this.disconnecting = provider;
42
+ this.sneatApiService
43
+ .delete('auth/disconnect?provider=' + provider)
44
+ .subscribe({
45
+ next: () => {
46
+ this.disconnecting = undefined;
47
+ alert('Disconnected!');
48
+ },
49
+ error: this.errorLogger.logErrorHandler('Failed to disconnect'),
50
+ });
51
+ }
52
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: UserAuthAccountsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
53
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: UserAuthAccountsComponent, isStandalone: true, selector: "sneat-user-auth-accounts", providers: [
54
+ {
55
+ provide: ClassName,
56
+ useValue: 'UserAuthAccountsComponent',
57
+ },
58
+ ], usesInheritance: true, ngImport: i0, template: "<ion-card>\n <!--\t<ion-item color=\"light\" lines=\"full\">-->\n <!--\t\t<ion-label color=\"medium\"-->\n <!--\t\t\t><h2 style=\"font-weight: bold\">Authentication</h2></ion-label-->\n <!--\t\t>-->\n <!--\t</ion-item>-->\n <!--\t<ion-accordion-group [value]=\"integrations\">-->\n <!--\t\t<ion-accordion value=\"messaging-apps\">-->\n <!--\t\t\t<ion-item slot=\"header\" color=\"light\">-->\n <!--\t\t\t\t<ion-label>First Accordion</ion-label>-->\n <!--\t\t\t</ion-item>-->\n <!--\t\t\t<div class=\"ion-padding\" slot=\"content\">First Content</div>-->\n <!--\t\t</ion-accordion>-->\n <!--\t\t<ion-accordion value=\"quick-logins\">-->\n <!--\t\t\t<ion-item slot=\"header\" color=\"light\">-->\n <!--\t\t\t\t<ion-label>Second Accordion</ion-label>-->\n <!--\t\t\t</ion-item>-->\n <!--\t\t\t<div class=\"ion-padding\" slot=\"content\">Second Content</div>-->\n <!--\t\t</ion-accordion>-->\n <!--\t</ion-accordion-group>-->\n <!--\t<ion-item>-->\n <!--\t\t<ion-segment [(ngModel)]=\"integrations\" style=\"margin-bottom: 0.5em\">-->\n <!--\t\t\t<ion-segment-button value=\"messaging-apps\"-->\n <!--\t\t\t\t>Messaging Apps-->\n <!--\t\t\t</ion-segment-button>-->\n <!--\t\t\t<ion-segment-button value=\"quick-logins\">Quick logins</ion-segment-button>-->\n <!--\t\t</ion-segment>-->\n <!--\t</ion-item>-->\n <ion-list>\n <ion-item-divider color=\"light\">\n <ion-label color=\"medium\" style=\"font-weight: bold\"\n >Quick logins</ion-label\n >\n </ion-item-divider>\n <sneat-user-auth-provider-status\n providerID=\"google.com\"\n [signingInWith]=\"signingInWith()\"\n (signingInWithChange)=\"signingInWith.set($event)\"\n />\n <sneat-user-auth-provider-status\n providerID=\"apple.com\"\n [signingInWith]=\"signingInWith()\"\n (signingInWithChange)=\"signingInWith.set($event)\"\n />\n <sneat-user-auth-provider-status\n providerID=\"microsoft.com\"\n [signingInWith]=\"signingInWith()\"\n (signingInWithChange)=\"signingInWith.set($event)\"\n />\n </ion-list>\n <ion-list>\n <ion-item-divider color=\"light\">\n <ion-label color=\"medium\" style=\"font-weight: bold\"\n >Messaging apps</ion-label\n >\n </ion-item-divider>\n\n <ion-item>\n <ion-icon slot=\"start\" name=\"paper-plane\" color=\"primary\"></ion-icon>\n @if (!$userRecord()) {\n <ion-label>\n <h3>Telegram</h3>\n <p>Checking....</p>\n </ion-label>\n } @else if (hasAccount(\"telegram\")) {\n <ion-label>\n <h3 style=\"font-weight: bold\">Telegram</h3>\n <ion-text color=\"success\" [title]=\"getAccountID('telegram')\"\n >Connected!\n </ion-text>\n </ion-label>\n <ion-buttons slot=\"end\">\n <ion-button\n [disabled]=\"disconnecting\"\n color=\"warning\"\n fill=\"solid\"\n (click)=\"disconnect('telegram')\"\n >\n <ion-icon name=\"close-outline\" slot=\"start\"></ion-icon>\n <ion-label>Disconnect</ion-label>\n </ion-button>\n </ion-buttons>\n } @else {\n <ion-label>\n <h3>Telegram</h3>\n <p>Not connected yet.</p>\n </ion-label>\n <ion-buttons slot=\"end\">\n <sneat-login-with-telegram [isUserAuthenticated]=\"true\" />\n </ion-buttons>\n }\n </ion-item>\n <ion-item>\n <ion-icon slot=\"start\" name=\"logo-whatsapp\"></ion-icon>\n <ion-label color=\"medium\">\n <h3 style=\"font-weight: bold\">WhatsApp</h3>\n <p>Not implemented yet - coming soon!</p>\n </ion-label>\n <!--\t\t<ion-buttons slot=\"end\">-->\n <!--\t\t\t<ion-button color=\"danger\">-->\n <!--\t\t\t\t<ion-icon name=\"close-circle-outline\" slot=\"start\"></ion-icon>-->\n <!--\t\t\t\t<ion-label>I have no WhatsApp</ion-label>-->\n <!--\t\t\t</ion-button>-->\n <!--\t\t</ion-buttons>-->\n </ion-item>\n <ion-item>\n <ion-icon slot=\"start\" name=\"logo-whatsapp\"></ion-icon>\n <!-- TODO: place Viber logo here -->\n <ion-label color=\"medium\">\n <h3 style=\"font-weight: bold\">Viber</h3>\n <p>Not implemented yet - coming soon!</p>\n </ion-label>\n </ion-item>\n </ion-list>\n <ion-card-content>\n <p>\n You'll get much more out of Sneat.app if you integrate it with messaging\n apps you use. Benefits:\n </p>\n <ul>\n <li>Easily add contacts</li>\n <li>Get reminders</li>\n <li>Add todo/to_buy from your messenger</li>\n <li>etc.</li>\n </ul>\n </ion-card-content>\n</ion-card>\n", dependencies: [{ kind: "component", type: LoginWithTelegramComponent, selector: "sneat-login-with-telegram", inputs: ["isUserAuthenticated", "botID", "size", "requestAccess", "userPic"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: UserAuthAProviderStatusComponent, selector: "sneat-user-auth-provider-status", inputs: ["providerID", "signingInWith"], outputs: ["signingInWithChange"] }, { kind: "component", type: IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { 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: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { 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: IonCardContent, selector: "ion-card-content", inputs: ["mode"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
59
+ }
60
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: UserAuthAccountsComponent, decorators: [{
61
+ type: Component,
62
+ args: [{ selector: 'sneat-user-auth-accounts', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
63
+ LoginWithTelegramComponent,
64
+ FormsModule,
65
+ UserAuthAProviderStatusComponent,
66
+ IonCard,
67
+ IonList,
68
+ IonItemDivider,
69
+ IonLabel,
70
+ IonItem,
71
+ IonIcon,
72
+ IonText,
73
+ IonButtons,
74
+ IonButton,
75
+ IonCardContent,
76
+ ], providers: [
77
+ {
78
+ provide: ClassName,
79
+ useValue: 'UserAuthAccountsComponent',
80
+ },
81
+ ], template: "<ion-card>\n <!--\t<ion-item color=\"light\" lines=\"full\">-->\n <!--\t\t<ion-label color=\"medium\"-->\n <!--\t\t\t><h2 style=\"font-weight: bold\">Authentication</h2></ion-label-->\n <!--\t\t>-->\n <!--\t</ion-item>-->\n <!--\t<ion-accordion-group [value]=\"integrations\">-->\n <!--\t\t<ion-accordion value=\"messaging-apps\">-->\n <!--\t\t\t<ion-item slot=\"header\" color=\"light\">-->\n <!--\t\t\t\t<ion-label>First Accordion</ion-label>-->\n <!--\t\t\t</ion-item>-->\n <!--\t\t\t<div class=\"ion-padding\" slot=\"content\">First Content</div>-->\n <!--\t\t</ion-accordion>-->\n <!--\t\t<ion-accordion value=\"quick-logins\">-->\n <!--\t\t\t<ion-item slot=\"header\" color=\"light\">-->\n <!--\t\t\t\t<ion-label>Second Accordion</ion-label>-->\n <!--\t\t\t</ion-item>-->\n <!--\t\t\t<div class=\"ion-padding\" slot=\"content\">Second Content</div>-->\n <!--\t\t</ion-accordion>-->\n <!--\t</ion-accordion-group>-->\n <!--\t<ion-item>-->\n <!--\t\t<ion-segment [(ngModel)]=\"integrations\" style=\"margin-bottom: 0.5em\">-->\n <!--\t\t\t<ion-segment-button value=\"messaging-apps\"-->\n <!--\t\t\t\t>Messaging Apps-->\n <!--\t\t\t</ion-segment-button>-->\n <!--\t\t\t<ion-segment-button value=\"quick-logins\">Quick logins</ion-segment-button>-->\n <!--\t\t</ion-segment>-->\n <!--\t</ion-item>-->\n <ion-list>\n <ion-item-divider color=\"light\">\n <ion-label color=\"medium\" style=\"font-weight: bold\"\n >Quick logins</ion-label\n >\n </ion-item-divider>\n <sneat-user-auth-provider-status\n providerID=\"google.com\"\n [signingInWith]=\"signingInWith()\"\n (signingInWithChange)=\"signingInWith.set($event)\"\n />\n <sneat-user-auth-provider-status\n providerID=\"apple.com\"\n [signingInWith]=\"signingInWith()\"\n (signingInWithChange)=\"signingInWith.set($event)\"\n />\n <sneat-user-auth-provider-status\n providerID=\"microsoft.com\"\n [signingInWith]=\"signingInWith()\"\n (signingInWithChange)=\"signingInWith.set($event)\"\n />\n </ion-list>\n <ion-list>\n <ion-item-divider color=\"light\">\n <ion-label color=\"medium\" style=\"font-weight: bold\"\n >Messaging apps</ion-label\n >\n </ion-item-divider>\n\n <ion-item>\n <ion-icon slot=\"start\" name=\"paper-plane\" color=\"primary\"></ion-icon>\n @if (!$userRecord()) {\n <ion-label>\n <h3>Telegram</h3>\n <p>Checking....</p>\n </ion-label>\n } @else if (hasAccount(\"telegram\")) {\n <ion-label>\n <h3 style=\"font-weight: bold\">Telegram</h3>\n <ion-text color=\"success\" [title]=\"getAccountID('telegram')\"\n >Connected!\n </ion-text>\n </ion-label>\n <ion-buttons slot=\"end\">\n <ion-button\n [disabled]=\"disconnecting\"\n color=\"warning\"\n fill=\"solid\"\n (click)=\"disconnect('telegram')\"\n >\n <ion-icon name=\"close-outline\" slot=\"start\"></ion-icon>\n <ion-label>Disconnect</ion-label>\n </ion-button>\n </ion-buttons>\n } @else {\n <ion-label>\n <h3>Telegram</h3>\n <p>Not connected yet.</p>\n </ion-label>\n <ion-buttons slot=\"end\">\n <sneat-login-with-telegram [isUserAuthenticated]=\"true\" />\n </ion-buttons>\n }\n </ion-item>\n <ion-item>\n <ion-icon slot=\"start\" name=\"logo-whatsapp\"></ion-icon>\n <ion-label color=\"medium\">\n <h3 style=\"font-weight: bold\">WhatsApp</h3>\n <p>Not implemented yet - coming soon!</p>\n </ion-label>\n <!--\t\t<ion-buttons slot=\"end\">-->\n <!--\t\t\t<ion-button color=\"danger\">-->\n <!--\t\t\t\t<ion-icon name=\"close-circle-outline\" slot=\"start\"></ion-icon>-->\n <!--\t\t\t\t<ion-label>I have no WhatsApp</ion-label>-->\n <!--\t\t\t</ion-button>-->\n <!--\t\t</ion-buttons>-->\n </ion-item>\n <ion-item>\n <ion-icon slot=\"start\" name=\"logo-whatsapp\"></ion-icon>\n <!-- TODO: place Viber logo here -->\n <ion-label color=\"medium\">\n <h3 style=\"font-weight: bold\">Viber</h3>\n <p>Not implemented yet - coming soon!</p>\n </ion-label>\n </ion-item>\n </ion-list>\n <ion-card-content>\n <p>\n You'll get much more out of Sneat.app if you integrate it with messaging\n apps you use. Benefits:\n </p>\n <ul>\n <li>Easily add contacts</li>\n <li>Get reminders</li>\n <li>Add todo/to_buy from your messenger</li>\n <li>etc.</li>\n </ul>\n </ion-card-content>\n</ion-card>\n" }]
82
+ }], ctorParameters: () => [] });
83
+ //# sourceMappingURL=user-auth-accounts.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-auth-accounts.component.js","sourceRoot":"","sources":["../../../../../../../../libs/auth/ui/src/lib/components/user-auth-providers/user-auth-accounts.component.ts","../../../../../../../../libs/auth/ui/src/lib/components/user-auth-providers/user-auth-accounts.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n signal,\n inject,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport {\n IonButton,\n IonButtons,\n IonCard,\n IonCardContent,\n IonIcon,\n IonItem,\n IonItemDivider,\n IonLabel,\n IonList,\n IonText,\n} from '@ionic/angular/standalone';\nimport { SneatApiService } from '@sneat/api';\nimport { AuthProviderID, SneatUserService } from '@sneat/auth-core';\nimport { IUserRecord } from '@sneat/auth-models';\nimport { ClassName, SneatBaseComponent } from '@sneat/ui';\nimport { LoginWithTelegramComponent } from '../../pages/login-page/login-with-telegram.component';\nimport { UserAuthAProviderStatusComponent } from './user-auth-provider-status';\n\n@Component({\n selector: 'sneat-user-auth-accounts',\n templateUrl: './user-auth-accounts.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n LoginWithTelegramComponent,\n FormsModule,\n UserAuthAProviderStatusComponent,\n IonCard,\n IonList,\n IonItemDivider,\n IonLabel,\n IonItem,\n IonIcon,\n IonText,\n IonButtons,\n IonButton,\n IonCardContent,\n ],\n providers: [\n {\n provide: ClassName,\n useValue: 'UserAuthAccountsComponent',\n },\n ],\n})\nexport class UserAuthAccountsComponent extends SneatBaseComponent {\n private readonly sneatUserService = inject(SneatUserService);\n private readonly sneatApiService = inject(SneatApiService);\n\n protected readonly $userRecord = signal<IUserRecord | undefined>(undefined);\n\n protected readonly signingInWith = signal<AuthProviderID | undefined>(\n undefined,\n );\n\n constructor() {\n super();\n this.sneatUserService.userState.pipe(this.takeUntilDestroyed()).subscribe({\n next: (user) => {\n this.$userRecord.set(user.record || undefined);\n },\n });\n }\n\n protected hasAccount(provider: 'telegram' | 'viber' | 'whatsapp'): boolean {\n provider = provider + ':';\n for (const account of this.$userRecord()?.accounts || []) {\n if (account.startsWith(provider)) {\n return true;\n }\n }\n return false;\n }\n\n protected getAccountID(provider: 'telegram'): string {\n const prefix = provider + '::';\n const a = this.$userRecord()?.accounts?.find((a) => a.startsWith(prefix));\n return a?.replace(prefix, '') || '';\n }\n\n protected disconnecting?: 'telegram' | 'viber' | 'whatsapp';\n\n protected disconnect(provider: 'telegram'): void {\n if (\n !confirm(\n 'Are you sure you want to disconnect Telegram login from your account?',\n )\n ) {\n return;\n }\n this.disconnecting = provider;\n this.sneatApiService\n .delete('auth/disconnect?provider=' + provider)\n .subscribe({\n next: () => {\n this.disconnecting = undefined;\n alert('Disconnected!');\n },\n error: this.errorLogger.logErrorHandler('Failed to disconnect'),\n });\n }\n}\n","<ion-card>\n <!--\t<ion-item color=\"light\" lines=\"full\">-->\n <!--\t\t<ion-label color=\"medium\"-->\n <!--\t\t\t><h2 style=\"font-weight: bold\">Authentication</h2></ion-label-->\n <!--\t\t>-->\n <!--\t</ion-item>-->\n <!--\t<ion-accordion-group [value]=\"integrations\">-->\n <!--\t\t<ion-accordion value=\"messaging-apps\">-->\n <!--\t\t\t<ion-item slot=\"header\" color=\"light\">-->\n <!--\t\t\t\t<ion-label>First Accordion</ion-label>-->\n <!--\t\t\t</ion-item>-->\n <!--\t\t\t<div class=\"ion-padding\" slot=\"content\">First Content</div>-->\n <!--\t\t</ion-accordion>-->\n <!--\t\t<ion-accordion value=\"quick-logins\">-->\n <!--\t\t\t<ion-item slot=\"header\" color=\"light\">-->\n <!--\t\t\t\t<ion-label>Second Accordion</ion-label>-->\n <!--\t\t\t</ion-item>-->\n <!--\t\t\t<div class=\"ion-padding\" slot=\"content\">Second Content</div>-->\n <!--\t\t</ion-accordion>-->\n <!--\t</ion-accordion-group>-->\n <!--\t<ion-item>-->\n <!--\t\t<ion-segment [(ngModel)]=\"integrations\" style=\"margin-bottom: 0.5em\">-->\n <!--\t\t\t<ion-segment-button value=\"messaging-apps\"-->\n <!--\t\t\t\t>Messaging Apps-->\n <!--\t\t\t</ion-segment-button>-->\n <!--\t\t\t<ion-segment-button value=\"quick-logins\">Quick logins</ion-segment-button>-->\n <!--\t\t</ion-segment>-->\n <!--\t</ion-item>-->\n <ion-list>\n <ion-item-divider color=\"light\">\n <ion-label color=\"medium\" style=\"font-weight: bold\"\n >Quick logins</ion-label\n >\n </ion-item-divider>\n <sneat-user-auth-provider-status\n providerID=\"google.com\"\n [signingInWith]=\"signingInWith()\"\n (signingInWithChange)=\"signingInWith.set($event)\"\n />\n <sneat-user-auth-provider-status\n providerID=\"apple.com\"\n [signingInWith]=\"signingInWith()\"\n (signingInWithChange)=\"signingInWith.set($event)\"\n />\n <sneat-user-auth-provider-status\n providerID=\"microsoft.com\"\n [signingInWith]=\"signingInWith()\"\n (signingInWithChange)=\"signingInWith.set($event)\"\n />\n </ion-list>\n <ion-list>\n <ion-item-divider color=\"light\">\n <ion-label color=\"medium\" style=\"font-weight: bold\"\n >Messaging apps</ion-label\n >\n </ion-item-divider>\n\n <ion-item>\n <ion-icon slot=\"start\" name=\"paper-plane\" color=\"primary\"></ion-icon>\n @if (!$userRecord()) {\n <ion-label>\n <h3>Telegram</h3>\n <p>Checking....</p>\n </ion-label>\n } @else if (hasAccount(\"telegram\")) {\n <ion-label>\n <h3 style=\"font-weight: bold\">Telegram</h3>\n <ion-text color=\"success\" [title]=\"getAccountID('telegram')\"\n >Connected!\n </ion-text>\n </ion-label>\n <ion-buttons slot=\"end\">\n <ion-button\n [disabled]=\"disconnecting\"\n color=\"warning\"\n fill=\"solid\"\n (click)=\"disconnect('telegram')\"\n >\n <ion-icon name=\"close-outline\" slot=\"start\"></ion-icon>\n <ion-label>Disconnect</ion-label>\n </ion-button>\n </ion-buttons>\n } @else {\n <ion-label>\n <h3>Telegram</h3>\n <p>Not connected yet.</p>\n </ion-label>\n <ion-buttons slot=\"end\">\n <sneat-login-with-telegram [isUserAuthenticated]=\"true\" />\n </ion-buttons>\n }\n </ion-item>\n <ion-item>\n <ion-icon slot=\"start\" name=\"logo-whatsapp\"></ion-icon>\n <ion-label color=\"medium\">\n <h3 style=\"font-weight: bold\">WhatsApp</h3>\n <p>Not implemented yet - coming soon!</p>\n </ion-label>\n <!--\t\t<ion-buttons slot=\"end\">-->\n <!--\t\t\t<ion-button color=\"danger\">-->\n <!--\t\t\t\t<ion-icon name=\"close-circle-outline\" slot=\"start\"></ion-icon>-->\n <!--\t\t\t\t<ion-label>I have no WhatsApp</ion-label>-->\n <!--\t\t\t</ion-button>-->\n <!--\t\t</ion-buttons>-->\n </ion-item>\n <ion-item>\n <ion-icon slot=\"start\" name=\"logo-whatsapp\"></ion-icon>\n <!-- TODO: place Viber logo here -->\n <ion-label color=\"medium\">\n <h3 style=\"font-weight: bold\">Viber</h3>\n <p>Not implemented yet - coming soon!</p>\n </ion-label>\n </ion-item>\n </ion-list>\n <ion-card-content>\n <p>\n You'll get much more out of Sneat.app if you integrate it with messaging\n apps you use. Benefits:\n </p>\n <ul>\n <li>Easily add contacts</li>\n <li>Get reminders</li>\n <li>Add todo/to_buy from your messenger</li>\n <li>etc.</li>\n </ul>\n </ion-card-content>\n</ion-card>\n"]}
@@ -0,0 +1,118 @@
1
+ import { Component, EventEmitter, input, Output, signal, computed, inject, } from '@angular/core';
2
+ import { IonButton, IonButtons, IonIcon, IonItem, IonLabel, IonSpinner, } from '@ionic/angular/standalone';
3
+ import { SneatAuthStateService, } from '@sneat/auth-core';
4
+ import { ErrorLogger } from '@sneat/core';
5
+ import { Subject, takeUntil } from 'rxjs';
6
+ import * as i0 from "@angular/core";
7
+ export class UserAuthAProviderStatusComponent {
8
+ constructor() {
9
+ this.errorLogger = inject(ErrorLogger);
10
+ this.authStateService = inject(SneatAuthStateService);
11
+ this.$destroyed = new Subject();
12
+ this.providerID = input.required(...(ngDevMode ? [{ debugName: "providerID" }] : []));
13
+ this.provider = computed(() => {
14
+ const id = this.providerID();
15
+ switch (id) {
16
+ case 'facebook.com':
17
+ return {
18
+ id: id,
19
+ title: 'Facebook',
20
+ icon: 'logo-facebook',
21
+ color: 'primary',
22
+ };
23
+ case 'google.com':
24
+ return {
25
+ id,
26
+ title: 'Google',
27
+ icon: 'logo-google',
28
+ color: 'danger',
29
+ };
30
+ case 'apple.com':
31
+ return {
32
+ id,
33
+ title: 'Apple',
34
+ icon: 'logo-apple',
35
+ color: 'dark',
36
+ };
37
+ case 'microsoft.com':
38
+ return {
39
+ id,
40
+ title: 'Microsoft',
41
+ icon: 'logo-windows',
42
+ color: 'secondary',
43
+ };
44
+ case undefined:
45
+ throw new Error('Undefined provider ID');
46
+ default:
47
+ return {
48
+ id,
49
+ title: id,
50
+ icon: 'help-circle',
51
+ color: 'medium',
52
+ };
53
+ }
54
+ }, ...(ngDevMode ? [{ debugName: "provider" }] : []));
55
+ this.signingInWith = input.required(...(ngDevMode ? [{ debugName: "signingInWith" }] : []));
56
+ this.signingInWithChange = new EventEmitter();
57
+ this.currentUser = signal(undefined, ...(ngDevMode ? [{ debugName: "currentUser" }] : []));
58
+ this.isSigningIn = computed(() => this.signingInWith() === this.providerID(), ...(ngDevMode ? [{ debugName: "isSigningIn" }] : []));
59
+ this.isDisabled = computed(() => {
60
+ const signingInWith = this.signingInWith();
61
+ return !!signingInWith;
62
+ }, ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
63
+ this.authProviderUserInfo = computed(() => {
64
+ const providerID = this.providerID();
65
+ return this.currentUser()?.providerData?.find((pd) => pd.providerId == providerID);
66
+ }, ...(ngDevMode ? [{ debugName: "authProviderUserInfo" }] : []));
67
+ this.isConnected = computed(() => !!this.authProviderUserInfo(), ...(ngDevMode ? [{ debugName: "isConnected" }] : []));
68
+ this.authStateService.authUser
69
+ .pipe(takeUntil(this.$destroyed))
70
+ .subscribe((authUser) => {
71
+ this.currentUser.set(authUser);
72
+ });
73
+ }
74
+ connect() {
75
+ const providerID = this.providerID();
76
+ if (!providerID) {
77
+ throw new Error('auth providerID is not set');
78
+ }
79
+ this.signingInWithChange.emit(providerID);
80
+ this.authStateService
81
+ .linkWith(providerID)
82
+ .then(() => {
83
+ this.signingInWithChange.emit(undefined);
84
+ })
85
+ .catch((e) => {
86
+ console.error('Failed to connect', e);
87
+ this.signingInWithChange.emit(undefined);
88
+ });
89
+ }
90
+ disconnect() {
91
+ const providerID = this.providerID();
92
+ if (!providerID) {
93
+ this.errorLogger.logError('auth providerID is not set');
94
+ return;
95
+ }
96
+ this.authStateService
97
+ .unlinkAuthProvider(providerID)
98
+ .then(() => this.signingInWithChange.emit(undefined))
99
+ .catch((err) => {
100
+ this.signingInWithChange.emit(undefined);
101
+ this.errorLogger.logError(err);
102
+ });
103
+ this.signingInWithChange.emit(providerID);
104
+ }
105
+ ngOnDestroy() {
106
+ this.$destroyed.next();
107
+ this.$destroyed.complete();
108
+ }
109
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: UserAuthAProviderStatusComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
110
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: UserAuthAProviderStatusComponent, isStandalone: true, selector: "sneat-user-auth-provider-status", inputs: { providerID: { classPropertyName: "providerID", publicName: "providerID", isSignal: true, isRequired: true, transformFunction: null }, signingInWith: { classPropertyName: "signingInWith", publicName: "signingInWith", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { signingInWithChange: "signingInWithChange" }, ngImport: i0, template: "<ion-item>\n <ion-icon [color]=\"provider().color\" [name]=\"provider().icon\" slot=\"start\" />\n <ion-label>\n <h3>{{ provider().title || providerID() }}</h3>\n <p>\n @if(isConnected()) { Connected as\n <b>{{authProviderUserInfo()?.displayName}}</b>\n &nbsp;&dash;&nbsp;\n <a href=\"mailto:{{authProviderUserInfo()?.email}}\"\n >{{authProviderUserInfo()?.email}}</a\n >. } @else { Not connected yet. }\n </p>\n </ion-label>\n <ion-buttons slot=\"end\">\n @if (isConnected()) {\n <ion-button color=\"medium\" fill=\"outline\" (click)=\"disconnect()\">\n <ion-icon name=\"close-circle-outline\" slot=\"start\"></ion-icon>\n <ion-label>\n @if (isSigningIn()) { Disconnecting... } @else { Disconnect }\n </ion-label>\n </ion-button>\n } @else {\n <ion-button\n [disabled]=\"isDisabled()\"\n [color]=\"provider().color\"\n [fill]=\"'solid'\"\n (click)=\"connect()\"\n >\n @if (isSigningIn()) {\n <ion-spinner slot=\"start\" name=\"lines-small\" />\n } @else {\n <ion-icon slot=\"start\" [name]=\"provider().icon\" />\n }\n\n <ion-label>\n @if (signingInWith() === providerID()) { Signing in... } @else { Connect\n }\n </ion-label>\n </ion-button>\n }\n </ion-buttons>\n</ion-item>\n", dependencies: [{ 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: 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: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { 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: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }] }); }
111
+ }
112
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: UserAuthAProviderStatusComponent, decorators: [{
113
+ type: Component,
114
+ args: [{ selector: 'sneat-user-auth-provider-status', imports: [IonItem, IonIcon, IonLabel, IonButtons, IonButton, IonSpinner], template: "<ion-item>\n <ion-icon [color]=\"provider().color\" [name]=\"provider().icon\" slot=\"start\" />\n <ion-label>\n <h3>{{ provider().title || providerID() }}</h3>\n <p>\n @if(isConnected()) { Connected as\n <b>{{authProviderUserInfo()?.displayName}}</b>\n &nbsp;&dash;&nbsp;\n <a href=\"mailto:{{authProviderUserInfo()?.email}}\"\n >{{authProviderUserInfo()?.email}}</a\n >. } @else { Not connected yet. }\n </p>\n </ion-label>\n <ion-buttons slot=\"end\">\n @if (isConnected()) {\n <ion-button color=\"medium\" fill=\"outline\" (click)=\"disconnect()\">\n <ion-icon name=\"close-circle-outline\" slot=\"start\"></ion-icon>\n <ion-label>\n @if (isSigningIn()) { Disconnecting... } @else { Disconnect }\n </ion-label>\n </ion-button>\n } @else {\n <ion-button\n [disabled]=\"isDisabled()\"\n [color]=\"provider().color\"\n [fill]=\"'solid'\"\n (click)=\"connect()\"\n >\n @if (isSigningIn()) {\n <ion-spinner slot=\"start\" name=\"lines-small\" />\n } @else {\n <ion-icon slot=\"start\" [name]=\"provider().icon\" />\n }\n\n <ion-label>\n @if (signingInWith() === providerID()) { Signing in... } @else { Connect\n }\n </ion-label>\n </ion-button>\n }\n </ion-buttons>\n</ion-item>\n" }]
115
+ }], ctorParameters: () => [], propDecorators: { providerID: [{ type: i0.Input, args: [{ isSignal: true, alias: "providerID", required: true }] }], signingInWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "signingInWith", required: true }] }], signingInWithChange: [{
116
+ type: Output
117
+ }] } });
118
+ //# sourceMappingURL=user-auth-provider-status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-auth-provider-status.js","sourceRoot":"","sources":["../../../../../../../../libs/auth/ui/src/lib/components/user-auth-providers/user-auth-provider-status.ts","../../../../../../../../libs/auth/ui/src/lib/components/user-auth-providers/user-auth-provider-status.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n EventEmitter,\n input,\n Output,\n signal,\n computed,\n OnDestroy,\n inject,\n} from '@angular/core';\nimport {\n IonButton,\n IonButtons,\n IonIcon,\n IonItem,\n IonLabel,\n IonSpinner,\n} from '@ionic/angular/standalone';\nimport {\n AuthProviderID,\n ISneatAuthUser,\n SneatAuthStateService,\n} from '@sneat/auth-core';\nimport { ErrorLogger, IErrorLogger } from '@sneat/core';\nimport { Subject, takeUntil } from 'rxjs';\n\ntype IonicColor =\n | 'primary'\n | 'secondary'\n | 'tertiary'\n | 'success'\n | 'warning'\n | 'danger'\n | 'light'\n | 'medium'\n | 'dark';\n\ninterface provider {\n readonly id: AuthProviderID;\n readonly title: string;\n readonly icon: string;\n readonly color: IonicColor;\n}\n\n@Component({\n selector: 'sneat-user-auth-provider-status',\n templateUrl: './user-auth-provider-status.html',\n imports: [IonItem, IonIcon, IonLabel, IonButtons, IonButton, IonSpinner],\n})\nexport class UserAuthAProviderStatusComponent implements OnDestroy {\n private readonly errorLogger = inject<IErrorLogger>(ErrorLogger);\n private readonly authStateService = inject(SneatAuthStateService);\n\n private readonly $destroyed = new Subject<void>();\n public providerID = input.required<AuthProviderID>();\n protected readonly provider = computed<provider>(() => {\n const id = this.providerID();\n switch (id) {\n case 'facebook.com':\n return {\n id: id,\n title: 'Facebook',\n icon: 'logo-facebook',\n color: 'primary',\n };\n case 'google.com':\n return {\n id,\n title: 'Google',\n icon: 'logo-google',\n color: 'danger',\n };\n case 'apple.com':\n return {\n id,\n title: 'Apple',\n icon: 'logo-apple',\n color: 'dark',\n };\n case 'microsoft.com':\n return {\n id,\n title: 'Microsoft',\n icon: 'logo-windows',\n color: 'secondary',\n };\n case undefined:\n throw new Error('Undefined provider ID');\n default:\n return {\n id,\n title: id,\n icon: 'help-circle',\n color: 'medium',\n };\n }\n });\n\n readonly signingInWith = input.required<AuthProviderID | undefined>();\n\n @Output() readonly signingInWithChange = new EventEmitter<\n AuthProviderID | undefined\n >();\n\n protected readonly currentUser = signal<ISneatAuthUser | null | undefined>(\n undefined,\n );\n\n constructor() {\n this.authStateService.authUser\n .pipe(takeUntil(this.$destroyed))\n .subscribe((authUser) => {\n this.currentUser.set(authUser);\n });\n }\n\n protected readonly isSigningIn = computed(\n () => this.signingInWith() === this.providerID(),\n );\n\n protected readonly isDisabled = computed(() => {\n const signingInWith = this.signingInWith();\n return !!signingInWith;\n });\n\n readonly authProviderUserInfo = computed(() => {\n const providerID = this.providerID();\n return this.currentUser()?.providerData?.find(\n (pd) => pd.providerId == providerID,\n );\n });\n\n readonly isConnected = computed(() => !!this.authProviderUserInfo());\n\n protected connect(): void {\n const providerID = this.providerID();\n if (!providerID) {\n throw new Error('auth providerID is not set');\n }\n this.signingInWithChange.emit(providerID);\n this.authStateService\n .linkWith(providerID)\n .then(() => {\n this.signingInWithChange.emit(undefined);\n })\n .catch((e) => {\n console.error('Failed to connect', e);\n this.signingInWithChange.emit(undefined);\n });\n }\n\n protected disconnect(): void {\n const providerID = this.providerID();\n if (!providerID) {\n this.errorLogger.logError('auth providerID is not set');\n return;\n }\n this.authStateService\n .unlinkAuthProvider(providerID)\n .then(() => this.signingInWithChange.emit(undefined))\n .catch((err) => {\n this.signingInWithChange.emit(undefined);\n this.errorLogger.logError(err);\n });\n this.signingInWithChange.emit(providerID);\n }\n\n public ngOnDestroy(): void {\n this.$destroyed.next();\n this.$destroyed.complete();\n }\n}\n","<ion-item>\n <ion-icon [color]=\"provider().color\" [name]=\"provider().icon\" slot=\"start\" />\n <ion-label>\n <h3>{{ provider().title || providerID() }}</h3>\n <p>\n @if(isConnected()) { Connected as\n <b>{{authProviderUserInfo()?.displayName}}</b>\n &nbsp;&dash;&nbsp;\n <a href=\"mailto:{{authProviderUserInfo()?.email}}\"\n >{{authProviderUserInfo()?.email}}</a\n >. } @else { Not connected yet. }\n </p>\n </ion-label>\n <ion-buttons slot=\"end\">\n @if (isConnected()) {\n <ion-button color=\"medium\" fill=\"outline\" (click)=\"disconnect()\">\n <ion-icon name=\"close-circle-outline\" slot=\"start\"></ion-icon>\n <ion-label>\n @if (isSigningIn()) { Disconnecting... } @else { Disconnect }\n </ion-label>\n </ion-button>\n } @else {\n <ion-button\n [disabled]=\"isDisabled()\"\n [color]=\"provider().color\"\n [fill]=\"'solid'\"\n (click)=\"connect()\"\n >\n @if (isSigningIn()) {\n <ion-spinner slot=\"start\" name=\"lines-small\" />\n } @else {\n <ion-icon slot=\"start\" [name]=\"provider().icon\" />\n }\n\n <ion-label>\n @if (signingInWith() === providerID()) { Signing in... } @else { Connect\n }\n </ion-label>\n </ion-button>\n }\n </ion-buttons>\n</ion-item>\n"]}
@@ -0,0 +1,3 @@
1
+ export * from './user-required-fields-modal.component';
2
+ export * from './user-required-fields.service';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../../../libs/auth/ui/src/lib/components/user-required-fields/index.ts"],"names":[],"mappings":"AAAA,cAAc,wCAAwC,CAAC;AACvD,cAAc,gCAAgC,CAAC","sourcesContent":["export * from './user-required-fields-modal.component';\nexport * from './user-required-fields.service';\n"]}
@@ -0,0 +1,111 @@
1
+ import { Component, Input, inject } from '@angular/core';
2
+ import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators, } from '@angular/forms';
3
+ import { IonButton, IonButtons, IonContent, IonFooter, IonHeader, IonIcon, IonInput, IonItem, IonItemDivider, IonLabel, IonTitle, IonToolbar, ModalController, } from '@ionic/angular/standalone';
4
+ import { SneatUserService, UserRecordService, } from '@sneat/auth-core';
5
+ import { ClassName, SelectFromListComponent } from '@sneat/ui';
6
+ import { SneatBaseComponent } from '@sneat/ui';
7
+ import { takeUntil } from 'rxjs';
8
+ import * as i0 from "@angular/core";
9
+ import * as i1 from "@angular/forms";
10
+ export class UserRequiredFieldsModalComponent extends SneatBaseComponent {
11
+ constructor() {
12
+ super();
13
+ this.modalController = inject(ModalController);
14
+ this.userRecordService = inject(UserRecordService);
15
+ this.sneatUserService = inject(SneatUserService);
16
+ this.space = { id: '' };
17
+ this.genders = [
18
+ { id: 'male', emoji: '♂️', title: 'Male' },
19
+ { id: 'female', emoji: '♀️', title: 'Female' },
20
+ { id: 'other', emoji: '⚥', title: 'Other' },
21
+ { id: 'undisclosed', iconName: 'body', title: 'Undisclosed' },
22
+ ];
23
+ this.ageGroups = [
24
+ { id: 'adult', emoji: '🧓', title: 'Adult' },
25
+ { id: 'child', emoji: '🧒', title: 'Child' },
26
+ ];
27
+ this.firstName = new FormControl('', Validators.required);
28
+ this.lastName = new FormControl('', Validators.required);
29
+ this.gender = new FormControl('', Validators.required);
30
+ this.ageGroup = new FormControl('', Validators.required);
31
+ this.form = new FormGroup({
32
+ firstName: this.firstName,
33
+ lastName: this.lastName,
34
+ gender: this.gender,
35
+ ageGroup: this.ageGroup,
36
+ });
37
+ this.submitting = false;
38
+ this.sneatUserService.userState.pipe(takeUntil(this.destroyed$)).subscribe({
39
+ next: (userState) => (this.userState = userState),
40
+ });
41
+ }
42
+ save() {
43
+ this.submitting = true;
44
+ if (!this.form.valid) {
45
+ setTimeout(() => {
46
+ alert('Please fill in all fields.');
47
+ this.submitting = false;
48
+ });
49
+ return;
50
+ }
51
+ const gender = this.gender.value;
52
+ const ageGroup = this.ageGroup.value;
53
+ const request = {
54
+ names: {
55
+ firstName: this.firstName.value || undefined,
56
+ lastName: this.lastName.value || undefined,
57
+ },
58
+ gender,
59
+ ageGroup,
60
+ };
61
+ this.userRecordService.initUserRecord(request).subscribe({
62
+ next: () => this.modalController.dismiss(true).catch(console.error),
63
+ error: (err) => {
64
+ this.errorLogger.logError('Failed to init user record:', err);
65
+ alert('Failed to init user record');
66
+ this.submitting = false;
67
+ },
68
+ });
69
+ }
70
+ close() {
71
+ this.modalController
72
+ .dismiss(false)
73
+ .catch(this.errorLogger.logErrorHandler('Failed to close modal'));
74
+ }
75
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: UserRequiredFieldsModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
76
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: UserRequiredFieldsModalComponent, isStandalone: true, selector: "sneat-user-required-fields-form", inputs: { space: "space" }, providers: [
77
+ {
78
+ provide: ClassName,
79
+ useValue: 'UserRequiredFieldsModalComponent',
80
+ },
81
+ ], usesInheritance: true, ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"primary\">\n <ion-title>About me</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button (click)=\"close()\">\n <ion-icon name=\"close-outline\" />\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n<ion-content>\n <form (ngSubmit)=\"save()\" [formGroup]=\"form\">\n <ion-item>\n <ion-input\n label=\"First name\"\n name=\"first_name\"\n type=\"text\"\n required\n max=\"20\"\n [formControl]=\"firstName\"\n />\n </ion-item>\n <ion-item>\n <ion-input\n label=\"Last name\"\n name=\"last_name\"\n type=\"text\"\n required\n max=\"20\"\n [formControl]=\"lastName\"\n />\n </ion-item>\n @if (!gender.value) {\n <ion-item-divider>\n <ion-label>Gender</ion-label>\n </ion-item-divider>\n }\n <sneat-select-from-list\n label=\"Gender\"\n [items]=\"genders\"\n [formControl]=\"gender\"\n />\n\n @if (gender.value || ageGroup.value) {\n @if (!ageGroup.value) {\n <ion-item-divider>\n <ion-label>Age group</ion-label>\n </ion-item-divider>\n }\n <sneat-select-from-list\n label=\"Age group\"\n [items]=\"ageGroups\"\n [formControl]=\"ageGroup\"\n />\n }\n </form>\n</ion-content>\n<ion-footer>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button [disabled]=\"submitting\" (click)=\"save()\">Save</ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-footer>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: SelectFromListComponent, selector: "sneat-select-from-list", inputs: ["value", "filterLabel", "label", "listLabel", "listLabelColor", "isFilterable", "isLoading", "items", "items$", "lastItemLines", "labelPlacement", "justify", "other", "canAdd", "filterItem", "selectMode", "isReadonly", "$isProcessing", "sortBy"], outputs: ["valueChange", "filterChanged"] }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { 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: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { 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: IonItemDivider, selector: "ion-item-divider", inputs: ["color", "mode", "sticky"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonFooter, selector: "ion-footer", inputs: ["collapse", "mode", "translucent"] }] }); }
82
+ }
83
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: UserRequiredFieldsModalComponent, decorators: [{
84
+ type: Component,
85
+ args: [{ selector: 'sneat-user-required-fields-form', imports: [
86
+ FormsModule,
87
+ ReactiveFormsModule,
88
+ SelectFromListComponent,
89
+ IonHeader,
90
+ IonToolbar,
91
+ IonTitle,
92
+ IonButtons,
93
+ IonButton,
94
+ IonIcon,
95
+ IonContent,
96
+ IonItem,
97
+ IonInput,
98
+ IonItemDivider,
99
+ IonLabel,
100
+ IonFooter,
101
+ ], providers: [
102
+ {
103
+ provide: ClassName,
104
+ useValue: 'UserRequiredFieldsModalComponent',
105
+ },
106
+ ], template: "<ion-header>\n <ion-toolbar color=\"primary\">\n <ion-title>About me</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button (click)=\"close()\">\n <ion-icon name=\"close-outline\" />\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n<ion-content>\n <form (ngSubmit)=\"save()\" [formGroup]=\"form\">\n <ion-item>\n <ion-input\n label=\"First name\"\n name=\"first_name\"\n type=\"text\"\n required\n max=\"20\"\n [formControl]=\"firstName\"\n />\n </ion-item>\n <ion-item>\n <ion-input\n label=\"Last name\"\n name=\"last_name\"\n type=\"text\"\n required\n max=\"20\"\n [formControl]=\"lastName\"\n />\n </ion-item>\n @if (!gender.value) {\n <ion-item-divider>\n <ion-label>Gender</ion-label>\n </ion-item-divider>\n }\n <sneat-select-from-list\n label=\"Gender\"\n [items]=\"genders\"\n [formControl]=\"gender\"\n />\n\n @if (gender.value || ageGroup.value) {\n @if (!ageGroup.value) {\n <ion-item-divider>\n <ion-label>Age group</ion-label>\n </ion-item-divider>\n }\n <sneat-select-from-list\n label=\"Age group\"\n [items]=\"ageGroups\"\n [formControl]=\"ageGroup\"\n />\n }\n </form>\n</ion-content>\n<ion-footer>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button [disabled]=\"submitting\" (click)=\"save()\">Save</ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-footer>\n" }]
107
+ }], ctorParameters: () => [], propDecorators: { space: [{
108
+ type: Input,
109
+ args: [{ required: true }]
110
+ }] } });
111
+ //# sourceMappingURL=user-required-fields-modal.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-required-fields-modal.component.js","sourceRoot":"","sources":["../../../../../../../../libs/auth/ui/src/lib/components/user-required-fields/user-required-fields-modal.component.ts","../../../../../../../../libs/auth/ui/src/lib/components/user-required-fields/user-required-fields-modal.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, inject } from '@angular/core';\nimport {\n FormControl,\n FormGroup,\n FormsModule,\n ReactiveFormsModule,\n Validators,\n} from '@angular/forms';\nimport {\n IonButton,\n IonButtons,\n IonContent,\n IonFooter,\n IonHeader,\n IonIcon,\n IonInput,\n IonItem,\n IonItemDivider,\n IonLabel,\n IonTitle,\n IonToolbar,\n ModalController,\n} from '@ionic/angular/standalone';\nimport {\n IInitUserRecordRequest,\n ISneatUserState,\n SneatUserService,\n UserRecordService,\n} from '@sneat/auth-core';\nimport { AgeGroupID, Gender } from '@sneat/core';\nimport { ClassName, ISelectItem, SelectFromListComponent } from '@sneat/ui';\nimport { ISpaceContext } from '@sneat/space-models';\nimport { SneatBaseComponent } from '@sneat/ui';\nimport { takeUntil } from 'rxjs';\n\n@Component({\n selector: 'sneat-user-required-fields-form',\n templateUrl: './user-required-fields-modal.component.html',\n imports: [\n FormsModule,\n ReactiveFormsModule,\n SelectFromListComponent,\n IonHeader,\n IonToolbar,\n IonTitle,\n IonButtons,\n IonButton,\n IonIcon,\n IonContent,\n IonItem,\n IonInput,\n IonItemDivider,\n IonLabel,\n IonFooter,\n ],\n providers: [\n {\n provide: ClassName,\n useValue: 'UserRequiredFieldsModalComponent',\n },\n ],\n})\nexport class UserRequiredFieldsModalComponent extends SneatBaseComponent {\n private readonly modalController = inject(ModalController);\n private readonly userRecordService = inject(UserRecordService);\n private readonly sneatUserService = inject(SneatUserService);\n\n @Input({ required: true }) space: ISpaceContext = { id: '' };\n\n protected readonly genders: ISelectItem[] = [\n { id: 'male', emoji: '♂️', title: 'Male' },\n { id: 'female', emoji: '♀️', title: 'Female' },\n { id: 'other', emoji: '⚥', title: 'Other' },\n { id: 'undisclosed', iconName: 'body', title: 'Undisclosed' },\n ];\n\n protected readonly ageGroups: ISelectItem[] = [\n { id: 'adult', emoji: '🧓', title: 'Adult' },\n { id: 'child', emoji: '🧒', title: 'Child' },\n ];\n\n protected readonly firstName = new FormControl('', Validators.required);\n protected readonly lastName = new FormControl('', Validators.required);\n protected readonly gender = new FormControl('', Validators.required);\n protected readonly ageGroup = new FormControl('', Validators.required);\n\n protected readonly form = new FormGroup({\n firstName: this.firstName,\n lastName: this.lastName,\n gender: this.gender,\n ageGroup: this.ageGroup,\n });\n\n protected submitting = false;\n\n protected userState?: ISneatUserState;\n\n constructor() {\n super();\n this.sneatUserService.userState.pipe(takeUntil(this.destroyed$)).subscribe({\n next: (userState) => (this.userState = userState),\n });\n }\n\n protected save(): void {\n this.submitting = true;\n if (!this.form.valid) {\n setTimeout(() => {\n alert('Please fill in all fields.');\n this.submitting = false;\n });\n return;\n }\n\n const gender = this.gender.value as Gender;\n const ageGroup = this.ageGroup.value as AgeGroupID;\n\n const request: IInitUserRecordRequest = {\n names: {\n firstName: this.firstName.value || undefined,\n lastName: this.lastName.value || undefined,\n },\n gender,\n ageGroup,\n };\n this.userRecordService.initUserRecord(request).subscribe({\n next: () => this.modalController.dismiss(true).catch(console.error),\n error: (err) => {\n this.errorLogger.logError('Failed to init user record:', err);\n alert('Failed to init user record');\n this.submitting = false;\n },\n });\n }\n\n protected close(): void {\n this.modalController\n .dismiss(false)\n .catch(this.errorLogger.logErrorHandler('Failed to close modal'));\n }\n}\n","<ion-header>\n <ion-toolbar color=\"primary\">\n <ion-title>About me</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button (click)=\"close()\">\n <ion-icon name=\"close-outline\" />\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n<ion-content>\n <form (ngSubmit)=\"save()\" [formGroup]=\"form\">\n <ion-item>\n <ion-input\n label=\"First name\"\n name=\"first_name\"\n type=\"text\"\n required\n max=\"20\"\n [formControl]=\"firstName\"\n />\n </ion-item>\n <ion-item>\n <ion-input\n label=\"Last name\"\n name=\"last_name\"\n type=\"text\"\n required\n max=\"20\"\n [formControl]=\"lastName\"\n />\n </ion-item>\n @if (!gender.value) {\n <ion-item-divider>\n <ion-label>Gender</ion-label>\n </ion-item-divider>\n }\n <sneat-select-from-list\n label=\"Gender\"\n [items]=\"genders\"\n [formControl]=\"gender\"\n />\n\n @if (gender.value || ageGroup.value) {\n @if (!ageGroup.value) {\n <ion-item-divider>\n <ion-label>Age group</ion-label>\n </ion-item-divider>\n }\n <sneat-select-from-list\n label=\"Age group\"\n [items]=\"ageGroups\"\n [formControl]=\"ageGroup\"\n />\n }\n </form>\n</ion-content>\n<ion-footer>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button [disabled]=\"submitting\" (click)=\"save()\">Save</ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-footer>\n"]}