├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── angular.json ├── e2e ├── protractor.conf.js ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts └── tsconfig.e2e.json ├── package-lock.json ├── package.json ├── projects └── xtream │ └── firebase-ngrx-user-management │ ├── karma.conf.js │ ├── ng-package.json │ ├── ng-package.prod.json │ ├── package.json │ ├── src │ ├── lib │ │ ├── actions │ │ │ ├── auth.actions.ts │ │ │ ├── custom-email-handler.actions.ts │ │ │ ├── index.ts │ │ │ ├── password-management.actions.ts │ │ │ └── providers-management.actions.ts │ │ ├── effects │ │ │ ├── custom-email-handler.effects.ts │ │ │ ├── login.effects.ts │ │ │ ├── password-management.effects.ts │ │ │ ├── providers-management.effects.ts │ │ │ ├── re-authentication-effects.service.ts │ │ │ └── registration.effects.ts │ │ ├── firebase-ngrx-user-management.module.ts │ │ ├── firebase-ngrx-user-management.service.ts │ │ ├── models │ │ │ ├── auth.model.ts │ │ │ └── credentials.ts │ │ └── reducers │ │ │ ├── custom-email-handler.reducer.ts │ │ │ ├── index.ts │ │ │ ├── password-management.reducer.ts │ │ │ ├── providers-management.reducer.ts │ │ │ └── user.reducer.ts │ ├── public_api.ts │ └── test.ts │ ├── tsconfig.lib.json │ ├── tsconfig.lib.prod.json │ ├── tsconfig.spec.json │ └── tslint.json ├── src ├── app │ ├── app-routing.module.ts │ ├── app.component.css │ ├── app.component.html │ ├── app.component.ts │ ├── app.module.ts │ ├── core │ │ ├── custom-material-module │ │ │ └── custom-material.module.ts │ │ ├── effects │ │ │ └── core.effects.ts │ │ └── reducers │ │ │ ├── app.reducer.ts │ │ │ └── index.ts │ ├── login │ │ ├── component │ │ │ └── login │ │ │ │ ├── login.component.css │ │ │ │ ├── login.component.html │ │ │ │ └── login.component.ts │ │ └── container │ │ │ └── login-container │ │ │ ├── login-container.component.css │ │ │ ├── login-container.component.html │ │ │ └── login-container.component.ts │ ├── registration │ │ ├── components │ │ │ └── registration │ │ │ │ ├── registration.component.css │ │ │ │ ├── registration.component.html │ │ │ │ └── registration.component.ts │ │ └── containers │ │ │ └── registration-container │ │ │ ├── registration-container.component.css │ │ │ ├── registration-container.component.html │ │ │ └── registration-container.component.ts │ ├── toolbar │ │ ├── components │ │ │ └── toolbar │ │ │ │ ├── toolbar.component.css │ │ │ │ ├── toolbar.component.html │ │ │ │ └── toolbar.component.ts │ │ └── containers │ │ │ └── toolbar-container │ │ │ ├── toolbar-container.component.css │ │ │ ├── toolbar-container.component.html │ │ │ └── toolbar-container.component.ts │ └── user │ │ ├── components │ │ └── user │ │ │ ├── user.component.css │ │ │ ├── user.component.html │ │ │ └── user.component.ts │ │ └── containers │ │ └── user-container │ │ ├── user-container.component.css │ │ ├── user-container.component.html │ │ └── user-container.component.ts ├── assets │ └── .gitkeep ├── browserslist ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── karma.conf.js ├── main.ts ├── polyfills.ts ├── styles.css ├── test.ts ├── tsconfig.app.json ├── tsconfig.spec.json └── tslint.json ├── tsconfig.json ├── tslint.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | yarn-error.log 34 | testem.log 35 | /typings 36 | 37 | # System Files 38 | .DS_Store 39 | Thumbs.db 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Brandon Roberts, Mike Ryan, Victor Savkin, Rob Wormald 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Firebase Ngrx User Management 2 | 3 | This library allows to easily manage user auth with ngrx and firebase. 4 | Most of auth flows have bben wrapped in ngrx effect and can be triggered dispatching specifica actions. 5 | 6 | Choose the version corresponding to your Angular version: 7 | 8 | | Angular | @xtream/firebase-ngrx-user-management | 9 | | ------------- | ------------- | 10 | | 7 | 1.x | 11 | | 8 | 2.x | 12 | | 9 | 3.x | 13 | 14 | ## Install 15 | 16 | `npm install --save @xtream/firebase-ngrx-user-management` 17 | ## Usage 18 | The library acts as a common NgRx feature module. You can find it under the "auth" key in the state tree. 19 | 20 | ### Import 21 | 22 | ```ts 23 | import {FirebaseNgrxUserManagementModule} from '@xtream/firebase-ngrx-user-management'; 24 | 25 | @NgModule({ 26 | ... 27 | imports: [ 28 | ... 29 | FirebaseNgrxUserManagementModule, 30 | AngularFireModule.initializeApp(environment.firebase), 31 | StoreModule.forRoot(reducers), 32 | ... 33 | ] 34 | }) 35 | export class AppModule { } 36 | ``` 37 | 38 | In order to try to restore the user and verify if he is logged modify the `app.component.ts` in this way: 39 | 40 | ```ts 41 | import {AuthActions} from '@xtream/firebase-ngrx-user-management'; 42 | 43 | export class AppComponent implements OnInit { 44 | ... 45 | ngOnInit(): void { 46 | this.store.dispatch(new AuthActions.GetUser()); 47 | } 48 | ... 49 | } 50 | ``` 51 | 52 | ### Available actions 53 | 54 | The library is logically divided into 4 logical parts: 55 | 56 | * Auth 57 | * Password management 58 | * Email actions handling 59 | * Providers management 60 | 61 | #### Auth 62 | | Action type | Description | 63 | | ------ | ------ | 64 | |Authenticated|Dispatched when an user is authenticated| 65 | |AuthError|Dispatched when an Authentication error occurs| 66 | |CredentialsLogin|| 67 | |CredentialsReAuthentication|| 68 | |CredentialsRegistration|| 69 | |DeleteAccount|| 70 | |DeleteAccountSuccess|| 71 | |DeleteAccountError|| 72 | |FacebookLogin|| 73 | |FacebookReAuthentication|| 74 | |FacebookRegistration|| 75 | |GetUser|Try to restore the user and dispatch as result `Authenticated` or `NotAuthenticated`| 76 | |GoogleLogin|| 77 | |GoogleReAuthentication|| 78 | |GoogleRegistration|| 79 | |Logout|| 80 | |NotAuthenticated|| 81 | |ReAuthenticationError|| 82 | |ReAuthenticationSuccess|| 83 | |RefreshToken|| 84 | |RegistrationSuccess|| 85 | |ResetAuthState|Will reset loading, error and success state| 86 | |SendVerificationEmail|| 87 | |VerificationEmailError|| 88 | |VerificationEmailSent|| 89 | 90 | Social login and registration tries to use popup, if popup-blocked error is trown it fallbacks to redirect. 91 | 92 | #### Email Actions Handlers 93 | 94 | | Action type | Description | 95 | | ------ | ------ | 96 | |CheckActionCode|Verify if the action code is valid or not| 97 | |CheckActionCodeError|| 98 | |CheckActionCodeSuccess|| 99 | |RecoverEmail|| 100 | |RecoverEmailSuccess|| 101 | |RecoverEmailError|| 102 | |ResetPassword|Set new password request| 103 | |ResetPasswordSuccess|| 104 | |ResetPasswordError|| 105 | |VerifyEmailAddress|Use action code to verify email address| 106 | |VerifyEmailAddressSuccess|| 107 | |VerifyEmailAddressError|| 108 | |VerifyPasswordResetCode|| 109 | |VerifyPasswordResetCodeError|| 110 | |VerifyPasswordResetCodeSuccess|| 111 | 112 | 113 | ### Password management 114 | | Action type | Description | 115 | | ------ | ------ | 116 | |ChangePasswordRequest|| 117 | |ChangePasswordError|| 118 | |ChangePasswordSuccess|| 119 | |ResetPasswordStatus|Reset error, loading and success| 120 | |ResetPasswordRequest|| 121 | |ResetPasswordRequestError|| 122 | |ResetPasswordRequestSuccess|| 123 | 124 | ### Providers Management 125 | | Action type | Description | 126 | | ------ | ------ | 127 | |CodeSent|Dispatched when phone number code has been sent successfully| 128 | |LinkCredentialAccount|| 129 | |LinkFacebookAccount|| 130 | |LinkGoogleAccount|| 131 | |LinkError|| 132 | |LinkSuccess|| 133 | |SendPhoneNumberCode|| 134 | |SetProviders|| 135 | |UnlinkCredentialAccount|| 136 | |UnlinkFacebookAccount|| 137 | |UnlinkGoogleAccount|| 138 | |UnlinkError|| 139 | |UnlinkPhoneNumber|| 140 | |UnlinkSuccess|| 141 | |VerifyPhoneNumber|| 142 | 143 | ### Selectors 144 | 145 | |Selector| Description | 146 | | ------ | ------ | 147 | |getUser|Select the current user (null if not logged)| 148 | |getUserProviders|Select the map of providers associated with the user `password`, `facebook`, `google`, `phone`| 149 | 150 | There are other selectors to get request status of all the previous actions 151 | 152 | 153 | # Who we are 154 | 155 | A proudly 🇮🇹 software development and data science startup.
We consider ourselves a family of talented and passionate people building their own products and powerful solutions for our clients. Get to know us more on xtreamers.io or follow us on LinkedIn. 156 | 157 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "firebase-ngrx-user-management-example": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": {}, 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "aot": true, 17 | "outputPath": "dist/firebase-ngrx-user-management-example", 18 | "index": "src/index.html", 19 | "main": "src/main.ts", 20 | "polyfills": "src/polyfills.ts", 21 | "tsConfig": "src/tsconfig.app.json", 22 | "assets": [ 23 | "src/favicon.ico", 24 | "src/assets" 25 | ], 26 | "styles": [ 27 | "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", 28 | "src/styles.css" 29 | ], 30 | "scripts": [] 31 | }, 32 | "configurations": { 33 | "production": { 34 | "budgets": [ 35 | { 36 | "type": "anyComponentStyle", 37 | "maximumWarning": "6kb" 38 | } 39 | ], 40 | "fileReplacements": [ 41 | { 42 | "replace": "src/environments/environment.ts", 43 | "with": "src/environments/environment.prod.ts" 44 | } 45 | ], 46 | "optimization": true, 47 | "outputHashing": "all", 48 | "sourceMap": false, 49 | "extractCss": true, 50 | "namedChunks": false, 51 | "aot": true, 52 | "extractLicenses": true, 53 | "vendorChunk": false, 54 | "buildOptimizer": true 55 | } 56 | } 57 | }, 58 | "serve": { 59 | "builder": "@angular-devkit/build-angular:dev-server", 60 | "options": { 61 | "browserTarget": "firebase-ngrx-user-management-example:build" 62 | }, 63 | "configurations": { 64 | "production": { 65 | "browserTarget": "firebase-ngrx-user-management-example:build:production" 66 | } 67 | } 68 | }, 69 | "extract-i18n": { 70 | "builder": "@angular-devkit/build-angular:extract-i18n", 71 | "options": { 72 | "browserTarget": "firebase-ngrx-user-management-example:build" 73 | } 74 | }, 75 | "test": { 76 | "builder": "@angular-devkit/build-angular:karma", 77 | "options": { 78 | "main": "src/test.ts", 79 | "polyfills": "src/polyfills.ts", 80 | "tsConfig": "src/tsconfig.spec.json", 81 | "karmaConfig": "src/karma.conf.js", 82 | "styles": [ 83 | "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", 84 | "src/styles.css" 85 | ], 86 | "scripts": [], 87 | "assets": [ 88 | "src/favicon.ico", 89 | "src/assets" 90 | ] 91 | } 92 | }, 93 | "lint": { 94 | "builder": "@angular-devkit/build-angular:tslint", 95 | "options": { 96 | "tsConfig": [ 97 | "src/tsconfig.app.json", 98 | "src/tsconfig.spec.json" 99 | ], 100 | "exclude": [ 101 | "**/node_modules/**" 102 | ] 103 | } 104 | } 105 | } 106 | }, 107 | "firebase-ngrx-user-management-example-e2e": { 108 | "root": "e2e/", 109 | "projectType": "application", 110 | "architect": { 111 | "e2e": { 112 | "builder": "@angular-devkit/build-angular:protractor", 113 | "options": { 114 | "protractorConfig": "e2e/protractor.conf.js", 115 | "devServerTarget": "firebase-ngrx-user-management-example:serve" 116 | }, 117 | "configurations": { 118 | "production": { 119 | "devServerTarget": "firebase-ngrx-user-management-example:serve:production" 120 | } 121 | } 122 | }, 123 | "lint": { 124 | "builder": "@angular-devkit/build-angular:tslint", 125 | "options": { 126 | "tsConfig": "e2e/tsconfig.e2e.json", 127 | "exclude": [ 128 | "**/node_modules/**" 129 | ] 130 | } 131 | } 132 | } 133 | }, 134 | "@xtream/firebase-ngrx-user-management": { 135 | "root": "projects/xtream/firebase-ngrx-user-management", 136 | "sourceRoot": "projects/xtream/firebase-ngrx-user-management/src", 137 | "projectType": "library", 138 | "prefix": "lib", 139 | "architect": { 140 | "build": { 141 | "builder": "@angular-devkit/build-ng-packagr:build", 142 | "options": { 143 | "tsConfig": "projects/xtream/firebase-ngrx-user-management/tsconfig.lib.json", 144 | "project": "projects/xtream/firebase-ngrx-user-management/ng-package.json" 145 | }, 146 | "configurations": { 147 | "production": { 148 | "tsConfig": "projects/xtream/firebase-ngrx-user-management/tsconfig.lib.prod.json" 149 | } 150 | } 151 | }, 152 | "test": { 153 | "builder": "@angular-devkit/build-angular:karma", 154 | "options": { 155 | "main": "projects/xtream/firebase-ngrx-user-management/src/test.ts", 156 | "tsConfig": "projects/xtream/firebase-ngrx-user-management/tsconfig.spec.json", 157 | "karmaConfig": "projects/xtream/firebase-ngrx-user-management/karma.conf.js" 158 | } 159 | }, 160 | "lint": { 161 | "builder": "@angular-devkit/build-angular:tslint", 162 | "options": { 163 | "tsConfig": [ 164 | "projects/xtream/firebase-ngrx-user-management/tsconfig.lib.json", 165 | "projects/xtream/firebase-ngrx-user-management/tsconfig.spec.json" 166 | ], 167 | "exclude": [ 168 | "**/node_modules/**" 169 | ] 170 | } 171 | } 172 | } 173 | } 174 | }, 175 | "defaultProject": "firebase-ngrx-user-management-example", 176 | "cli": { 177 | "analytics": false 178 | } 179 | } -------------------------------------------------------------------------------- /e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './src/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: require('path').join(__dirname, './tsconfig.e2e.json') 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('workspace-project App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to firebase-ngrx-user-management-example!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "firebase-ngrx-user-management-example", 3 | "version": "0.0.0-development", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "npx ng serve", 7 | "build": "npx ng build", 8 | "test": "npx ng test", 9 | "lint": "npx ng lint", 10 | "e2e": "npx ng e2e", 11 | "build_lib": "npx ng build @xtream/firebase-ngrx-user-management", 12 | "copy-license": "copy .\\LICENSE .\\dist\\xtream\\firebase-ngrx-user-management", 13 | "copy-readme": "copy .\\README.md .\\dist\\xtream\\firebase-ngrx-user-management", 14 | "copy-files": "npm run copy-license && npm run copy-readme", 15 | "npm_pack": "cd dist/xtream/firebase-ngrx-user-management && npm pack", 16 | "package": "npm run build_lib && npm run copy-files && npm run npm_pack", 17 | "semantic-release": "npx semantic-release", 18 | "commit": "npx git-cz" 19 | }, 20 | "private": false, 21 | "dependencies": { 22 | "@angular/animations": "~9.0.7", 23 | "@angular/cdk": "^9.1.3", 24 | "@angular/common": "~9.0.7", 25 | "@angular/compiler": "~9.0.7", 26 | "@angular/core": "~9.0.7", 27 | "@angular/fire": "^6.0.0-rc.1", 28 | "@angular/forms": "~9.0.7", 29 | "@angular/material": "^9.1.3", 30 | "@angular/platform-browser": "~9.0.7", 31 | "@angular/platform-browser-dynamic": "~9.0.7", 32 | "@angular/router": "~9.0.7", 33 | "@ngrx/effects": "^9.0.0", 34 | "@ngrx/entity": "^9.0.0", 35 | "@ngrx/store": "^9.0.0", 36 | "@ngrx/store-devtools": "^9.0.0", 37 | "core-js": "^3.2.1", 38 | "firebase": "^7.8.0", 39 | "rxjs": "~6.5.4", 40 | "tslib": "^1.10.0", 41 | "zone.js": "~0.10.2" 42 | }, 43 | "devDependencies": { 44 | "@angular-devkit/build-angular": "~0.900.7", 45 | "@angular-devkit/build-ng-packagr": "~0.900.7", 46 | "@angular/cli": "~9.0.7", 47 | "@angular/compiler-cli": "~9.0.7", 48 | "@angular/language-service": "~9.0.7", 49 | "@types/jasmine": "~3.5.0", 50 | "@types/jasminewd2": "~2.0.3", 51 | "@types/node": "^12.11.1", 52 | "codelyzer": "^5.1.2", 53 | "commitizen": "^4.0.3", 54 | "cz-conventional-changelog": "^3.0.2", 55 | "jasmine-core": "~3.5.0", 56 | "jasmine-spec-reporter": "~4.2.1", 57 | "karma": "~4.3.0", 58 | "karma-chrome-launcher": "~3.1.0", 59 | "karma-coverage-istanbul-reporter": "~2.1.0", 60 | "karma-jasmine": "~2.0.1", 61 | "karma-jasmine-html-reporter": "^1.4.2", 62 | "ng-packagr": "^9.0.0", 63 | "protractor": "~5.4.3", 64 | "semantic-release": "^15.12.5", 65 | "semantic-release-cli": "^5.2.1", 66 | "ts-node": "~8.3.0", 67 | "tslint": "~5.18.0", 68 | "typescript": "~3.7.5" 69 | }, 70 | "repository": { 71 | "type": "git", 72 | "url": "https://github.com/xtreamsrl/firebase-ngrx-user-management.git" 73 | }, 74 | "config": { 75 | "commitizen": { 76 | "path": "./node_modules/cz-conventional-changelog" 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../../coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; 32 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../../dist/xtream/firebase-ngrx-user-management", 4 | "deleteDestPath": false, 5 | "lib": { 6 | "entryFile": "src/public_api.ts" 7 | } 8 | } -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/ng-package.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../../dist/xtream/firebase-ngrx-user-management", 4 | "lib": { 5 | "entryFile": "src/public_api.ts" 6 | } 7 | } -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@xtream/firebase-ngrx-user-management", 3 | "version": "3.0.0", 4 | "description": "User auth management powerd by NgRx and Firebase", 5 | "homepage": "https://github.com/xtreamsrl/firebase-ngrx-user-management#readme", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/xtreamsrl/firebase-ngrx-user-management.git" 9 | }, 10 | "keywords": [ 11 | "NgRx", 12 | "Firebase", 13 | "Angular", 14 | "Auth", 15 | "User" 16 | ], 17 | "author": "xtream", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/xtreamsrl/firebase-ngrx-user-management/issues" 21 | }, 22 | "peerDependencies": { 23 | "@angular/common": "^9.0.7", 24 | "@angular/core": "^9.0.7", 25 | "@angular/fire": "^6.0.0-rc.1", 26 | "@angular/platform-browser-dynamic": "^9.0.7", 27 | "@ngrx/effects": "^9.0.0", 28 | "@ngrx/entity": "^9.0.0", 29 | "@ngrx/store": "^9.0.0", 30 | "core-js": "^3.2.1", 31 | "firebase": "^7.8.0", 32 | "rxjs": "^6.0.0", 33 | "zone.js": "~0.10.2" 34 | }, 35 | "devDependencies": {}, 36 | "browserslist": [ 37 | "last 1 version", 38 | "> 1%", 39 | "maintained node versions", 40 | "not dead" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/src/lib/actions/auth.actions.ts: -------------------------------------------------------------------------------- 1 | import {Action} from '@ngrx/store'; 2 | import {User} from '../models/auth.model'; 3 | import {Credentials} from '../models/credentials'; 4 | 5 | export enum AuthActionTypes { 6 | Authenticated = '[Auth] Authenticated', 7 | AuthError = '[Auth] Error', 8 | CredentialsLogin = '[Auth] Credentials Login Attempt', 9 | CredentialsReAuthentication = '[Auth] Credentials ReAuthentication attempt', 10 | CredentialsRegistration = '[Auth] Credentials Registration Attempt', 11 | DeleteAccount = '[Auth] Delete Account', 12 | DeleteAccountSuccess = '[Auth] Delete Account Success', 13 | DeleteAccountError = '[Auth] Delete Account Error', 14 | FacebookLogin = '[Auth] Facebook Login Attempt', 15 | FacebookReAuthentication = '[Auth] Facebook ReAuthentication Attempt', 16 | FacebookRegistration = '[Auth] Facebook Registration Attempt', 17 | GetUser = '[Auth] Get User', 18 | GoogleLogin = '[Auth] Google Login Attempt', 19 | GoogleReAuthentication = '[Auth] Google ReAuthentication Attempt', 20 | GoogleRegistration = '[Auth] Google Registration Attempt', 21 | Logout = '[Auth] Logout', 22 | NotAuthenticated = '[Auth] Not Authenticated', 23 | SendVerificationEmail = '[Auth] Send Verification Email', 24 | ReAuthenticationError = '[Auth] ReAuthentication Error', 25 | ReAuthenticationSuccess = '[Auth] ReAuthentication Success', 26 | RefreshToken = '[Auth] ReFresh Token', 27 | RegistrationSuccess = '[Auth] Registration Success', 28 | ResetAuthState = '[Auth] Reset State', 29 | VerificationEmailError = '[Auth] Verification Email Error', 30 | VerificationEmailSent = '[Auth] Verification Email Sent' 31 | } 32 | 33 | export class GetUser implements Action { 34 | readonly type = AuthActionTypes.GetUser; 35 | 36 | constructor(public payload?: any) { 37 | } 38 | } 39 | 40 | export class Authenticated implements Action { 41 | readonly type = AuthActionTypes.Authenticated; 42 | 43 | constructor(public payload: { user: User }) { 44 | 45 | } 46 | } 47 | 48 | export class NotAuthenticated implements Action { 49 | readonly type = AuthActionTypes.NotAuthenticated; 50 | 51 | constructor(public payload?: any) { 52 | } 53 | } 54 | 55 | export class AuthError implements Action { 56 | readonly type = AuthActionTypes.AuthError; 57 | 58 | constructor(public payload?: any) { 59 | } 60 | } 61 | 62 | export class GoogleRegistration implements Action { 63 | readonly type = AuthActionTypes.GoogleRegistration; 64 | 65 | constructor(public payload?: any) { 66 | } 67 | } 68 | 69 | export class FacebookRegistration implements Action { 70 | readonly type = AuthActionTypes.FacebookRegistration; 71 | 72 | constructor(public payload?: any) { 73 | } 74 | } 75 | 76 | export class CredentialsRegistration implements Action { 77 | readonly type = AuthActionTypes.CredentialsRegistration; 78 | 79 | constructor(public payload: Credentials) { 80 | } 81 | } 82 | 83 | export class RegistrationSuccess implements Action { 84 | readonly type = AuthActionTypes.RegistrationSuccess; 85 | 86 | constructor(public payload: { user: User }) { 87 | } 88 | } 89 | 90 | export class Logout implements Action { 91 | readonly type = AuthActionTypes.Logout; 92 | 93 | constructor(public payload?: any) { 94 | } 95 | } 96 | 97 | export class GoogleLogin implements Action { 98 | readonly type = AuthActionTypes.GoogleLogin; 99 | 100 | constructor(public payload?: any) { 101 | } 102 | } 103 | 104 | export class FacebookLogin implements Action { 105 | readonly type = AuthActionTypes.FacebookLogin; 106 | 107 | constructor(public payload?: any) { 108 | } 109 | } 110 | 111 | export class CredentialsLogin implements Action { 112 | readonly type = AuthActionTypes.CredentialsLogin; 113 | 114 | constructor(public email: string, public password: string, public remember?: boolean) { 115 | } 116 | } 117 | 118 | export class GoogleReAuthentication implements Action { 119 | readonly type = AuthActionTypes.GoogleReAuthentication; 120 | 121 | constructor(public payload?: any) { 122 | } 123 | } 124 | 125 | export class FacebookReAuthentication implements Action { 126 | readonly type = AuthActionTypes.FacebookReAuthentication; 127 | 128 | constructor(public payload?: any) { 129 | } 130 | } 131 | 132 | export class CredentialsReAuthentication implements Action { 133 | readonly type = AuthActionTypes.CredentialsReAuthentication; 134 | 135 | constructor(public email: string, public password: string, public remember?: boolean) { 136 | } 137 | } 138 | 139 | export class ReAuthenticationSuccess implements Action { 140 | readonly type = AuthActionTypes.ReAuthenticationSuccess; 141 | 142 | constructor() { 143 | } 144 | } 145 | 146 | export class ReAuthenticationError implements Action { 147 | readonly type = AuthActionTypes.ReAuthenticationError; 148 | 149 | constructor(public payload?: any) { 150 | } 151 | } 152 | 153 | export class SendVerificationEmail implements Action { 154 | readonly type = AuthActionTypes.SendVerificationEmail; 155 | 156 | constructor(public payload: { redirectUrl: string }) { 157 | } 158 | } 159 | 160 | export class VerificationEmailError implements Action { 161 | readonly type = AuthActionTypes.VerificationEmailError; 162 | 163 | constructor(public payload: { code: string, message: string }) { 164 | } 165 | } 166 | 167 | export class VerificationEmailSent implements Action { 168 | readonly type = AuthActionTypes.VerificationEmailSent; 169 | 170 | constructor() { 171 | } 172 | } 173 | 174 | export class DeleteAccount implements Action { 175 | readonly type = AuthActionTypes.DeleteAccount; 176 | 177 | constructor() { 178 | } 179 | } 180 | 181 | export class DeleteAccountSuccess implements Action { 182 | readonly type = AuthActionTypes.DeleteAccountSuccess; 183 | 184 | constructor() { 185 | } 186 | 187 | } 188 | 189 | export class DeleteAccountError implements Action { 190 | readonly type = AuthActionTypes.DeleteAccountError; 191 | 192 | constructor(public payload: { code: string; message: string; }) { 193 | } 194 | 195 | } 196 | 197 | export class ResetAuthState implements Action { 198 | readonly type = AuthActionTypes.ResetAuthState; 199 | 200 | } 201 | 202 | export class RefreshToken implements Action { 203 | readonly type = AuthActionTypes.RefreshToken; 204 | 205 | } 206 | 207 | export type AuthActionsUnion 208 | = GetUser 209 | | Authenticated 210 | | NotAuthenticated 211 | | ReAuthenticationSuccess 212 | | ReAuthenticationError 213 | | GoogleReAuthentication 214 | | FacebookReAuthentication 215 | | CredentialsReAuthentication 216 | | GoogleLogin 217 | | FacebookLogin 218 | | CredentialsLogin 219 | | AuthError 220 | | Logout 221 | | GoogleRegistration 222 | | FacebookRegistration 223 | | CredentialsRegistration 224 | | RegistrationSuccess 225 | | SendVerificationEmail 226 | | VerificationEmailSent 227 | | VerificationEmailError 228 | | DeleteAccount 229 | | DeleteAccountError 230 | | DeleteAccountSuccess 231 | | ResetAuthState 232 | | RefreshToken; 233 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/src/lib/actions/custom-email-handler.actions.ts: -------------------------------------------------------------------------------- 1 | import {Action} from '@ngrx/store'; 2 | 3 | export enum CustomEmailHandlerActionTypes { 4 | CheckActionCode = '[Auth/CustomEmailHandler] Check Action Code', 5 | CheckActionCodeError = '[Auth/CustomEmailHandler] Check Action Code Error', 6 | CheckActionCodeSuccess = '[Auth/CustomEmailHandler] Check Action Code Success', 7 | RecoverEmail = '[Auth/CustomEmailHandler] Recover Email', 8 | RecoverEmailSuccess = '[Auth/CustomEmailHandler] Recover Email Success', 9 | RecoverEmailError = '[Auth/CustomEmailHandler] Recover Email Error', 10 | ResetPassword = '[Auth/CustomEmailHandler] Reset Password', 11 | ResetPasswordSuccess = '[Auth/CustomEmailHandler] Reset Password Success', 12 | ResetPasswordError = '[Auth/CustomEmailHandler] Reset Password Error', 13 | VerifyEmailAddress = '[Auth/CustomEmailHandler] Verify Email Address', 14 | VerifyEmailAddressSuccess = '[Auth/CustomEmailHandler] Verify Email Address Success', 15 | VerifyEmailAddressError = '[Auth/CustomEmailHandler] Verify Email Address Error', 16 | VerifyPasswordResetCode = '[Auth/CustomEmailHandler] Verify Password Reset Code', 17 | VerifyPasswordResetCodeError = '[Auth/CustomEmailHandler] Verify Password Reset Error', 18 | VerifyPasswordResetCodeSuccess = '[Auth/CustomEmailHandler] Verify Password Reset Success' 19 | } 20 | 21 | export class CheckActionCode implements Action { 22 | readonly type = CustomEmailHandlerActionTypes.CheckActionCode; 23 | 24 | constructor(public payload: { actionCode: string }) { 25 | } 26 | } 27 | 28 | export class CheckActionCodeError implements Action { 29 | readonly type = CustomEmailHandlerActionTypes.CheckActionCodeError; 30 | 31 | constructor(public payload: { code: string, message: string }) { 32 | } 33 | } 34 | 35 | export class CheckActionCodeSuccess implements Action { 36 | readonly type = CustomEmailHandlerActionTypes.CheckActionCodeSuccess; 37 | 38 | constructor(public payload: { actionCode: string, restoredEmail: string }) { 39 | } 40 | } 41 | 42 | export class RecoverEmail implements Action { 43 | readonly type = CustomEmailHandlerActionTypes.RecoverEmail; 44 | 45 | constructor(public payload: { actionCode: string}) { 46 | } 47 | } 48 | 49 | export class RecoverEmailError implements Action { 50 | readonly type = CustomEmailHandlerActionTypes.RecoverEmailError; 51 | 52 | constructor(public payload: { code: string, message: string }) { 53 | } 54 | } 55 | 56 | export class RecoverEmailSuccess implements Action { 57 | readonly type = CustomEmailHandlerActionTypes.RecoverEmailSuccess; 58 | 59 | } 60 | 61 | export class ResetPassword implements Action { 62 | readonly type = CustomEmailHandlerActionTypes.ResetPassword; 63 | 64 | constructor(public payload: { actionCode: string, newPassword: string }) { 65 | } 66 | } 67 | 68 | export class ResetPasswordError implements Action { 69 | readonly type = CustomEmailHandlerActionTypes.ResetPasswordError; 70 | 71 | constructor(public payload: { code: string, message: string }) { 72 | } 73 | } 74 | 75 | export class ResetPasswordSuccess implements Action { 76 | readonly type = CustomEmailHandlerActionTypes.ResetPasswordSuccess; 77 | 78 | } 79 | 80 | export class VerifyEmailAddress implements Action { 81 | readonly type = CustomEmailHandlerActionTypes.VerifyEmailAddress; 82 | 83 | constructor(public payload: { actionCode: string}) { 84 | } 85 | } 86 | 87 | export class VerifyEmailAddressError implements Action { 88 | readonly type = CustomEmailHandlerActionTypes.VerifyEmailAddressError; 89 | 90 | constructor(public payload: { code: string, message: string }) { 91 | } 92 | } 93 | 94 | export class VerifyEmailAddressSuccess implements Action { 95 | readonly type = CustomEmailHandlerActionTypes.VerifyEmailAddressSuccess; 96 | 97 | } 98 | 99 | export class VerifyPasswordResetCode implements Action { 100 | readonly type = CustomEmailHandlerActionTypes.VerifyPasswordResetCode; 101 | 102 | constructor(public payload: { actionCode: string }) { 103 | } 104 | } 105 | 106 | export class VerifyPasswordResetCodeError implements Action { 107 | readonly type = CustomEmailHandlerActionTypes.VerifyPasswordResetCodeError; 108 | 109 | constructor(public payload: { code: string, message: string }) { 110 | } 111 | } 112 | 113 | export class VerifyPasswordResetCodeSuccess implements Action { 114 | readonly type = CustomEmailHandlerActionTypes.VerifyPasswordResetCodeSuccess; 115 | 116 | constructor(public payload: { email: string, actionCode: string }) { 117 | } 118 | } 119 | 120 | export type CustomEmailHandlerActionsUnion = CheckActionCode 121 | | CheckActionCodeError 122 | | CheckActionCodeSuccess 123 | | RecoverEmail 124 | | RecoverEmailSuccess 125 | | RecoverEmailError 126 | | ResetPassword 127 | | ResetPasswordSuccess 128 | | ResetPasswordError 129 | | VerifyEmailAddress 130 | | VerifyEmailAddressError 131 | | VerifyEmailAddressSuccess 132 | | VerifyPasswordResetCode 133 | | VerifyPasswordResetCodeError 134 | | VerifyPasswordResetCodeSuccess; 135 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/src/lib/actions/index.ts: -------------------------------------------------------------------------------- 1 | import * as CustomEmailHandlerActions from './custom-email-handler.actions'; 2 | import * as PasswordManagementActions from './password-management.actions'; 3 | import * as ProvidersManagementActions from './providers-management.actions'; 4 | import * as AuthActions from './auth.actions'; 5 | 6 | export { 7 | AuthActions, 8 | CustomEmailHandlerActions, 9 | PasswordManagementActions, 10 | ProvidersManagementActions 11 | }; 12 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/src/lib/actions/password-management.actions.ts: -------------------------------------------------------------------------------- 1 | import {Action} from '@ngrx/store'; 2 | 3 | export enum PasswordManagementActionTypes { 4 | ChangePasswordRequest = '[Auth/Password] Change Password Request', 5 | ChangePasswordError = '[Auth/Password] Change Password Error', 6 | ChangePasswordSuccess = '[Auth/Password] Change Password Success', 7 | ResetPasswordStatus = '[Auth/Password] Reset Change Password Status', 8 | ResetPasswordRequest = '[Auth/Password] Reset Password Request', 9 | ResetPasswordRequestError = '[Auth/Password] Reset Password Request Error', 10 | ResetPasswordRequestSuccess = '[Auth/Password] Reset Password Request Success' 11 | } 12 | 13 | export class ChangePasswordRequest implements Action { 14 | readonly type = PasswordManagementActionTypes.ChangePasswordRequest; 15 | 16 | constructor(public payload: { oldPassword: string, newPassword: string }) { 17 | } 18 | } 19 | 20 | export class ChangePasswordError implements Action { 21 | readonly type = PasswordManagementActionTypes.ChangePasswordError; 22 | 23 | constructor(public payload: { code: string, message: string }) { 24 | } 25 | } 26 | 27 | export class ChangePasswordSuccess implements Action { 28 | readonly type = PasswordManagementActionTypes.ChangePasswordSuccess; 29 | 30 | } 31 | 32 | export class ResetPasswordStatus implements Action { 33 | readonly type = PasswordManagementActionTypes.ResetPasswordStatus; 34 | 35 | } 36 | 37 | export class ResetPasswordRequest implements Action { 38 | readonly type = PasswordManagementActionTypes.ResetPasswordRequest; 39 | 40 | constructor(public payload: { email: string, redirectUrl: string }) { 41 | } 42 | 43 | } 44 | 45 | export class ResetPasswordRequestError implements Action { 46 | readonly type = PasswordManagementActionTypes.ResetPasswordRequestError; 47 | 48 | constructor(public payload: { code: string, message: string }) { 49 | } 50 | } 51 | 52 | export class ResetPasswordRequestSuccess implements Action { 53 | readonly type = PasswordManagementActionTypes.ResetPasswordRequestSuccess; 54 | 55 | } 56 | 57 | export type PasswordManagementActionsUnion = ChangePasswordRequest 58 | | ChangePasswordError 59 | | ChangePasswordSuccess 60 | | ResetPasswordStatus 61 | | ResetPasswordRequest 62 | | ResetPasswordRequestSuccess 63 | | ResetPasswordRequestError; 64 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/src/lib/actions/providers-management.actions.ts: -------------------------------------------------------------------------------- 1 | import {Action} from '@ngrx/store'; 2 | 3 | export enum ProvidersManagementActionTypes { 4 | CodeSent = '[Auth/Providers] Code Sent', 5 | LinkCredentialAccount = '[Auth/Providers] Link Credential Account', 6 | LinkFacebookAccount = '[Auth/Providers] Link Facebook Account', 7 | LinkGoogleAccount = '[Auth/Providers] Link Google Account', 8 | LinkError = '[Auth/Providers] Link Error', 9 | LinkSuccess = '[Auth/Providers] Link Success', 10 | SetProviders = '[Auth/Providers] Set Providers', 11 | UnlinkCredentialAccount = '[Auth/Providers] Unlink Credential Account', 12 | UnlinkFacebookAccount = '[Auth/Providers] Unlink Facebook Account', 13 | UnlinkGoogleAccount = '[Auth/Providers] Unlink Google Account', 14 | UnlinkError = '[Auth/Providers] Unlink Error', 15 | UnlinkSuccess = '[Auth/Providers] Unlink Success', 16 | 17 | SendPhoneNumberCode = '[Auth] Send Phone Number Code', 18 | UnlinkPhoneNumber = '[Auth] Unlink Phone Number', 19 | VerifyPhoneNumber = '[Auth] Verify Phone' 20 | } 21 | 22 | export class LinkCredentialAccount implements Action { 23 | readonly type = ProvidersManagementActionTypes.LinkCredentialAccount; 24 | 25 | constructor(public payload: { password: string }) { 26 | } 27 | 28 | } 29 | 30 | export class LinkFacebookAccount implements Action { 31 | readonly type = ProvidersManagementActionTypes.LinkFacebookAccount; 32 | 33 | constructor() { 34 | } 35 | 36 | } 37 | 38 | export class LinkGoogleAccount implements Action { 39 | readonly type = ProvidersManagementActionTypes.LinkGoogleAccount; 40 | 41 | constructor() { 42 | } 43 | 44 | } 45 | 46 | export class VerifyPhoneNumber implements Action { 47 | readonly type = ProvidersManagementActionTypes.VerifyPhoneNumber; 48 | 49 | constructor(public payload: { code: string }) { 50 | } 51 | 52 | } 53 | 54 | export class LinkSuccess implements Action { 55 | readonly type = ProvidersManagementActionTypes.LinkSuccess; 56 | 57 | constructor(public payload: { provider: string }) { 58 | } 59 | 60 | } 61 | 62 | export class LinkError implements Action { 63 | readonly type = ProvidersManagementActionTypes.LinkError; 64 | 65 | constructor(public payload: { code: string, message: string }) { 66 | } 67 | 68 | } 69 | 70 | export class UnlinkSuccess implements Action { 71 | readonly type = ProvidersManagementActionTypes.UnlinkSuccess; 72 | 73 | constructor(public payload: { provider: string }) { 74 | } 75 | 76 | } 77 | 78 | export class UnlinkError implements Action { 79 | readonly type = ProvidersManagementActionTypes.UnlinkError; 80 | 81 | constructor(public payload: { code: string, message: string }) { 82 | } 83 | 84 | } 85 | 86 | export class SendPhoneNumberCode implements Action { 87 | readonly type = ProvidersManagementActionTypes.SendPhoneNumberCode; 88 | 89 | constructor(public payload: { number: string, captchaContainerId: string }) { 90 | } 91 | 92 | } 93 | 94 | export class UnlinkFacebookAccount implements Action { 95 | readonly type = ProvidersManagementActionTypes.UnlinkFacebookAccount; 96 | 97 | constructor() { 98 | } 99 | 100 | } 101 | 102 | export class UnlinkGoogleAccount implements Action { 103 | readonly type = ProvidersManagementActionTypes.UnlinkGoogleAccount; 104 | 105 | constructor() { 106 | } 107 | 108 | } 109 | 110 | export class UnlinkCredentialAccount implements Action { 111 | readonly type = ProvidersManagementActionTypes.UnlinkCredentialAccount; 112 | 113 | constructor() { 114 | } 115 | 116 | } 117 | 118 | export class SetProviders implements Action { 119 | readonly type = ProvidersManagementActionTypes.SetProviders; 120 | 121 | constructor(public payload: Partial<{ 122 | facebook: boolean; 123 | google: boolean; 124 | phone: boolean; 125 | password: boolean; 126 | }>) { 127 | } 128 | 129 | } 130 | 131 | export class CodeSent implements Action { 132 | readonly type = ProvidersManagementActionTypes.CodeSent; 133 | } 134 | 135 | export class UnlinkPhoneNumber implements Action { 136 | readonly type = ProvidersManagementActionTypes.UnlinkPhoneNumber; 137 | } 138 | 139 | export type ProvidersManagementActionsUnion = LinkCredentialAccount 140 | | LinkFacebookAccount 141 | | LinkGoogleAccount 142 | | VerifyPhoneNumber 143 | | LinkError 144 | | LinkSuccess 145 | | UnlinkSuccess 146 | | UnlinkFacebookAccount 147 | | UnlinkGoogleAccount 148 | | UnlinkError 149 | | UnlinkCredentialAccount 150 | | SendPhoneNumberCode 151 | | SetProviders 152 | | CodeSent; 153 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/src/lib/effects/custom-email-handler.effects.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core'; 2 | import {Actions, Effect, ofType} from '@ngrx/effects'; 3 | import {AngularFireAuth} from '@angular/fire/auth'; 4 | import {from, Observable, of} from 'rxjs'; 5 | 6 | import * as customEmailHandlerActions from '../actions/custom-email-handler.actions'; 7 | import {catchError, map, mapTo, switchMap} from 'rxjs/operators'; 8 | 9 | export type Action = customEmailHandlerActions.CustomEmailHandlerActionsUnion; 10 | 11 | @Injectable() 12 | export class CustomEmailHandlerEffects { 13 | 14 | @Effect() 15 | verifyEmail: Observable = this.actions$.pipe( 16 | ofType(customEmailHandlerActions.CustomEmailHandlerActionTypes.VerifyEmailAddress), 17 | map(action => action.payload), 18 | switchMap(payload => { 19 | return from(this.afAuth.applyActionCode(payload.actionCode).then(async res => { 20 | })).pipe( 21 | mapTo(new customEmailHandlerActions.VerifyEmailAddressSuccess()), 22 | catchError(error => of(new customEmailHandlerActions.VerifyEmailAddressError(error))) 23 | ); 24 | }) 25 | ); 26 | 27 | @Effect() 28 | verifyPasswordResetCode: Observable = this.actions$.pipe( 29 | ofType(customEmailHandlerActions.CustomEmailHandlerActionTypes.VerifyPasswordResetCode), 30 | map(action => action.payload), 31 | switchMap(payload => { 32 | return from(this.afAuth.verifyPasswordResetCode(payload.actionCode)).pipe( 33 | switchMap((email: string) => { 34 | return of(new customEmailHandlerActions.VerifyPasswordResetCodeSuccess({ 35 | email, 36 | actionCode: payload.actionCode 37 | })); 38 | }), 39 | catchError(error => of(new customEmailHandlerActions.VerifyPasswordResetCodeError(error))) 40 | ); 41 | }) 42 | ); 43 | 44 | @Effect() 45 | resetPassword: Observable = this.actions$.pipe( 46 | ofType(customEmailHandlerActions.CustomEmailHandlerActionTypes.ResetPassword), 47 | map(action => action.payload), 48 | switchMap(payload => { 49 | return from(this.afAuth.confirmPasswordReset(payload.actionCode, payload.newPassword)).pipe( 50 | switchMap(() => { 51 | return of(new customEmailHandlerActions.ResetPasswordSuccess()); 52 | }), 53 | catchError(error => of(new customEmailHandlerActions.ResetPasswordError(error))) 54 | ); 55 | }) 56 | ); 57 | 58 | @Effect() 59 | checkActionCode: Observable = this.actions$.pipe( 60 | ofType(customEmailHandlerActions.CustomEmailHandlerActionTypes.CheckActionCode), 61 | map(action => action.payload), 62 | switchMap(payload => { 63 | return from(this.afAuth.checkActionCode(payload.actionCode)).pipe( 64 | map(info => info['data']['email']), 65 | switchMap((restoredEmail: string) => { 66 | return of(new customEmailHandlerActions.CheckActionCodeSuccess({ 67 | actionCode: payload.actionCode, 68 | restoredEmail 69 | })); 70 | }), 71 | catchError(error => of(new customEmailHandlerActions.CheckActionCodeError(error))) 72 | ); 73 | }) 74 | ); 75 | 76 | @Effect() 77 | revertOldEmail: Observable = this.actions$.pipe( 78 | ofType(customEmailHandlerActions.CustomEmailHandlerActionTypes.RecoverEmail), 79 | map(action => action.payload), 80 | switchMap(payload => { 81 | return from(this.afAuth.applyActionCode(payload.actionCode)).pipe( 82 | switchMap(() => { 83 | return of(new customEmailHandlerActions.RecoverEmailSuccess()); 84 | }), 85 | catchError(error => of(new customEmailHandlerActions.RecoverEmailError(error))) 86 | ); 87 | }) 88 | ); 89 | 90 | constructor(private actions$: Actions, 91 | private afAuth: AngularFireAuth) { 92 | 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/src/lib/effects/login.effects.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core'; 2 | import {Actions, Effect, ofType} from '@ngrx/effects'; 3 | import {User} from '../models/auth.model'; 4 | 5 | import {AngularFireAuth} from '@angular/fire/auth'; 6 | 7 | import {catchError, exhaustMap, map, switchMap, take, tap} from 'rxjs/operators'; 8 | import * as userActions from '../actions/auth.actions'; 9 | import {from, Observable, of, zip} from 'rxjs'; 10 | import {auth} from 'firebase/app'; 11 | import 'firebase/auth'; 12 | import {SetProviders} from '../actions/providers-management.actions'; 13 | import {Action} from '@ngrx/store'; 14 | import UserCredential = auth.UserCredential; 15 | 16 | const PROVIDERS_MAP = {}; 17 | PROVIDERS_MAP[auth.FacebookAuthProvider.FACEBOOK_SIGN_IN_METHOD] = 'facebook'; 18 | PROVIDERS_MAP[auth.GoogleAuthProvider.GOOGLE_SIGN_IN_METHOD] = 'google'; 19 | PROVIDERS_MAP[auth.EmailAuthProvider.EMAIL_PASSWORD_SIGN_IN_METHOD] = 'password'; 20 | PROVIDERS_MAP[auth.PhoneAuthProvider.PHONE_SIGN_IN_METHOD] = 'phone'; 21 | 22 | @Injectable() 23 | export class LoginEffects { 24 | 25 | @Effect() 26 | getUser: Observable = this.actions$.pipe( 27 | ofType(userActions.AuthActionTypes.GetUser), 28 | map((action: userActions.GetUser) => action.payload), 29 | exhaustMap(payload => this.afAuth.authState.pipe( 30 | take(1), 31 | switchMap(authData => { 32 | console.debug(authData); 33 | if (authData) { 34 | /// User logged in 35 | console.debug('USER', authData); 36 | return zip(from(authData.getIdToken(true))).pipe( 37 | switchMap(res => { 38 | console.debug('providers found', authData.providerData); 39 | const providers = authData.providerData.reduce((prev, current) => { 40 | const key = PROVIDERS_MAP[current.providerId]; 41 | if (key) { 42 | prev[key] = true; 43 | } 44 | return prev; 45 | }, {}); 46 | console.debug(providers, authData.providerData.map(p => p.providerId)); 47 | const user = new User(authData.uid, authData.displayName, authData.email, authData.phoneNumber, authData.photoURL, authData.emailVerified); 48 | return from([new SetProviders(providers), new userActions.Authenticated({user})]); 49 | }) 50 | ); 51 | } else { 52 | return of(new userActions.NotAuthenticated()); 53 | } 54 | })) 55 | ) 56 | ); 57 | 58 | @Effect() 59 | googleLogin: Observable = this.actions$.pipe( 60 | ofType(userActions.AuthActionTypes.GoogleLogin), 61 | map((action: userActions.GoogleLogin) => action.payload), 62 | exhaustMap(payload => { 63 | return from(this.doGoogleLogin()).pipe( 64 | map(credential => { 65 | // successful login 66 | return new userActions.GetUser(); 67 | }), 68 | catchError(error => of(new userActions.AuthError(error))) 69 | ); 70 | }) 71 | ); 72 | 73 | @Effect() 74 | facebookLogin: Observable = this.actions$.pipe( 75 | ofType(userActions.AuthActionTypes.FacebookLogin), 76 | map((action: userActions.FacebookLogin) => action.payload), 77 | exhaustMap(payload => { 78 | return from(this.doFacebookLogin()).pipe( 79 | map(credential => { 80 | // successful login 81 | return new userActions.GetUser(); 82 | }), 83 | catchError(error => of(new userActions.AuthError(error))) 84 | ); 85 | }) 86 | ); 87 | 88 | @Effect() 89 | loginWithCredentials: Observable = this.actions$.pipe( 90 | ofType(userActions.AuthActionTypes.CredentialsLogin), 91 | map((action: userActions.CredentialsLogin) => { 92 | return { 93 | email: action.email, 94 | password: action.password, 95 | remember: (action.remember) ? action.remember : false 96 | }; 97 | }), 98 | exhaustMap(credentials => { 99 | return from(this.doLoginWithCredentials(credentials)).pipe( 100 | map(p => { 101 | // successful login 102 | return new userActions.GetUser(); 103 | }), 104 | catchError(error => of(new userActions.AuthError(error))) 105 | ); 106 | }) 107 | ); 108 | 109 | @Effect() 110 | logout: Observable = this.actions$.pipe( 111 | ofType(userActions.AuthActionTypes.Logout), 112 | map((action: userActions.Logout) => action.payload), 113 | exhaustMap(payload => { 114 | return from(this.afAuth.signOut()); 115 | }), 116 | map(authData => { 117 | return new userActions.NotAuthenticated(); 118 | }) 119 | ); 120 | 121 | @Effect() 122 | onDeleteNotVerifiedAccount$: Observable = this.actions$.pipe( 123 | ofType(userActions.AuthActionTypes.DeleteAccount), 124 | switchMap(() => { 125 | return this.afAuth.user.pipe(tap(user => user.delete())).pipe( 126 | map(() => new userActions.DeleteAccountSuccess()), 127 | catchError(error => of(new userActions.DeleteAccountError(error))) 128 | ); 129 | }) 130 | ); 131 | 132 | @Effect({dispatch: false}) 133 | refreshToken$ = this.actions$.pipe( 134 | ofType(userActions.AuthActionTypes.RefreshToken), 135 | tap(action => this.afAuth.user.pipe(tap(user => user.getIdToken(true)))) 136 | ); 137 | 138 | constructor(private actions$: Actions, 139 | private afAuth: AngularFireAuth) { 140 | } 141 | 142 | private doFacebookLogin(): Promise { 143 | const provider = new auth.FacebookAuthProvider(); 144 | return this.afAuth.signInWithPopup(provider); 145 | } 146 | 147 | private doGoogleLogin(): Promise { 148 | const provider = new auth.GoogleAuthProvider(); 149 | provider.setCustomParameters({ 150 | prompt: 'select_account' 151 | }); 152 | return this.afAuth.signInWithPopup(provider); 153 | } 154 | 155 | private doLoginWithCredentials(credentials: { email: string, password: string, remember?: boolean }): Promise { 156 | if (credentials.remember) { 157 | return this.afAuth.setPersistence(auth.Auth.Persistence.LOCAL).then(() => { 158 | return this.afAuth.signInWithEmailAndPassword(credentials.email, credentials.password); 159 | }); 160 | } else { 161 | return this.afAuth.setPersistence(auth.Auth.Persistence.SESSION).then(() => { 162 | return this.afAuth.signInWithEmailAndPassword(credentials.email, credentials.password); 163 | }); 164 | } 165 | } 166 | 167 | } 168 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/src/lib/effects/password-management.effects.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core'; 2 | import {Actions, Effect, ofType} from '@ngrx/effects'; 3 | import {AngularFireAuth} from '@angular/fire/auth'; 4 | import {from, Observable, of} from 'rxjs'; 5 | import {Action} from '@ngrx/store'; 6 | import {auth} from 'firebase/app'; 7 | 8 | import * as PasswordActions from '../actions/password-management.actions'; 9 | import {catchError, exhaustMap, map, mapTo, take} from 'rxjs/operators'; 10 | import {User} from 'firebase'; 11 | 12 | @Injectable() 13 | export class PasswordManagementEffects { 14 | 15 | @Effect() 16 | passwordForgotten$ = this.actions$.pipe( 17 | ofType(PasswordActions.PasswordManagementActionTypes.ResetPasswordRequest), 18 | map(action => action.payload), 19 | exhaustMap(payload => { 20 | return from(this.afAuth.sendPasswordResetEmail(payload.email, {url: payload.redirectUrl})).pipe( 21 | map(authData => { 22 | return new PasswordActions.ResetPasswordRequestSuccess(); 23 | }), 24 | catchError(err => of(new PasswordActions.ResetPasswordRequestError(err))) 25 | ); 26 | }) 27 | ); 28 | private user: User; 29 | @Effect() 30 | changePassword: Observable = this.actions$.pipe( 31 | ofType(PasswordActions.PasswordManagementActionTypes.ChangePasswordRequest), 32 | map(action => action.payload), 33 | exhaustMap(payload => { 34 | const credentials = auth.EmailAuthProvider.credential(this.user.email, payload.oldPassword); 35 | return from(this.user.reauthenticateWithCredential(credentials).then(async res => { 36 | await this.user.updatePassword(payload.newPassword); 37 | return; 38 | })).pipe( 39 | mapTo(new PasswordActions.ChangePasswordSuccess()), 40 | catchError(error => of(new PasswordActions.ChangePasswordError(error))) 41 | ); 42 | }) 43 | ); 44 | 45 | constructor(private actions$: Actions, 46 | private afAuth: AngularFireAuth) { 47 | this.userObservable().subscribe(user => this.user = user); 48 | 49 | } 50 | 51 | private userObservable(): Observable { 52 | return this.afAuth.user.pipe( 53 | take(1), 54 | map(user => user) 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/src/lib/effects/providers-management.effects.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core'; 2 | import {Actions, Effect, ofType} from '@ngrx/effects'; 3 | import {from, Observable, of} from 'rxjs'; 4 | import {Action} from '@ngrx/store'; 5 | import {ProvidersManagementActions} from '../actions'; 6 | import {catchError, exhaustMap, map, mapTo, take} from 'rxjs/operators'; 7 | import {AngularFireAuth} from '@angular/fire/auth'; 8 | import {auth, User} from 'firebase/app'; 9 | import EmailAuthProvider = auth.EmailAuthProvider; 10 | import ConfirmationResult = auth.ConfirmationResult; 11 | 12 | @Injectable() 13 | export class ProvidersManagementEffects { 14 | 15 | @Effect() 16 | linkToGoogleAccount$: Observable = this.actions$.pipe( 17 | ofType(ProvidersManagementActions.ProvidersManagementActionTypes.LinkGoogleAccount), 18 | exhaustMap(actins => { 19 | return from(this.doLinkToGoogleProvider()).pipe( 20 | mapTo(new ProvidersManagementActions.LinkSuccess({provider: 'google'})), 21 | catchError(error => of(new ProvidersManagementActions.LinkError(error))) 22 | ); 23 | }) 24 | ); 25 | 26 | @Effect() 27 | linkToFacebookAccount$: Observable = this.actions$.pipe( 28 | ofType(ProvidersManagementActions.ProvidersManagementActionTypes.LinkFacebookAccount), 29 | exhaustMap(actions => { 30 | return from(this.doLinkToFacebookProvider()).pipe( 31 | mapTo(new ProvidersManagementActions.LinkSuccess({provider: 'facebook'})), 32 | catchError(error => of(new ProvidersManagementActions.LinkError(error))) 33 | ); 34 | }) 35 | ); 36 | @Effect() 37 | unlinkGoogleAccount$: Observable = this.actions$.pipe( 38 | ofType(ProvidersManagementActions.ProvidersManagementActionTypes.UnlinkGoogleAccount), 39 | exhaustMap(actions => { 40 | return from(this.doUnlinkToGoogleProvider()).pipe( 41 | mapTo(new ProvidersManagementActions.UnlinkSuccess({provider: 'google'})), 42 | catchError(error => of(new ProvidersManagementActions.UnlinkError(error))) 43 | ); 44 | }) 45 | ); 46 | @Effect() 47 | unlinkFacebookAccount$: Observable = this.actions$.pipe( 48 | ofType(ProvidersManagementActions.ProvidersManagementActionTypes.UnlinkFacebookAccount), 49 | exhaustMap(actions => { 50 | return from(this.doUnlinkToFacebookProvider()).pipe( 51 | mapTo(new ProvidersManagementActions.UnlinkSuccess({provider: 'facebook'})), 52 | catchError(error => of(new ProvidersManagementActions.UnlinkError(error))) 53 | ); 54 | }) 55 | ); 56 | @Effect() 57 | unlinkCredentialsAccount$: Observable = this.actions$.pipe( 58 | ofType(ProvidersManagementActions.ProvidersManagementActionTypes.UnlinkCredentialAccount), 59 | exhaustMap(action => { 60 | return from(this.reAuthenticate().then(async res => { 61 | await this.doUnlinkToCredentials(); 62 | })).pipe( 63 | mapTo(new ProvidersManagementActions.UnlinkSuccess({provider: 'password'})), 64 | catchError(error => of(new ProvidersManagementActions.UnlinkError(error))) 65 | ); 66 | }) 67 | ); 68 | @Effect() 69 | unlinkPhone$: Observable = this.actions$.pipe( 70 | ofType(ProvidersManagementActions.ProvidersManagementActionTypes.UnlinkPhoneNumber), 71 | exhaustMap(action => { 72 | return from(this.doUnlinkPhoneNumber()).pipe( 73 | mapTo(new ProvidersManagementActions.UnlinkSuccess({provider: 'phone'})), 74 | catchError(error => of(new ProvidersManagementActions.UnlinkError(error))) 75 | ); 76 | }) 77 | ); 78 | private phoneNumberConfirmation: ConfirmationResult; 79 | @Effect() 80 | verifyCode$: Observable = this.actions$.pipe( 81 | ofType(ProvidersManagementActions.ProvidersManagementActionTypes.VerifyPhoneNumber), 82 | map(action => action.payload), 83 | exhaustMap(payload => { 84 | return from(this.phoneNumberConfirmation.confirm(payload.code)).pipe( 85 | mapTo(new ProvidersManagementActions.LinkSuccess({provider: 'phone'})), 86 | catchError(error => of(new ProvidersManagementActions.LinkError(error))) 87 | ); 88 | }) 89 | ); 90 | private user: User; 91 | @Effect() 92 | linkToCredentialsAccount$: Observable = this.actions$.pipe( 93 | ofType(ProvidersManagementActions.ProvidersManagementActionTypes.LinkCredentialAccount), 94 | map(action => action.payload), 95 | exhaustMap(payload => { 96 | return from(this.reAuthenticate().then(async res => { 97 | await this.doLinkToCredentials(this.user.email, payload.password); 98 | })).pipe( 99 | mapTo(new ProvidersManagementActions.LinkSuccess({provider: 'password'})), 100 | catchError(error => { 101 | if (error.code === 'auth/popup-blocked') { 102 | return from(this.reAuthenticateWithRedirect().then(async res => { 103 | await this.doLinkToCredentials(this.user.email, payload.password); 104 | })).pipe( 105 | mapTo(new ProvidersManagementActions.LinkSuccess({provider: 'password'})), 106 | catchError(error1 => of(new ProvidersManagementActions.LinkError(error1))) 107 | ); 108 | } else { 109 | return of(new ProvidersManagementActions.LinkError(error)); 110 | } 111 | }) 112 | ); 113 | }) 114 | ); 115 | @Effect() 116 | sendCodeToPhone$: Observable = this.actions$.pipe( 117 | ofType(ProvidersManagementActions.ProvidersManagementActionTypes.SendPhoneNumberCode), 118 | map(action => action.payload), 119 | exhaustMap(payload => { 120 | return from((() => { 121 | return this.user.linkWithPhoneNumber(payload.number, new auth.RecaptchaVerifier(payload.captchaContainerId, { 122 | size: 'invisible', 123 | callback: token => { 124 | console.debug('Captcha token', token); 125 | 126 | } 127 | })).then(confirmation => { 128 | this.phoneNumberConfirmation = confirmation; 129 | }); 130 | })()).pipe( 131 | mapTo(new ProvidersManagementActions.CodeSent()), 132 | catchError(error => of(new ProvidersManagementActions.LinkError(error))) 133 | ); 134 | }) 135 | ); 136 | 137 | constructor(private actions$: Actions, 138 | private afAuth: AngularFireAuth) { 139 | this.userObservable().subscribe(user => this.user = user); 140 | } 141 | 142 | private doLinkToGoogleProvider(): Promise { 143 | const provider = new auth.GoogleAuthProvider(); 144 | return this.user.linkWithPopup(provider); 145 | } 146 | 147 | private doUnlinkToGoogleProvider(): Promise { 148 | const provider = new auth.GoogleAuthProvider(); 149 | return this.user.unlink(provider.providerId); 150 | } 151 | 152 | private doLinkToFacebookProvider(): Promise { 153 | const provider = new auth.FacebookAuthProvider(); 154 | return this.user.linkWithPopup(provider); 155 | } 156 | 157 | private doUnlinkToFacebookProvider(): Promise { 158 | const provider = new auth.FacebookAuthProvider(); 159 | return this.user.unlink(provider.providerId); 160 | } 161 | 162 | private doUnlinkPhoneNumber(): Promise { 163 | const provider = new auth.PhoneAuthProvider(); 164 | return this.user.unlink(provider.providerId); 165 | } 166 | 167 | private doLinkToCredentials(email: string, password: string): Promise { 168 | const credentials = EmailAuthProvider.credential(email, password); 169 | return this.user.linkAndRetrieveDataWithCredential(credentials); 170 | } 171 | 172 | private doUnlinkToCredentials(): Promise { 173 | return this.user.unlink(EmailAuthProvider.EMAIL_PASSWORD_SIGN_IN_METHOD); 174 | } 175 | 176 | private doFacebookReAuthentication(): Promise { 177 | const provider = new auth.FacebookAuthProvider(); 178 | return this.user.reauthenticateWithPopup(provider); 179 | } 180 | 181 | private doFacebookReAuthenticationWithRedirect(): Promise { 182 | const provider = new auth.FacebookAuthProvider(); 183 | return this.user.reauthenticateWithRedirect(provider); 184 | } 185 | 186 | private reAuthenticate(): Promise { 187 | return this.afAuth.fetchSignInMethodsForEmail(this.user.email).then( 188 | async res => { 189 | if (res.indexOf('facebook.com') >= 0) { 190 | await this.doFacebookReAuthentication(); 191 | return; 192 | } else { 193 | await this.doGoogleReAuthentication(); 194 | return; 195 | } 196 | } 197 | ); 198 | } 199 | 200 | private reAuthenticateWithRedirect(): Promise { 201 | return this.afAuth.fetchSignInMethodsForEmail(this.user.email).then( 202 | async res => { 203 | if (res.indexOf('facebook.com') >= 0) { 204 | await this.doFacebookReAuthenticationWithRedirect(); 205 | return; 206 | } else { 207 | await this.doGoogleReAuthenticationWithRedirect(); 208 | return; 209 | } 210 | } 211 | ); 212 | } 213 | 214 | private doGoogleReAuthentication(): Promise { 215 | const provider = new auth.GoogleAuthProvider(); 216 | return this.user.reauthenticateWithPopup(provider); 217 | } 218 | 219 | private doGoogleReAuthenticationWithRedirect(): Promise { 220 | const provider = new auth.GoogleAuthProvider(); 221 | return this.user.reauthenticateWithPopup(provider); 222 | } 223 | 224 | private userObservable(): Observable { 225 | return this.afAuth.user.pipe( 226 | take(1), 227 | map(user => user) 228 | ); 229 | } 230 | 231 | } 232 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/src/lib/effects/re-authentication-effects.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core'; 2 | import {Actions, Effect, ofType} from '@ngrx/effects'; 3 | 4 | import {AngularFireAuth} from '@angular/fire/auth'; 5 | 6 | import {catchError, exhaustMap, map, take, tap} from 'rxjs/operators'; 7 | import * as userActions from '../actions/auth.actions'; 8 | import {from, Observable, of} from 'rxjs'; 9 | import {auth} from 'firebase/app'; 10 | import {User} from 'firebase'; 11 | 12 | export type Action = userActions.AuthActionsUnion; 13 | 14 | @Injectable() 15 | export class ReAuthenticationEffects { 16 | 17 | @Effect() 18 | googleReAuthentication: Observable = this.actions.pipe( 19 | ofType(userActions.AuthActionTypes.GoogleReAuthentication), 20 | map((action: userActions.GoogleReAuthentication) => action.payload), 21 | exhaustMap(payload => { 22 | return this.doGoogleReAuthentication().pipe( 23 | map(credential => { 24 | // successful login 25 | return new userActions.ReAuthenticationSuccess(); 26 | }), 27 | catchError(error => of(new userActions.ReAuthenticationError(error))) 28 | ); 29 | }) 30 | ); 31 | @Effect() 32 | facebookReAuthentication: Observable = this.actions.pipe( 33 | ofType(userActions.AuthActionTypes.FacebookReAuthentication), 34 | map((action: userActions.FacebookReAuthentication) => action.payload), 35 | exhaustMap(payload => { 36 | return this.doFacebookReAuthentication().pipe( 37 | map(credential => { 38 | // successful login 39 | return new userActions.ReAuthenticationSuccess(); 40 | }), 41 | catchError(error => of(new userActions.ReAuthenticationError(error))) 42 | ); 43 | }) 44 | ); 45 | @Effect() 46 | reAuthenticateWithCredentials: Observable = this.actions.pipe( 47 | ofType(userActions.AuthActionTypes.CredentialsReAuthentication), 48 | map((action: userActions.CredentialsReAuthentication) => { 49 | return { 50 | email: action.email, 51 | password: action.password 52 | }; 53 | }), 54 | exhaustMap(credentials => { 55 | return this.doReAuthenticationWithCredentials(credentials).pipe( 56 | map(p => { 57 | // successful login 58 | return new userActions.ReAuthenticationSuccess(); 59 | }), 60 | catchError(error => of(new userActions.ReAuthenticationError(error))) 61 | ); 62 | }) 63 | ); 64 | private user: User; 65 | constructor(private actions: Actions, 66 | private afAuth: AngularFireAuth) { 67 | this.userObservable().subscribe(user => this.user = user); 68 | } 69 | 70 | private doFacebookReAuthentication(): Observable { 71 | const provider = new auth.FacebookAuthProvider(); 72 | return from(this.user.reauthenticateWithPopup(provider)); 73 | } 74 | 75 | private doGoogleReAuthentication(): Observable { 76 | const provider = new auth.GoogleAuthProvider(); 77 | return from(this.user.reauthenticateWithPopup(provider)); 78 | } 79 | 80 | private doReAuthenticationWithCredentials(credentials: { email: string, password: string }): Observable { 81 | const credential = auth.EmailAuthProvider.credential( 82 | credentials.email, 83 | credentials.password 84 | ); 85 | return from(this.user.reauthenticateWithCredential(credential)); 86 | } 87 | 88 | private userObservable(): Observable { 89 | return this.afAuth.user.pipe( 90 | take(1), 91 | map(user => user) 92 | ); 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/src/lib/effects/registration.effects.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core'; 2 | import {Actions, Effect, ofType} from '@ngrx/effects'; 3 | 4 | import {AngularFireAuth} from '@angular/fire/auth'; 5 | 6 | import {catchError, exhaustMap, map, switchMap, tap} from 'rxjs/operators'; 7 | import {AuthActions} from '../actions'; 8 | import {from, Observable, of} from 'rxjs'; 9 | import {auth} from 'firebase/app'; 10 | import {User} from '../models/auth.model'; 11 | import {ProvidersManagementActionsUnion, SetProviders} from '../actions/providers-management.actions'; 12 | import UserCredential = auth.UserCredential; 13 | 14 | export type Action = AuthActions.AuthActionsUnion | ProvidersManagementActionsUnion; 15 | 16 | @Injectable() 17 | export class RegistrationEffects { 18 | 19 | @Effect() 20 | googleSignUp: Observable = this.actions.pipe( 21 | ofType(AuthActions.AuthActionTypes.GoogleRegistration), 22 | map((action: AuthActions.GoogleRegistration) => action.payload), 23 | exhaustMap(payload => { 24 | return from(this.doGoogleRegistration()).pipe( 25 | switchMap(credential => { 26 | console.debug('credential', credential); 27 | const authData = credential.user; 28 | const photoUrl = authData.photoURL; 29 | const user = new User(authData.uid, authData.displayName, authData.email, authData.phoneNumber, photoUrl, authData.emailVerified); 30 | return from([new SetProviders({google: true}), new AuthActions.RegistrationSuccess({user})]); 31 | }), 32 | catchError(error => of(new AuthActions.AuthError(error))) 33 | ); 34 | }) 35 | ); 36 | @Effect() 37 | facebookSignUp: Observable = this.actions.pipe( 38 | ofType(AuthActions.AuthActionTypes.FacebookRegistration), 39 | map((action: AuthActions.FacebookRegistration) => action.payload), 40 | exhaustMap(payload => { 41 | return from(this.doFacebookRegistration()).pipe( 42 | switchMap(credential => { 43 | console.debug('facebookSignUp', credential); 44 | const authData = credential.user; 45 | const photoUrl = authData.photoURL; 46 | console.log('PhotoUrl', photoUrl); 47 | const user = new User(authData.uid, authData.displayName, authData.email, authData.phoneNumber, photoUrl, authData.emailVerified); 48 | return from([new SetProviders({facebook: true}), new AuthActions.RegistrationSuccess({user})]); 49 | }), 50 | catchError(error => of(new AuthActions.AuthError(error))) 51 | ); 52 | }) 53 | ); 54 | @Effect() 55 | signUpWithCredentials: Observable = this.actions.pipe( 56 | ofType(AuthActions.AuthActionTypes.CredentialsRegistration), 57 | map((action: AuthActions.CredentialsRegistration) => { 58 | return { 59 | email: action.payload.email, 60 | password: action.payload.password 61 | }; 62 | }), 63 | exhaustMap(credentials => { 64 | return from(this.doSignUpWithCredentials(credentials)).pipe( 65 | map(credential => { 66 | console.debug('doSignUpWithCredentials', credential); 67 | const authData = credential.user; 68 | const user = new User(authData.uid, authData.displayName, authData.email, authData.phoneNumber, authData.photoURL, authData.emailVerified); 69 | return new AuthActions.RegistrationSuccess({user}); 70 | }), 71 | catchError(error => of(new AuthActions.AuthError(error))) 72 | ); 73 | }) 74 | ); 75 | @Effect() 76 | sendVerificationEmail$: Observable = this.actions.pipe( 77 | ofType(AuthActions.AuthActionTypes.SendVerificationEmail), 78 | map(action => action.payload), 79 | exhaustMap(payload => { 80 | return this.afAuth.user.pipe(tap(user => user.sendEmailVerification({url: payload.redirectUrl}))).pipe( 81 | map(p => { 82 | return new AuthActions.VerificationEmailSent(); 83 | }), 84 | catchError(error => of(new AuthActions.VerificationEmailError(error))) 85 | ); 86 | }) 87 | ); 88 | 89 | constructor(private actions: Actions, 90 | private afAuth: AngularFireAuth) { 91 | } 92 | 93 | private doFacebookRegistration(): Promise { 94 | const provider = new auth.FacebookAuthProvider(); 95 | return this.afAuth.signInWithPopup(provider); 96 | } 97 | 98 | private doGoogleRegistration(): Promise { 99 | const provider = new auth.GoogleAuthProvider(); 100 | provider.setCustomParameters({ 101 | prompt: 'select_account' 102 | }); 103 | return this.afAuth.signInWithPopup(provider); 104 | } 105 | 106 | private doSignUpWithCredentials(credentials: { email: string, password: string }): Promise { 107 | return this.afAuth.createUserWithEmailAndPassword(credentials.email, credentials.password); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/src/lib/firebase-ngrx-user-management.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {EffectsModule} from '@ngrx/effects'; 3 | import {StoreModule} from '@ngrx/store'; 4 | 5 | import {AngularFireAuthModule} from '@angular/fire/auth'; 6 | import {reducers} from './reducers/index'; 7 | import {LoginEffects} from './effects/login.effects'; 8 | import {AngularFireModule} from '@angular/fire'; 9 | import {RegistrationEffects} from './effects/registration.effects'; 10 | import {ReAuthenticationEffects} from './effects/re-authentication-effects.service'; 11 | import {ProvidersManagementEffects} from './effects/providers-management.effects'; 12 | import {PasswordManagementEffects} from './effects/password-management.effects'; 13 | import {CustomEmailHandlerEffects} from './effects/custom-email-handler.effects'; 14 | 15 | @NgModule({ 16 | imports: [ 17 | AngularFireModule, 18 | AngularFireAuthModule, 19 | EffectsModule.forFeature([ 20 | CustomEmailHandlerEffects, 21 | LoginEffects, 22 | ReAuthenticationEffects, 23 | RegistrationEffects, 24 | ProvidersManagementEffects, 25 | PasswordManagementEffects 26 | ]), 27 | 28 | StoreModule.forFeature('auth', reducers) 29 | ], 30 | declarations: [], 31 | exports: [], 32 | providers: [] 33 | }) 34 | export class FirebaseNgrxUserManagementModule { 35 | 36 | } 37 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/src/lib/firebase-ngrx-user-management.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable({ 4 | providedIn: 'root' 5 | }) 6 | export class FirebaseNgrxUserManagementService { 7 | 8 | constructor() { } 9 | } 10 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/src/lib/models/auth.model.ts: -------------------------------------------------------------------------------- 1 | export interface IUser { 2 | uid?: string; 3 | displayName: string; 4 | email: string; 5 | photoURL?: string; 6 | phoneNumber: string; 7 | emailVerified?: boolean; 8 | } 9 | 10 | export class User implements IUser { 11 | 12 | constructor(public uid: string, 13 | public displayName: string, 14 | public email: string, 15 | public phoneNumber: string, 16 | public photoURL?: string, 17 | public emailVerified?: boolean 18 | ) { 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/src/lib/models/credentials.ts: -------------------------------------------------------------------------------- 1 | export interface Credentials { 2 | email: string; 3 | password: string; 4 | remember?: boolean; 5 | } 6 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/src/lib/reducers/custom-email-handler.reducer.ts: -------------------------------------------------------------------------------- 1 | import {CustomEmailHandlerActionsUnion, CustomEmailHandlerActionTypes} from '../actions/custom-email-handler.actions'; 2 | const initialRequestStatus = { 3 | loading: false, 4 | success: false, 5 | error: null 6 | }; 7 | 8 | export interface State { 9 | emailVerified: { 10 | loading: boolean; 11 | success: boolean; 12 | error: { 13 | code: string; 14 | message: string 15 | }; 16 | }; 17 | passwordResetCode: { 18 | email: string; 19 | loading: boolean; 20 | success: boolean; 21 | error: { 22 | code: string; 23 | message: string 24 | }; 25 | }; 26 | resetPassword: { 27 | loading: boolean; 28 | success: boolean; 29 | error: { 30 | code: string; 31 | message: string 32 | }; 33 | }; 34 | checkCode: { 35 | loading: boolean; 36 | success: boolean; 37 | error: { 38 | code: string; 39 | message: string 40 | }; 41 | }; 42 | recoverEmail: { 43 | loading: boolean; 44 | success: boolean; 45 | error: { 46 | code: string; 47 | message: string 48 | }; 49 | }; 50 | } 51 | 52 | const initialState = { 53 | emailVerified: {...initialRequestStatus}, 54 | passwordResetCode: {...initialRequestStatus}, 55 | resetPassword: {...initialRequestStatus}, 56 | checkCode: {...initialRequestStatus}, 57 | recoverEmail: {...initialRequestStatus} 58 | } as State; 59 | 60 | export function reducer(state = initialState, action: CustomEmailHandlerActionsUnion): State { 61 | switch (action.type) { 62 | 63 | case CustomEmailHandlerActionTypes.VerifyEmailAddress: { 64 | return {...state, emailVerified: {loading: true, success: false, error: null}}; 65 | } 66 | 67 | case CustomEmailHandlerActionTypes.VerifyEmailAddressError: { 68 | return {...state, emailVerified: {loading: false, success: false, error: action.payload}}; 69 | } 70 | 71 | case CustomEmailHandlerActionTypes.VerifyEmailAddressSuccess: { 72 | return {...state, emailVerified: {loading: false, success: true, error: null}}; 73 | } 74 | 75 | case CustomEmailHandlerActionTypes.VerifyPasswordResetCode: { 76 | return {...state, passwordResetCode: {email: null, loading: true, success: false, error: null}}; 77 | } 78 | 79 | case CustomEmailHandlerActionTypes.VerifyPasswordResetCodeError: { 80 | return {...state, passwordResetCode: {email: null, loading: false, success: false, error: action.payload}}; 81 | } 82 | 83 | case CustomEmailHandlerActionTypes.VerifyPasswordResetCodeSuccess: { 84 | return {...state, passwordResetCode: {email: action.payload.email, loading: false, success: true, error: null}}; 85 | } 86 | 87 | case CustomEmailHandlerActionTypes.CheckActionCode: { 88 | return {...state, checkCode: {loading: true, success: false, error: null}}; 89 | } 90 | 91 | case CustomEmailHandlerActionTypes.CheckActionCodeError: { 92 | return {...state, checkCode: {loading: false, success: false, error: action.payload}}; 93 | } 94 | 95 | case CustomEmailHandlerActionTypes.CheckActionCodeSuccess: { 96 | return {...state, checkCode: {loading: false, success: true, error: null}}; 97 | } 98 | 99 | case CustomEmailHandlerActionTypes.RecoverEmail: { 100 | return {...state, recoverEmail: {loading: true, success: false, error: null}}; 101 | } 102 | 103 | case CustomEmailHandlerActionTypes.RecoverEmailError: { 104 | return {...state, recoverEmail: {loading: false, success: false, error: action.payload}}; 105 | } 106 | 107 | case CustomEmailHandlerActionTypes.RecoverEmailSuccess: { 108 | return {...state, recoverEmail: {loading: false, success: true, error: null}}; 109 | } 110 | 111 | case CustomEmailHandlerActionTypes.ResetPassword: { 112 | return {...state, resetPassword: {loading: true, success: false, error: null}}; 113 | } 114 | 115 | case CustomEmailHandlerActionTypes.ResetPasswordError: { 116 | return {...state, resetPassword: {loading: false, success: false, error: action.payload}}; 117 | } 118 | 119 | case CustomEmailHandlerActionTypes.ResetPasswordSuccess: { 120 | return {...state, resetPassword: {loading: false, success: true, error: null}}; 121 | } 122 | default: 123 | return state; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/src/lib/reducers/index.ts: -------------------------------------------------------------------------------- 1 | import * as fromUser from './user.reducer'; 2 | import * as fromChangePassword from './password-management.reducer'; 3 | import * as fromProviders from './providers-management.reducer'; 4 | import * as fromCustomEmailHandler from './custom-email-handler.reducer'; 5 | 6 | import {ActionReducerMap, createFeatureSelector, createSelector, MemoizedSelector} from '@ngrx/store'; 7 | 8 | export interface AuthState { 9 | user: fromUser.State; 10 | changePassword: fromChangePassword.State; 11 | providers: fromProviders.State; 12 | customEmailHandler: fromCustomEmailHandler.State; 13 | } 14 | 15 | export const reducers: ActionReducerMap = { 16 | user: fromUser.reducer, 17 | changePassword: fromChangePassword.reducer, 18 | providers: fromProviders.reducer, 19 | customEmailHandler: fromCustomEmailHandler.reducer 20 | }; 21 | 22 | export const getAuthState: MemoizedSelector = createFeatureSelector('auth'); 23 | 24 | export const getUserState = createSelector( 25 | getAuthState, 26 | state => state.user 27 | ); 28 | 29 | export const isAuthLoading = createSelector( 30 | getAuthState, 31 | state => state.user.loading 32 | ); 33 | 34 | export const isUserLogged = createSelector( 35 | getAuthState, 36 | state => state.user.loggedIn && state.user.user && state.user.user.emailVerified 37 | ); 38 | 39 | export const getUser = createSelector( 40 | getAuthState, 41 | state => state.user.user 42 | ); 43 | 44 | export const getAuthError = createSelector( 45 | getAuthState, 46 | state => state.user.error 47 | ); 48 | 49 | export const isAuthSuccess = createSelector( 50 | getAuthState, 51 | state => state.user.success 52 | ); 53 | 54 | export const getChangePasswordStatus = createSelector( 55 | getAuthState, 56 | state => state.changePassword 57 | ); 58 | 59 | export const getUserProvidersState = createSelector( 60 | getAuthState, 61 | state => state.providers 62 | ); 63 | 64 | export const getUserProviders = createSelector( 65 | getAuthState, 66 | state => state.providers.providers 67 | ); 68 | 69 | export const getProvidersRequestStatus = createSelector( 70 | getAuthState, 71 | state => { 72 | return { 73 | loading: state.providers.loading, 74 | error: state.providers.error, 75 | success: state.providers.success 76 | }; 77 | } 78 | ); 79 | 80 | export const getCustomEmailHandlerRequestStatus = createSelector( 81 | getAuthState, 82 | state => { 83 | return { 84 | emailVerified: { 85 | loading: state.customEmailHandler.emailVerified.loading, 86 | error: state.customEmailHandler.emailVerified.error, 87 | success: state.customEmailHandler.emailVerified.success 88 | } 89 | }; 90 | } 91 | ); 92 | 93 | export const getEmailVerifiedStatus = createSelector( 94 | getAuthState, 95 | state => state.customEmailHandler.emailVerified 96 | ); 97 | 98 | export const getPasswordResetStatus = createSelector( 99 | getAuthState, 100 | state => state.customEmailHandler.resetPassword 101 | ); 102 | 103 | export const getVerifyResetPasswordProcedure = createSelector( 104 | getAuthState, 105 | state => state.customEmailHandler.passwordResetCode 106 | ); 107 | 108 | export const getEmailVerifiedError = createSelector( 109 | getAuthState, 110 | state => state.customEmailHandler.emailVerified.error 111 | ); 112 | 113 | export const isEmailVerifiedLoading = createSelector( 114 | getAuthState, 115 | state => state.customEmailHandler.emailVerified.loading 116 | ); 117 | 118 | export const getPasswordResetCodeError = createSelector( 119 | getAuthState, 120 | state => state.customEmailHandler.passwordResetCode.error 121 | ); 122 | 123 | export const isPasswordResetCodeLoading = createSelector( 124 | getAuthState, 125 | state => state.customEmailHandler.passwordResetCode.loading 126 | ); 127 | 128 | export const getPasswordResetCodeEmail = createSelector( 129 | getAuthState, 130 | state => state.customEmailHandler.passwordResetCode.email 131 | ); 132 | 133 | export const getResetPasswordError = createSelector( 134 | getAuthState, 135 | state => state.customEmailHandler.resetPassword.error 136 | ); 137 | 138 | export const isResetPasswordLoading = createSelector( 139 | getAuthState, 140 | state => state.customEmailHandler.resetPassword.loading 141 | ); 142 | 143 | export const getCheckCodeError = createSelector( 144 | getAuthState, 145 | state => state.customEmailHandler.checkCode.error 146 | ); 147 | 148 | export const isCheckCodeLoading = createSelector( 149 | getAuthState, 150 | state => state.customEmailHandler.checkCode.loading 151 | ); 152 | 153 | export const getRecoverEmailError = createSelector( 154 | getAuthState, 155 | state => state.customEmailHandler.recoverEmail.error 156 | ); 157 | 158 | export const isRecoverEmailLoading = createSelector( 159 | getAuthState, 160 | state => state.customEmailHandler.recoverEmail.loading 161 | ); 162 | 163 | export const getResetPasswordOrCodeError = createSelector( 164 | getAuthState, 165 | state => state.customEmailHandler.resetPassword.error || state.customEmailHandler.passwordResetCode.error 166 | ); 167 | 168 | export const getResetPasswordSuccess = createSelector( 169 | getAuthState, 170 | state => state.customEmailHandler.resetPassword.success 171 | ); 172 | 173 | export const isResetPasswordOrCodeLoading = createSelector( 174 | getAuthState, 175 | state => state.customEmailHandler.resetPassword.loading || state.customEmailHandler.passwordResetCode.loading 176 | ); 177 | 178 | export const getRecoverEmailOrCheckCodeError = createSelector( 179 | getAuthState, 180 | state => state.customEmailHandler.recoverEmail.error || state.customEmailHandler.checkCode.error 181 | ); 182 | 183 | export const isRecoverEmailOrCheckCodeLoading = createSelector( 184 | getAuthState, 185 | state => state.customEmailHandler.recoverEmail.loading || state.customEmailHandler.checkCode.loading 186 | ); 187 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/src/lib/reducers/password-management.reducer.ts: -------------------------------------------------------------------------------- 1 | import {PasswordManagementActions} from '../actions'; 2 | 3 | export interface State { 4 | loading: boolean; 5 | success: boolean; 6 | error: { 7 | code: string; 8 | message: string 9 | }; 10 | } 11 | 12 | const initialState = { 13 | loading: false, 14 | success: false, 15 | error: null 16 | } as State; 17 | 18 | export function reducer(state = initialState, action: PasswordManagementActions.PasswordManagementActionsUnion): State { 19 | switch (action.type) { 20 | 21 | case PasswordManagementActions.PasswordManagementActionTypes.ResetPasswordRequest: 22 | case PasswordManagementActions.PasswordManagementActionTypes.ChangePasswordRequest: { 23 | return {...state, loading: true, success: false, error: null}; 24 | } 25 | 26 | case PasswordManagementActions.PasswordManagementActionTypes.ResetPasswordRequestError: 27 | case PasswordManagementActions.PasswordManagementActionTypes.ChangePasswordError: { 28 | return {...state, loading: false, success: false, error: action.payload}; 29 | } 30 | 31 | case PasswordManagementActions.PasswordManagementActionTypes.ResetPasswordRequestSuccess: 32 | case PasswordManagementActions.PasswordManagementActionTypes.ChangePasswordSuccess: { 33 | return {...state, loading: false, success: true, error: null}; 34 | } 35 | 36 | case PasswordManagementActions.PasswordManagementActionTypes.ResetPasswordStatus: { 37 | return {...state, loading: false, success: false, error: null}; 38 | } 39 | 40 | default: 41 | return state; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/src/lib/reducers/providers-management.reducer.ts: -------------------------------------------------------------------------------- 1 | import {ProvidersManagementActions} from '../actions'; 2 | 3 | export interface State { 4 | loading: boolean; 5 | success: boolean; 6 | providers: { 7 | facebook: boolean; 8 | google: boolean; 9 | phone: boolean; 10 | password: boolean; 11 | }; 12 | error: { 13 | code: string; 14 | message: string 15 | }; 16 | } 17 | 18 | const initialState = { 19 | loading: false, 20 | success: false, 21 | providers: { 22 | facebook: false, 23 | google: false, 24 | phone: false, 25 | password: false 26 | }, 27 | error: null 28 | } as State; 29 | 30 | export function reducer(state = initialState, action: ProvidersManagementActions.ProvidersManagementActionsUnion): State { 31 | switch (action.type) { 32 | 33 | case ProvidersManagementActions.ProvidersManagementActionTypes.SetProviders: 34 | return {...state, providers: {...state.providers, ...action.payload}}; 35 | 36 | case ProvidersManagementActions.ProvidersManagementActionTypes.LinkCredentialAccount: 37 | case ProvidersManagementActions.ProvidersManagementActionTypes.LinkFacebookAccount: 38 | case ProvidersManagementActions.ProvidersManagementActionTypes.LinkGoogleAccount: 39 | case ProvidersManagementActions.ProvidersManagementActionTypes.UnlinkCredentialAccount: 40 | case ProvidersManagementActions.ProvidersManagementActionTypes.UnlinkGoogleAccount: 41 | case ProvidersManagementActions.ProvidersManagementActionTypes.UnlinkFacebookAccount: 42 | return {...state, success: false, error: null, loading: true}; 43 | 44 | case ProvidersManagementActions.ProvidersManagementActionTypes.LinkSuccess: 45 | return {...state, providers: {...state.providers, [action.payload.provider]: true}, success: true, error: null, loading: false}; 46 | 47 | case ProvidersManagementActions.ProvidersManagementActionTypes.UnlinkSuccess: 48 | return {...state, providers: {...state.providers, [action.payload.provider]: false}, success: true, error: null, loading: false}; 49 | 50 | case ProvidersManagementActions.ProvidersManagementActionTypes.LinkError: 51 | case ProvidersManagementActions.ProvidersManagementActionTypes.UnlinkError: 52 | return {...state, success: false, error: action.payload, loading: false}; 53 | 54 | default: 55 | return state; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/src/lib/reducers/user.reducer.ts: -------------------------------------------------------------------------------- 1 | import {AuthActions, CustomEmailHandlerActions, ProvidersManagementActions} from '../actions'; 2 | import {User} from '../models/auth.model'; 3 | 4 | export type Action = AuthActions.AuthActionsUnion 5 | | ProvidersManagementActions.ProvidersManagementActionsUnion 6 | | CustomEmailHandlerActions.CustomEmailHandlerActionsUnion; 7 | 8 | export interface State { 9 | loggedIn: boolean; 10 | user: User | null; 11 | loading: boolean; 12 | error: { 13 | code: string; 14 | message: string; 15 | }; 16 | success: boolean; 17 | } 18 | 19 | const defaultState = { 20 | loggedIn: false, 21 | user: null, 22 | loading: true, 23 | error: null, 24 | success: false 25 | }; 26 | 27 | /// Reducer function 28 | export function reducer(state: State = defaultState, action: Action): State { 29 | switch (action.type) { 30 | 31 | case AuthActions.AuthActionTypes.GetUser: 32 | return {...state, loading: true, success: false}; 33 | 34 | case AuthActions.AuthActionTypes.Authenticated: 35 | case AuthActions.AuthActionTypes.RegistrationSuccess: 36 | return {...state, user: action.payload.user, loading: false, loggedIn: true, success: false}; 37 | 38 | case AuthActions.AuthActionTypes.NotAuthenticated: 39 | return {...state, ...defaultState, loading: false, loggedIn: false, success: false}; 40 | 41 | case AuthActions.AuthActionTypes.GoogleLogin: 42 | case AuthActions.AuthActionTypes.FacebookLogin: 43 | case AuthActions.AuthActionTypes.GoogleRegistration: 44 | case AuthActions.AuthActionTypes.FacebookRegistration: 45 | case AuthActions.AuthActionTypes.CredentialsLogin: 46 | case AuthActions.AuthActionTypes.CredentialsRegistration: 47 | case AuthActions.AuthActionTypes.FacebookReAuthentication: 48 | case AuthActions.AuthActionTypes.CredentialsReAuthentication: 49 | case AuthActions.AuthActionTypes.GoogleReAuthentication: 50 | return {...state, loading: true, success: false}; 51 | 52 | case AuthActions.AuthActionTypes.ResetAuthState: 53 | return {...state, loading: false, success: false, error: null}; 54 | 55 | case AuthActions.AuthActionTypes.AuthError: 56 | case AuthActions.AuthActionTypes.ReAuthenticationError: 57 | return {...state, loading: false, success: false, error: action.payload}; 58 | case AuthActions.AuthActionTypes.ReAuthenticationSuccess: 59 | return {...state, loading: false, error: null, success: true}; 60 | case AuthActions.AuthActionTypes.Logout: 61 | return {...state, loading: true}; 62 | case AuthActions.AuthActionTypes.DeleteAccount: 63 | return {...state, loading: true, success: true, error: null}; 64 | case AuthActions.AuthActionTypes.DeleteAccountError: 65 | return {...state, loading: false, success: false, error: action.payload}; 66 | case AuthActions.AuthActionTypes.DeleteAccountSuccess: 67 | return {...state, loading: false, loggedIn: false, success: true, error: null, user: null}; 68 | 69 | case CustomEmailHandlerActions.CustomEmailHandlerActionTypes.VerifyEmailAddressSuccess: 70 | return {...state, user: {...state.user, emailVerified: true}}; 71 | 72 | default: 73 | return state; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/src/public_api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of firebase-ngrx-user-management 3 | */ 4 | 5 | import {IUser, User} from './lib/models/auth.model'; 6 | import { 7 | AuthState, 8 | getAuthError, 9 | getAuthState, 10 | getChangePasswordStatus, 11 | getCheckCodeError, 12 | getCustomEmailHandlerRequestStatus, 13 | getEmailVerifiedError, 14 | getEmailVerifiedStatus, 15 | getPasswordResetCodeEmail, 16 | getPasswordResetCodeError, 17 | getPasswordResetStatus, 18 | getProvidersRequestStatus, 19 | getRecoverEmailError, 20 | getRecoverEmailOrCheckCodeError, 21 | getResetPasswordError, 22 | getResetPasswordOrCodeError, 23 | getResetPasswordSuccess, 24 | getUser, 25 | getUserProviders, 26 | getUserProvidersState, 27 | getUserState, 28 | getVerifyResetPasswordProcedure, 29 | isAuthLoading, 30 | isAuthSuccess, 31 | isCheckCodeLoading, 32 | isEmailVerifiedLoading, 33 | isPasswordResetCodeLoading, 34 | isRecoverEmailLoading, 35 | isRecoverEmailOrCheckCodeLoading, 36 | isResetPasswordLoading, 37 | isResetPasswordOrCodeLoading, 38 | isUserLogged 39 | } from './lib/reducers/index'; 40 | import {Credentials} from './lib/models/credentials'; 41 | import {AuthActions, CustomEmailHandlerActions, PasswordManagementActions, ProvidersManagementActions} from './lib/actions/index'; 42 | import {State as UserState} from './lib/reducers/user.reducer'; 43 | import {State as ProvidersState} from './lib/reducers/providers-management.reducer'; 44 | 45 | export * from './lib/firebase-ngrx-user-management.service'; 46 | export * from './lib/firebase-ngrx-user-management.module'; 47 | 48 | export { 49 | AuthState, 50 | Credentials, 51 | IUser, 52 | User, 53 | getAuthState, 54 | isAuthLoading, 55 | PasswordManagementActions, 56 | ProvidersManagementActions, 57 | UserState, 58 | getChangePasswordStatus, 59 | isUserLogged, 60 | getUser, 61 | getAuthError, 62 | isAuthSuccess, 63 | getUserState, 64 | getUserProvidersState, 65 | getUserProviders, 66 | getProvidersRequestStatus, 67 | getCustomEmailHandlerRequestStatus, 68 | isEmailVerifiedLoading, 69 | getEmailVerifiedError, 70 | isRecoverEmailLoading, 71 | isResetPasswordLoading, 72 | isPasswordResetCodeLoading, 73 | isCheckCodeLoading, 74 | getPasswordResetCodeError, 75 | getRecoverEmailError, 76 | getCheckCodeError, 77 | getResetPasswordError, 78 | isResetPasswordOrCodeLoading, 79 | getResetPasswordOrCodeError, 80 | getRecoverEmailOrCheckCodeError, 81 | isRecoverEmailOrCheckCodeLoading, 82 | getPasswordResetCodeEmail, 83 | getResetPasswordSuccess, 84 | ProvidersState, 85 | AuthActions, 86 | CustomEmailHandlerActions, 87 | getEmailVerifiedStatus, 88 | getVerifyResetPasswordProcedure, 89 | getPasswordResetStatus 90 | }; 91 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'core-js/es7/reflect'; 4 | import 'zone.js/dist/zone'; 5 | import 'zone.js/dist/zone-testing'; 6 | import { getTestBed } from '@angular/core/testing'; 7 | import { 8 | BrowserDynamicTestingModule, 9 | platformBrowserDynamicTesting 10 | } from '@angular/platform-browser-dynamic/testing'; 11 | 12 | declare const require: any; 13 | 14 | // First, initialize the Angular testing environment. 15 | getTestBed() 16 | .initTestEnvironment( 17 | BrowserDynamicTestingModule, 18 | platformBrowserDynamicTesting() 19 | ); 20 | // Then we find all the tests. 21 | const context = require.context('./', true, /\.spec\.ts$/); 22 | // And load the modules. 23 | context.keys() 24 | .map(context); 25 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../out-tsc/lib", 5 | "target": "es2015", 6 | "module": "es2015", 7 | "moduleResolution": "node", 8 | "declaration": true, 9 | "sourceMap": true, 10 | "inlineSources": true, 11 | "emitDecoratorMetadata": true, 12 | "experimentalDecorators": true, 13 | "importHelpers": true, 14 | "types": [], 15 | "lib": [ 16 | "dom", 17 | "es2015" 18 | ] 19 | }, 20 | "angularCompilerOptions": { 21 | "skipTemplateCodegen": true, 22 | "strictMetadataEmit": true, 23 | "fullTemplateTypeCheck": true, 24 | "strictInjectionParameters": true, 25 | "flatModuleId": "AUTOGENERATED", 26 | "flatModuleOutFile": "AUTOGENERATED", 27 | "enableIvy": false 28 | }, 29 | "exclude": [ 30 | "src/test.ts", 31 | "**/*.spec.ts" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.lib.json", 3 | "angularCompilerOptions": { 4 | "enableIvy": false 5 | } 6 | } -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts" 12 | ], 13 | "include": [ 14 | "**/*.spec.ts", 15 | "**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /projects/xtream/firebase-ngrx-user-management/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "lib", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "lib", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {RouterModule, Routes} from '@angular/router'; 3 | import {LoginContainerComponent} from './login/container/login-container/login-container.component'; 4 | import {RegistrationContainerComponent} from './registration/containers/registration-container/registration-container.component'; 5 | import {UserContainerComponent} from './user/containers/user-container/user-container.component'; 6 | 7 | const routes: Routes = [ 8 | {path: 'user', component: UserContainerComponent}, 9 | {path: 'login', component: LoginContainerComponent}, 10 | {path: 'registration', component: RegistrationContainerComponent}, 11 | {path: '', redirectTo: 'login', pathMatch: 'full'} 12 | ]; 13 | 14 | @NgModule({ 15 | imports: [ 16 | RouterModule.forRoot(routes) 17 | ], 18 | exports: [ 19 | RouterModule 20 | ] 21 | }) 22 | export class AppRoutingModule { 23 | } 24 | -------------------------------------------------------------------------------- /src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtreamsrl/firebase-ngrx-user-management/4779e365fcf9d27bbe2df6e7cf1c28d3a2b7177b/src/app/app.component.css -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |

5 | {{ title }} 6 |

7 |
8 | 9 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import {Store} from '@ngrx/store'; 3 | import {AuthActions} from '@xtream/firebase-ngrx-user-management'; 4 | import {AuthState} from '../../projects/xtream/firebase-ngrx-user-management/src/lib/reducers'; 5 | 6 | @Component({ 7 | selector: 'app-root', 8 | templateUrl: './app.component.html', 9 | styleUrls: ['./app.component.css'] 10 | }) 11 | export class AppComponent { 12 | title = 'firebase-ngrx-user-management-example'; 13 | 14 | constructor(private store: Store) { 15 | this.store.dispatch(new AuthActions.GetUser()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {AppComponent} from './app.component'; 3 | import {LoginComponent} from './login/component/login/login.component'; 4 | import {UserComponent} from './user/components/user/user.component'; 5 | import {CustomMaterialModule} from './core/custom-material-module/custom-material.module'; 6 | import {FormsModule, ReactiveFormsModule} from '@angular/forms'; 7 | import {AppRoutingModule} from './app-routing.module'; 8 | import {LoginContainerComponent} from './login/container/login-container/login-container.component'; 9 | import {AngularFireModule} from '@angular/fire'; 10 | import {environment} from '../environments/environment'; 11 | import {StoreModule} from '@ngrx/store'; 12 | import {reducers} from './core/reducers'; 13 | import {StoreDevtoolsModule} from '@ngrx/store-devtools'; 14 | import {FirebaseNgrxUserManagementModule} from '@xtream/firebase-ngrx-user-management'; 15 | import {EffectsModule} from '@ngrx/effects'; 16 | import {ToolbarContainerComponent} from './toolbar/containers/toolbar-container/toolbar-container.component'; 17 | import {ToolbarComponent} from './toolbar/components/toolbar/toolbar.component'; 18 | import {RegistrationComponent} from './registration/components/registration/registration.component'; 19 | import {RegistrationContainerComponent} from './registration/containers/registration-container/registration-container.component'; 20 | import {UserContainerComponent} from './user/containers/user-container/user-container.component'; 21 | import {CoreEffects} from './core/effects/core.effects'; 22 | 23 | @NgModule({ 24 | declarations: [ 25 | AppComponent, 26 | LoginComponent, 27 | UserComponent, 28 | LoginContainerComponent, 29 | ToolbarContainerComponent, 30 | ToolbarComponent, 31 | RegistrationComponent, 32 | RegistrationContainerComponent, 33 | UserContainerComponent 34 | ], 35 | imports: [ 36 | CustomMaterialModule, 37 | FormsModule, 38 | AppRoutingModule, 39 | ReactiveFormsModule, 40 | FirebaseNgrxUserManagementModule, 41 | AngularFireModule.initializeApp(environment.firebase), 42 | StoreModule.forRoot(reducers), 43 | EffectsModule.forRoot([CoreEffects]), 44 | !environment.production ? StoreDevtoolsModule.instrument() : [] 45 | ], 46 | providers: [], 47 | bootstrap: [AppComponent] 48 | }) 49 | export class AppModule { 50 | } 51 | -------------------------------------------------------------------------------- /src/app/core/custom-material-module/custom-material.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {BrowserModule} from '@angular/platform-browser'; 4 | import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; 5 | import { MatButtonModule } from '@angular/material/button'; 6 | import { MatCardModule } from '@angular/material/card'; 7 | import { MatCheckboxModule } from '@angular/material/checkbox'; 8 | import { MatDialogModule } from '@angular/material/dialog'; 9 | import { MatIconModule } from '@angular/material/icon'; 10 | import { MatInputModule } from '@angular/material/input'; 11 | import { MatMenuModule } from '@angular/material/menu'; 12 | import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; 13 | import { MatTableModule } from '@angular/material/table'; 14 | import { MatToolbarModule } from '@angular/material/toolbar'; 15 | 16 | @NgModule({ 17 | imports: [ 18 | CommonModule, 19 | BrowserModule, 20 | BrowserAnimationsModule, 21 | CommonModule, 22 | MatToolbarModule, 23 | MatButtonModule, 24 | MatCardModule, 25 | MatInputModule, 26 | MatDialogModule, 27 | MatTableModule, 28 | MatMenuModule, 29 | MatIconModule, 30 | MatProgressSpinnerModule, 31 | MatCheckboxModule 32 | ], 33 | exports: [ 34 | CommonModule, 35 | MatToolbarModule, 36 | MatButtonModule, 37 | MatCardModule, 38 | MatInputModule, 39 | MatDialogModule, 40 | MatTableModule, 41 | MatMenuModule, 42 | MatIconModule, 43 | MatProgressSpinnerModule, 44 | MatCheckboxModule 45 | ], 46 | declarations: [] 47 | }) 48 | export class CustomMaterialModule { 49 | } 50 | -------------------------------------------------------------------------------- /src/app/core/effects/core.effects.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core'; 2 | import {Router} from '@angular/router'; 3 | import * as AuthActions from '../../../../projects/xtream/firebase-ngrx-user-management/src/lib/actions/auth.actions'; 4 | import {tap} from 'rxjs/operators'; 5 | import {Actions, Effect, ofType} from '@ngrx/effects'; 6 | 7 | export type Action = AuthActions.AuthActionsUnion; 8 | 9 | @Injectable() 10 | export class CoreEffects { 11 | 12 | constructor(private actions: Actions, private router: Router) { 13 | } 14 | 15 | 16 | @Effect({dispatch: false}) 17 | routeRegistrationEffect = this.actions.pipe( 18 | ofType(AuthActions.AuthActionTypes.RegistrationSuccess), 19 | tap((payload) => { 20 | console.log(payload); 21 | this.router.navigate(['/user']); 22 | }) 23 | ); 24 | 25 | @Effect({dispatch: false}) 26 | routeAuthenticationEffect = this.actions.pipe( 27 | ofType(AuthActions.AuthActionTypes.Authenticated), 28 | tap((payload) => { 29 | console.log(payload); 30 | this.router.navigate(['/user']); 31 | }) 32 | ); 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/app/core/reducers/app.reducer.ts: -------------------------------------------------------------------------------- 1 | import {State} from './index'; 2 | import {Action} from '@ngrx/store'; 3 | 4 | export function appReducer(state: State = {} as State, action: Action): State { 5 | switch (action.type) { 6 | default: 7 | return state; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/app/core/reducers/index.ts: -------------------------------------------------------------------------------- 1 | import {ActionReducer, ActionReducerMap} from '@ngrx/store'; 2 | import * as fromApp from './app.reducer'; 3 | import {Params} from '@angular/router'; 4 | 5 | export interface RouterStateUrl { 6 | url: string; 7 | queryParams: Params; 8 | params: Params; 9 | } 10 | 11 | export interface State { 12 | app: any; 13 | } 14 | 15 | export function debug(reducer: ActionReducer): ActionReducer { 16 | return (state, action) => { 17 | return reducer(state, action); 18 | }; 19 | } 20 | 21 | export const reducers: ActionReducerMap = { 22 | app: fromApp.appReducer 23 | }; 24 | 25 | -------------------------------------------------------------------------------- /src/app/login/component/login/login.component.css: -------------------------------------------------------------------------------- 1 | .main-card { 2 | margin: auto; 3 | max-width: 200px; 4 | } 5 | 6 | .success-message { 7 | color: green; 8 | } 9 | 10 | .error-message { 11 | color: red; 12 | } 13 | -------------------------------------------------------------------------------- /src/app/login/component/login/login.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Login 4 | 5 | 6 |
7 | 8 | 9 |
10 |
11 | 12 | 13 | 18 | 19 | 20 | 25 | 26 | 27 | 30 | 31 |
14 | 15 | 16 | 17 |
21 | 22 | 23 | 24 |
28 | Remember me! 29 |
32 |
33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |
42 | LOGIN ERROR: {{ error }} 43 |
44 |
45 | LOGIN SUCCESSFULLY DONE 46 |
47 |
48 | 49 |
50 | -------------------------------------------------------------------------------- /src/app/login/component/login/login.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; 2 | import {FormControl, FormGroup, Validators} from '@angular/forms'; 3 | import {Credentials} from '@xtream/firebase-ngrx-user-management'; 4 | import {Router} from '@angular/router'; 5 | 6 | @Component({ 7 | selector: 'app-login', 8 | templateUrl: './login.component.html', 9 | styleUrls: ['./login.component.css'] 10 | }) 11 | export class LoginComponent implements OnInit { 12 | public loginForm: FormGroup; 13 | @Input() error: string; 14 | @Input() success: boolean; 15 | @Output() googleLogin = new EventEmitter(); 16 | @Output() facebookLogin = new EventEmitter(); 17 | @Output() loginWithCredentials = new EventEmitter(); 18 | 19 | constructor(private router: Router) { 20 | this.loginForm = new FormGroup({ 21 | email: new FormControl(null, [Validators.required]), 22 | password: new FormControl(null, [Validators.required]), 23 | rememberMe: new FormControl(true) 24 | }); 25 | } 26 | 27 | ngOnInit() { 28 | } 29 | 30 | login(): void { 31 | this.error = null; 32 | this.loginWithCredentials.emit(this.loginForm.getRawValue()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/app/login/container/login-container/login-container.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtreamsrl/firebase-ngrx-user-management/4779e365fcf9d27bbe2df6e7cf1c28d3a2b7177b/src/app/login/container/login-container/login-container.component.css -------------------------------------------------------------------------------- /src/app/login/container/login-container/login-container.component.html: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /src/app/login/container/login-container/login-container.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit} from '@angular/core'; 2 | import {AuthActions, AuthState} from '@xtream/firebase-ngrx-user-management'; 3 | import {select, Store} from '@ngrx/store'; 4 | import {Observable} from 'rxjs'; 5 | import {getAuthError, isAuthSuccess} from '@xtream/firebase-ngrx-user-management'; 6 | 7 | @Component({ 8 | selector: 'app-login-container', 9 | templateUrl: './login-container.component.html', 10 | styleUrls: ['./login-container.component.css'] 11 | }) 12 | export class LoginContainerComponent implements OnInit { 13 | public error$: Observable<{ code: string }>; 14 | public success$: Observable; 15 | 16 | constructor(private store: Store) { 17 | this.error$ = this.store.pipe( 18 | select(getAuthError) 19 | ); 20 | this.success$ = this.store.pipe( 21 | select(isAuthSuccess) 22 | ); 23 | } 24 | 25 | ngOnInit(): void { 26 | } 27 | 28 | onGoogleLogin(): void { 29 | this.store.dispatch(new AuthActions.GoogleLogin()); 30 | } 31 | 32 | onFacebookLogin(): void { 33 | this.store.dispatch(new AuthActions.FacebookLogin()); 34 | } 35 | 36 | onLoginWithCredentials(credentials: { email: string, password: string, rememberMe?: boolean }): void { 37 | this.store.dispatch(new AuthActions.CredentialsLogin(credentials.email, credentials.password, credentials.rememberMe)); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/app/registration/components/registration/registration.component.css: -------------------------------------------------------------------------------- 1 | .main-card { 2 | margin: auto; 3 | max-width: 200px; 4 | } 5 | 6 | .error-message{ 7 | color: red; 8 | } 9 | -------------------------------------------------------------------------------- /src/app/registration/components/registration/registration.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Registration 4 | 5 | 6 |
7 | 8 | 9 |
10 |
11 | 12 | 13 | 18 | 19 | 20 | 25 | 26 | 27 | 33 | 34 |
14 | 15 | 16 | 17 |
21 | 22 | 23 | 24 |
28 | 29 | 31 | 32 |
35 |
36 |
37 | 38 | 39 | 40 | 41 |
42 | LOGIN ERROR: {{ error }} 43 |
44 |
45 | LOGIN SUCCESSFULLY DONE 46 |
47 |
48 |
49 | -------------------------------------------------------------------------------- /src/app/registration/components/registration/registration.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; 2 | import {FormControl, FormGroup, Validators} from '@angular/forms'; 3 | import {Credentials} from '@xtream/firebase-ngrx-user-management'; 4 | 5 | @Component({ 6 | selector: 'app-registration', 7 | templateUrl: './registration.component.html', 8 | styleUrls: ['./registration.component.css'] 9 | }) 10 | export class RegistrationComponent implements OnInit { 11 | 12 | public registrationForm: FormGroup; 13 | @Input() error: string; 14 | @Input() success: boolean; 15 | @Output() googleLogin = new EventEmitter(); 16 | @Output() facebookLogin = new EventEmitter(); 17 | @Output() signUpWithCredentials = new EventEmitter(); 18 | 19 | constructor() { 20 | this.registrationForm = new FormGroup({ 21 | email: new FormControl(null, [Validators.required]), 22 | password: new FormControl(null, [Validators.required, Validators.minLength(6)]), 23 | confirmPassword: new FormControl(null, [Validators.required]) 24 | }, this.passwordConfirming.bind(this)); 25 | } 26 | 27 | ngOnInit(): void { 28 | } 29 | 30 | passwordConfirming(frm: FormGroup): any { 31 | if (frm.get('password').value !== frm.get('confirmPassword').value) { 32 | this.registrationForm.controls['confirmPassword'].setErrors({passwordsMismatch: true}); 33 | return {passwordsMismatch: true}; 34 | } 35 | return null; 36 | } 37 | 38 | public signUp(): void { 39 | this.error = null; 40 | this.signUpWithCredentials.emit({ 41 | email: this.registrationForm.controls['email'].value, 42 | password: this.registrationForm.controls['password'].value 43 | } 44 | ); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/app/registration/containers/registration-container/registration-container.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtreamsrl/firebase-ngrx-user-management/4779e365fcf9d27bbe2df6e7cf1c28d3a2b7177b/src/app/registration/containers/registration-container/registration-container.component.css -------------------------------------------------------------------------------- /src/app/registration/containers/registration-container/registration-container.component.html: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/app/registration/containers/registration-container/registration-container.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit} from '@angular/core'; 2 | import {AuthActions, AuthState} from '@xtream/firebase-ngrx-user-management'; 3 | import {select, Store} from '@ngrx/store'; 4 | import {Credentials} from '@xtream/firebase-ngrx-user-management'; 5 | import {Observable} from 'rxjs'; 6 | import {getAuthError, isAuthSuccess} from '@xtream/firebase-ngrx-user-management'; 7 | 8 | @Component({ 9 | selector: 'app-registration-container', 10 | templateUrl: './registration-container.component.html', 11 | styleUrls: ['./registration-container.component.css'] 12 | }) 13 | export class RegistrationContainerComponent implements OnInit { 14 | 15 | public error$: Observable<{ code: string }>; 16 | public success$: Observable; 17 | 18 | constructor(private store: Store) { 19 | this.error$ = this.store.pipe( 20 | select(getAuthError) 21 | ); 22 | this.success$ = this.store.pipe( 23 | select(isAuthSuccess) 24 | ); 25 | } 26 | 27 | ngOnInit(): void { 28 | } 29 | 30 | onGoogleLogin(): void { 31 | this.store.dispatch(new AuthActions.GoogleLogin()); 32 | } 33 | 34 | onFacebookLogin(): void { 35 | this.store.dispatch(new AuthActions.FacebookLogin()); 36 | } 37 | 38 | onSignUpWithCredentials(credentials: Credentials): void { 39 | this.store.dispatch(new AuthActions.CredentialsRegistration(credentials)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/app/toolbar/components/toolbar/toolbar.component.css: -------------------------------------------------------------------------------- 1 | .flexed{ 2 | flex: 1 1 auto; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/toolbar/components/toolbar/toolbar.component.html: -------------------------------------------------------------------------------- 1 | 2 | {{auth.user.email}} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/app/toolbar/components/toolbar/toolbar.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; 2 | import {UserState} from '../../../../../projects/xtream/firebase-ngrx-user-management/src/public_api'; 3 | 4 | @Component({ 5 | selector: 'app-toolbar', 6 | templateUrl: './toolbar.component.html', 7 | styleUrls: ['./toolbar.component.css'] 8 | }) 9 | export class ToolbarComponent implements OnInit { 10 | @Input() auth: UserState; 11 | @Output() logout = new EventEmitter(); 12 | 13 | constructor() { 14 | } 15 | 16 | ngOnInit() { 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/app/toolbar/containers/toolbar-container/toolbar-container.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtreamsrl/firebase-ngrx-user-management/4779e365fcf9d27bbe2df6e7cf1c28d3a2b7177b/src/app/toolbar/containers/toolbar-container/toolbar-container.component.css -------------------------------------------------------------------------------- /src/app/toolbar/containers/toolbar-container/toolbar-container.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/app/toolbar/containers/toolbar-container/toolbar-container.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit} from '@angular/core'; 2 | import {select, Store} from '@ngrx/store'; 3 | import {Observable} from 'rxjs'; 4 | import {getUserState, UserState} from '../../../../../projects/xtream/firebase-ngrx-user-management/src/public_api'; 5 | import {State} from '../../../core/reducers/index'; 6 | import {AuthActions} from '@xtream/firebase-ngrx-user-management'; 7 | import {Router} from '@angular/router'; 8 | 9 | @Component({ 10 | selector: 'app-toolbar-container', 11 | templateUrl: './toolbar-container.component.html', 12 | styleUrls: ['./toolbar-container.component.css'] 13 | }) 14 | export class ToolbarContainerComponent implements OnInit { 15 | auth$: Observable; 16 | 17 | constructor(private store: Store, private router: Router) { 18 | 19 | } 20 | 21 | ngOnInit(): void { 22 | this.auth$ = this.store.pipe(select(getUserState)); 23 | } 24 | 25 | onLogout(): void { 26 | this.store.dispatch(new AuthActions.Logout()); 27 | this.router.navigate(['/login']); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/app/user/components/user/user.component.css: -------------------------------------------------------------------------------- 1 | .profile-card { 2 | margin: auto; 3 | max-width: 400px; 4 | } 5 | 6 | -------------------------------------------------------------------------------- /src/app/user/components/user/user.component.html: -------------------------------------------------------------------------------- 1 | 2 | my profile 3 | mail: {{auth?.user?.email}} 4 | name: {{auth?.user?.displayName}} 5 | e-mail verified: {{auth?.user?.emailVerified}} 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/app/user/components/user/user.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; 2 | import {UserState} from '../../../../../projects/xtream/firebase-ngrx-user-management/src/public_api'; 3 | 4 | @Component({ 5 | selector: 'app-user', 6 | templateUrl: './user.component.html', 7 | styleUrls: ['./user.component.css'] 8 | }) 9 | export class UserComponent implements OnInit { 10 | @Input() auth: UserState; 11 | @Output() sendMail = new EventEmitter(); 12 | @Output() logout = new EventEmitter(); 13 | @Output() deleteAccount = new EventEmitter(); 14 | 15 | constructor() { 16 | } 17 | 18 | ngOnInit(): void { 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/app/user/containers/user-container/user-container.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtreamsrl/firebase-ngrx-user-management/4779e365fcf9d27bbe2df6e7cf1c28d3a2b7177b/src/app/user/containers/user-container/user-container.component.css -------------------------------------------------------------------------------- /src/app/user/containers/user-container/user-container.component.html: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /src/app/user/containers/user-container/user-container.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit} from '@angular/core'; 2 | import {Observable} from 'rxjs'; 3 | import {getUserState, UserState} from '@xtream/firebase-ngrx-user-management'; 4 | import {select, Store} from '@ngrx/store'; 5 | import {State} from '../../../../../projects/xtream/firebase-ngrx-user-management/src/lib/reducers/user.reducer'; 6 | import {AuthActions} from '@xtream/firebase-ngrx-user-management'; 7 | import {Actions} from '@ngrx/effects'; 8 | import {Router} from '@angular/router'; 9 | import {environment} from '../../../../environments/environment'; 10 | 11 | @Component({ 12 | selector: 'app-user-container', 13 | templateUrl: './user-container.component.html', 14 | styleUrls: ['./user-container.component.css'] 15 | }) 16 | export class UserContainerComponent implements OnInit { 17 | auth$: Observable; 18 | 19 | constructor(private store: Store, private router: Router) { 20 | } 21 | 22 | ngOnInit(): void { 23 | this.auth$ = this.store.pipe(select(getUserState)); 24 | } 25 | 26 | sendVerificationEmail(): void { 27 | this.store.dispatch(new AuthActions.SendVerificationEmail({redirectUrl: environment.emailVerificationRedirectUrl})); 28 | } 29 | 30 | onLogout(): void { 31 | this.store.dispatch(new AuthActions.Logout()); 32 | this.router.navigate(['/login']); 33 | } 34 | 35 | onDeleteAccount(): void { 36 | this.store.dispatch(new AuthActions.DeleteAccount()); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtreamsrl/firebase-ngrx-user-management/4779e365fcf9d27bbe2df6e7cf1c28d3a2b7177b/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # 5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed 6 | 7 | > 0.5% 8 | last 2 versions 9 | Firefox ESR 10 | not dead 11 | not IE 9-11 -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false, 7 | firebase: { 8 | apiKey: 'AIzaSyDpoVHftqTj6i74Fl19l5BbNgI5scisfQU', 9 | authDomain: 'fir-ngrx-sample.firebaseapp.com', 10 | databaseURL: 'https://fir-ngrx-sample.firebaseio.com', 11 | projectId: 'fir-ngrx-sample', 12 | storageBucket: 'fir-ngrx-sample.appspot.com', 13 | messagingSenderId: '121802774076' 14 | }, 15 | emailVerificationRedirectUrl: 'localhost:4200/login' 16 | }; 17 | 18 | /* 19 | * For easier debugging in development mode, you can import the following file 20 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 21 | * 22 | * This import should be commented out in production mode because it will have a negative impact 23 | * on performance if an error is thrown. 24 | */ 25 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 26 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xtreamsrl/firebase-ngrx-user-management/4779e365fcf9d27bbe2df6e7cf1c28d3a2b7177b/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | FirebaseNgrxUserManagementExample 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | 14 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 38 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 39 | 40 | /** IE10 and IE11 requires the following for the Reflect API. */ 41 | // import 'core-js/es6/reflect'; 42 | 43 | 44 | /** Evergreen browsers require these. **/ 45 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. 46 | import 'core-js/features/reflect'; 47 | 48 | 49 | /** 50 | * Web Animations `@angular/platform-browser/animations` 51 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 52 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 53 | **/ 54 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 55 | 56 | /** 57 | * By default, zone.js will patch all possible macroTask and DomEvents 58 | * user can disable parts of macroTask/DomEvents patch by setting following flags 59 | */ 60 | 61 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 62 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 63 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 64 | 65 | /* 66 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 67 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 68 | */ 69 | // (window as any).__Zone_enable_cross_context_check = true; 70 | 71 | /*************************************************************************************************** 72 | * Zone JS is required by default for Angular itself. 73 | */ 74 | import 'zone.js/dist/zone'; // Included with Angular CLI. 75 | 76 | 77 | 78 | /*************************************************************************************************** 79 | * APPLICATION IMPORTS 80 | */ 81 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | @import "~@angular/material/prebuilt-themes/indigo-pink.css"; 2 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "types": [] 6 | }, 7 | "files": [ 8 | "main.ts", 9 | "polyfills.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "test.ts", 12 | "polyfills.ts" 13 | ], 14 | "include": [ 15 | "**/*.spec.ts", 16 | "**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /src/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "target": "es5", 13 | "typeRoots": [ 14 | "node_modules/@types" 15 | ], 16 | "lib": [ 17 | "es2017", 18 | "dom" 19 | ], 20 | "paths": { 21 | "@xtream/firebase-ngrx-user-management": [ 22 | "dist/xtream/firebase-ngrx-user-management" 23 | ], 24 | "@xtream/firebase-ngrx-user-management/*": [ 25 | "dist/xtream/firebase-ngrx-user-management/*" 26 | ] 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "arrow-return-shorthand": true, 7 | "callable-types": true, 8 | "class-name": true, 9 | "comment-format": [ 10 | true, 11 | "check-space" 12 | ], 13 | "curly": true, 14 | "deprecation": { 15 | "severity": "warn" 16 | }, 17 | "eofline": true, 18 | "forin": true, 19 | "import-blacklist": [ 20 | true, 21 | "rxjs/Rx" 22 | ], 23 | "import-spacing": true, 24 | "indent": [ 25 | true, 26 | "spaces" 27 | ], 28 | "interface-over-type-literal": true, 29 | "label-position": true, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-arg": true, 47 | "no-bitwise": true, 48 | "no-console": [ 49 | true, 50 | "debug", 51 | "info", 52 | "time", 53 | "timeEnd", 54 | "trace" 55 | ], 56 | "no-construct": true, 57 | "no-debugger": true, 58 | "no-duplicate-super": true, 59 | "no-empty": false, 60 | "no-empty-interface": true, 61 | "no-eval": true, 62 | "no-inferrable-types": [ 63 | true, 64 | "ignore-params" 65 | ], 66 | "no-misused-new": true, 67 | "no-non-null-assertion": true, 68 | "no-redundant-jsdoc": true, 69 | "no-shadowed-variable": true, 70 | "no-string-literal": false, 71 | "no-string-throw": true, 72 | "no-switch-case-fall-through": true, 73 | "no-trailing-whitespace": true, 74 | "no-unnecessary-initializer": true, 75 | "no-unused-expression": true, 76 | "no-use-before-declare": true, 77 | "no-var-keyword": true, 78 | "object-literal-sort-keys": false, 79 | "one-line": [ 80 | true, 81 | "check-open-brace", 82 | "check-catch", 83 | "check-else", 84 | "check-whitespace" 85 | ], 86 | "prefer-const": true, 87 | "quotemark": [ 88 | true, 89 | "single" 90 | ], 91 | "radix": true, 92 | "semicolon": [ 93 | true, 94 | "always" 95 | ], 96 | "triple-equals": [ 97 | true, 98 | "allow-null-check" 99 | ], 100 | "typedef-whitespace": [ 101 | true, 102 | { 103 | "call-signature": "nospace", 104 | "index-signature": "nospace", 105 | "parameter": "nospace", 106 | "property-declaration": "nospace", 107 | "variable-declaration": "nospace" 108 | } 109 | ], 110 | "unified-signatures": true, 111 | "variable-name": false, 112 | "whitespace": [ 113 | true, 114 | "check-branch", 115 | "check-decl", 116 | "check-operator", 117 | "check-separator", 118 | "check-type" 119 | ], 120 | "no-output-on-prefix": true, 121 | "use-input-property-decorator": true, 122 | "use-output-property-decorator": true, 123 | "use-host-property-decorator": true, 124 | "no-input-rename": true, 125 | "no-output-rename": true, 126 | "use-life-cycle-interface": true, 127 | "use-pipe-transform-interface": true, 128 | "component-class-suffix": true, 129 | "directive-class-suffix": true 130 | } 131 | } 132 | --------------------------------------------------------------------------------