├── .gitignore ├── .npmignore ├── build ├── accounts-module.d.ts ├── accounts-module.js ├── annotations.d.ts ├── annotations.js ├── index.d.ts ├── index.js ├── login-buttons.d.ts └── login-buttons.js ├── demo ├── .gitignore ├── .meteor │ ├── .finished-upgraders │ ├── .gitignore │ ├── .id │ ├── packages │ ├── platforms │ ├── release │ └── versions ├── client │ ├── imports │ │ ├── app.module.ts │ │ ├── app │ │ │ ├── app.component.html │ │ │ ├── app.component.scss │ │ │ ├── app.component.ts │ │ │ └── app.routes.ts │ │ ├── guarded │ │ │ ├── guarded.component.html │ │ │ └── guarded.component.ts │ │ └── main │ │ │ └── main.component.ts │ ├── index.html │ ├── main.ts │ └── styles │ │ └── main.scss ├── package.json ├── tsconfig.json └── typings.d.ts ├── package.json ├── src ├── accounts-module.ts ├── annotations.ts ├── index.ts └── login-buttons.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /*.ts 3 | /*.html 4 | /*.css 5 | /*.less 6 | !webpack.config.js 7 | npm-debug.log 8 | typings 9 | ### JetBrains template 10 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 11 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 12 | 13 | # User-specific stuff: 14 | .idea/workspace.xml 15 | .idea/tasks.xml 16 | .idea/dictionaries 17 | .idea/vcs.xml 18 | .idea/jsLibraryMappings.xml 19 | 20 | # Sensitive or high-churn files: 21 | .idea/dataSources.ids 22 | .idea/dataSources.xml 23 | .idea/dataSources.local.xml 24 | .idea/sqlDataSources.xml 25 | .idea/dynamic.xml 26 | .idea/uiDesigner.xml 27 | 28 | # Gradle: 29 | .idea/gradle.xml 30 | .idea/libraries 31 | 32 | # Mongo Explorer plugin: 33 | .idea/mongoSettings.xml 34 | 35 | ## File-based project format: 36 | *.iws 37 | 38 | ## Plugin-specific files: 39 | 40 | # IntelliJ 41 | /out/ 42 | 43 | # mpeltonen/sbt-idea plugin 44 | .idea_modules/ 45 | 46 | # JIRA plugin 47 | atlassian-ide-plugin.xml 48 | 49 | # Crashlytics plugin (for Android Studio and IntelliJ) 50 | com_crashlytics_export_strings.xml 51 | crashlytics.properties 52 | crashlytics-build.properties 53 | fabric.properties 54 | 55 | .idea/ -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | tests 2 | node_modules 3 | npm-debug* 4 | demo -------------------------------------------------------------------------------- /build/accounts-module.d.ts: -------------------------------------------------------------------------------- 1 | export declare class AccountsModule { 2 | } 3 | -------------------------------------------------------------------------------- /build/accounts-module.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var core_1 = require('@angular/core'); 3 | var common_1 = require('@angular/common'); 4 | var forms_1 = require('@angular/forms'); 5 | var login_buttons_1 = require('./login-buttons'); 6 | var annotations_1 = require("./annotations"); 7 | var AccountsModule = (function () { 8 | function AccountsModule() { 9 | } 10 | AccountsModule = __decorate([ 11 | core_1.NgModule({ 12 | imports: [ 13 | common_1.CommonModule, 14 | forms_1.FormsModule 15 | ], 16 | declarations: [ 17 | login_buttons_1.LoginButtons 18 | ], 19 | providers: [ 20 | annotations_1.AuthGuard 21 | ], 22 | exports: [ 23 | login_buttons_1.LoginButtons 24 | ] 25 | }), 26 | __metadata('design:paramtypes', []) 27 | ], AccountsModule); 28 | return AccountsModule; 29 | }()); 30 | exports.AccountsModule = AccountsModule; 31 | -------------------------------------------------------------------------------- /build/annotations.d.ts: -------------------------------------------------------------------------------- 1 | import { CanActivate } from '@angular/router'; 2 | import { Observable } from "rxjs"; 3 | export declare function InjectUser(propName?: string): (cls: any) => any; 4 | /** 5 | * A service to use as auth guard on the route. 6 | * 7 | */ 8 | export declare class AuthGuard implements CanActivate { 9 | canActivate(): Observable; 10 | } 11 | -------------------------------------------------------------------------------- /build/annotations.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var meteor_1 = require('meteor/meteor'); 3 | var rxjs_1 = require("rxjs"); 4 | var tracker_1 = require("meteor/tracker"); 5 | var InjectUserAnnotation = (function () { 6 | function InjectUserAnnotation(propName) { 7 | if (propName === void 0) { propName = 'user'; } 8 | this.propName = propName; 9 | } 10 | return InjectUserAnnotation; 11 | }()); 12 | function InjectUser(propName) { 13 | var annInstance = new InjectUserAnnotation(propName); 14 | var TypeDecorator = function TypeDecorator(cls) { 15 | var propName = annInstance.propName; 16 | var fieldName = "_" + propName; 17 | var injected = fieldName + "Injected"; 18 | Object.defineProperty(cls.prototype, propName, { 19 | get: function () { 20 | var _this = this; 21 | if (!this[injected]) { 22 | this[fieldName] = meteor_1.Meteor.user(); 23 | // If uses MeteorReactive / MeteorComponent 24 | if (this.autorun) { 25 | this.autorun(function () { 26 | _this[fieldName] = meteor_1.Meteor.user(); 27 | }, true); 28 | } 29 | else { 30 | var zone_1 = Zone.current; 31 | tracker_1.Tracker.autorun(function () { 32 | zone_1.run(function () { 33 | _this[fieldName] = meteor_1.Meteor.user(); 34 | }); 35 | }); 36 | } 37 | this[injected] = true; 38 | } 39 | return this[fieldName]; 40 | }, 41 | enumerable: true, 42 | configurable: false 43 | }); 44 | return cls; 45 | }; 46 | return TypeDecorator; 47 | } 48 | exports.InjectUser = InjectUser; 49 | /** 50 | * A service to use as auth guard on the route. 51 | * 52 | */ 53 | var AuthGuard = (function () { 54 | function AuthGuard() { 55 | } 56 | AuthGuard.prototype.canActivate = function () { 57 | return rxjs_1.Observable.create(function (observer) { 58 | tracker_1.Tracker.autorun(function (c) { 59 | if (!meteor_1.Meteor.loggingIn()) { 60 | observer.next(!!meteor_1.Meteor.user()); 61 | observer.complete(); 62 | c.stop(); 63 | } 64 | }); 65 | }); 66 | }; 67 | return AuthGuard; 68 | }()); 69 | exports.AuthGuard = AuthGuard; 70 | -------------------------------------------------------------------------------- /build/index.d.ts: -------------------------------------------------------------------------------- 1 | export * from './login-buttons'; 2 | export * from './annotations'; 3 | export * from './accounts-module'; 4 | -------------------------------------------------------------------------------- /build/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | function __export(m) { 3 | for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; 4 | } 5 | __export(require('./login-buttons')); 6 | __export(require('./annotations')); 7 | __export(require('./accounts-module')); 8 | -------------------------------------------------------------------------------- /build/login-buttons.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { NgZone } from '@angular/core'; 3 | import { Tracker } from 'meteor/tracker'; 4 | import { Meteor } from 'meteor/meteor'; 5 | export interface LoginCredentials { 6 | email: string; 7 | password: string; 8 | } 9 | export declare class LoginButtons { 10 | private zone; 11 | autorunComputation: Tracker.Computation; 12 | currentUser: Meteor.User; 13 | currentUserId: string; 14 | isLoggingIn: boolean; 15 | isLoggedIn: boolean; 16 | services: Array; 17 | credentials: LoginCredentials; 18 | errors: Array; 19 | isPasswordRecovery: boolean; 20 | isSignup: boolean; 21 | isDropdownOpen: boolean; 22 | message: string; 23 | constructor(zone: NgZone); 24 | _resetCredentialsFields(): void; 25 | resetErrors(): void; 26 | singleService(): Object; 27 | displayName(): string; 28 | login(): void; 29 | recover(): void; 30 | logout(): void; 31 | signup(): void; 32 | _hasPasswordService(): boolean; 33 | _getLoginServices(): Array; 34 | dropdown(): boolean; 35 | _initAutorun(): void; 36 | } 37 | -------------------------------------------------------------------------------- /build/login-buttons.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var core_1 = require('@angular/core'); 3 | var accounts_base_1 = require('meteor/accounts-base'); 4 | var tracker_1 = require('meteor/tracker'); 5 | var meteor_1 = require('meteor/meteor'); 6 | var LoginButtons = (function () { 7 | function LoginButtons(zone) { 8 | this.zone = zone; 9 | this._initAutorun(); 10 | this.services = this._getLoginServices(); 11 | this.resetErrors(); 12 | this.isPasswordRecovery = false; 13 | this.isSignup = false; 14 | this.isDropdownOpen = false; 15 | this._resetCredentialsFields(); 16 | } 17 | LoginButtons.prototype._resetCredentialsFields = function () { 18 | this.credentials = { email: '', password: '' }; 19 | }; 20 | LoginButtons.prototype.resetErrors = function () { 21 | this.errors = []; 22 | this.message = ""; 23 | }; 24 | LoginButtons.prototype.singleService = function () { 25 | var services = this._getLoginServices(); 26 | return services[0]; 27 | }; 28 | LoginButtons.prototype.displayName = function () { 29 | var user = this.currentUser; 30 | if (!user) 31 | return ''; 32 | if (user.profile && user.profile.name) 33 | return user.profile.name; 34 | if (user.username) 35 | return user.username; 36 | if (user.emails && user.emails[0] && user.emails[0].address) 37 | return user.emails[0].address; 38 | return ''; 39 | }; 40 | ; 41 | LoginButtons.prototype.login = function () { 42 | var _this = this; 43 | this.resetErrors(); 44 | var email = this.credentials.email; 45 | var password = this.credentials.password; 46 | meteor_1.Meteor.loginWithPassword(email, password, function (error) { 47 | if (error) { 48 | _this.errors.push(error.reason || "Unknown error"); 49 | } 50 | else { 51 | _this.isDropdownOpen = false; 52 | _this._resetCredentialsFields(); 53 | } 54 | }); 55 | }; 56 | LoginButtons.prototype.recover = function () { 57 | var _this = this; 58 | this.resetErrors(); 59 | accounts_base_1.Accounts.forgotPassword({ email: this.credentials.email }, function (error) { 60 | if (error) { 61 | _this.errors.push(error.reason || "Unknown error"); 62 | } 63 | else { 64 | _this.message = "You will receive further instruction to you email address!"; 65 | _this.isDropdownOpen = false; 66 | _this._resetCredentialsFields(); 67 | } 68 | }); 69 | }; 70 | LoginButtons.prototype.logout = function () { 71 | meteor_1.Meteor.logout(); 72 | this.isDropdownOpen = false; 73 | }; 74 | LoginButtons.prototype.signup = function () { 75 | var _this = this; 76 | this.resetErrors(); 77 | accounts_base_1.Accounts.createUser(this.credentials, function (error) { 78 | if (error) { 79 | _this.errors.push(error.reason || "Unknown error"); 80 | } 81 | else { 82 | _this.isDropdownOpen = false; 83 | _this._resetCredentialsFields(); 84 | } 85 | }); 86 | }; 87 | LoginButtons.prototype._hasPasswordService = function () { 88 | return !!Package['accounts-password']; 89 | }; 90 | LoginButtons.prototype._getLoginServices = function () { 91 | var services = Package['accounts-oauth'] ? accounts_base_1.Accounts.oauth.serviceNames() : []; 92 | services.sort(); 93 | if (this._hasPasswordService()) 94 | services.push('password'); 95 | return _.map(services, function (name) { 96 | return { name: name }; 97 | }); 98 | }; 99 | LoginButtons.prototype.dropdown = function () { 100 | return this._hasPasswordService() || this._getLoginServices().length > 1; 101 | }; 102 | LoginButtons.prototype._initAutorun = function () { 103 | var _this = this; 104 | this.autorunComputation = tracker_1.Tracker.autorun(function () { 105 | _this.zone.run(function () { 106 | _this.currentUser = meteor_1.Meteor.user(); 107 | _this.currentUserId = meteor_1.Meteor.userId(); 108 | _this.isLoggingIn = meteor_1.Meteor.loggingIn(); 109 | _this.isLoggedIn = !!meteor_1.Meteor.user(); 110 | }); 111 | }); 112 | }; 113 | LoginButtons = __decorate([ 114 | core_1.Component({ 115 | selector: 'login-buttons', 116 | styles: [ 117 | "\n .login-buttons {\n position: relative;\n display: inline-block;\n }\n .login-buttons .dropdown-toggle span {\n cursor: pointer;\n }\n .login-buttons .accounts-close {\n position: absolute;\n top: 0;\n right: 5px;\n cursor: pointer;\n font-weight: bold;\n line-height: 20px;\n text-decoration: underline;\n opacity: 0.8;\n }\n .login-buttons .accounts-close:hover {\n opacity: 1;\n }\n .login-buttons .content-container {\n position: absolute;\n top: 0;\n left: 0;\n border: 1px solid #ccc;\n z-index: 1000;\n background: white;\n border-radius: 4px;\n padding: 8px 12px;\n margin: -8px -12px 0 -12px;\n width: 250px;\n box-shadow: 0 0 3px 0 rgba(0, 0, 0, 0.2);\n -webkit-box-shadow: 0 0 3px 0 rgba(0, 0, 0, 0.2);\n font-size: 16px;\n color: #333;\n }\n .login-buttons .content-container > * {\n line-height: 1.6;\n }\n .login-buttons .content-container > .login-close-text {\n line-height: inherit;\n font-size: inherit;\n font-family: inherit;\n }\n .login-buttons .content-container label,\n .login-buttons .content-container .title {\n font-size: 80%;\n margin-top: 7px;\n margin-bottom: -2px;\n }\n .login-buttons .content-container label {\n display: inline;\n }\n .login-buttons .content-container input[type=text],\n .login-buttons .content-container input[type=email],\n .login-buttons .content-container input[type=password] {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n width: 100%;\n }\n .login-buttons .content-container input[type=text][type],\n .login-buttons .content-container input[type=email][type],\n .login-buttons .content-container input[type=password][type] {\n height: auto;\n }\n .login-buttons .loading {\n line-height: 1;\n background-image: url(data:image/gif;base64,R0lGODlhEAALAPQAAP///wAAANra2tDQ0Orq6gYGBgAAAC4uLoKCgmBgYLq6uiIiIkpKSoqKimRkZL6+viYmJgQEBE5OTubm5tjY2PT09Dg4ONzc3PLy8ra2tqCgoMrKyu7u7gAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCwAAACwAAAAAEAALAAAFLSAgjmRpnqSgCuLKAq5AEIM4zDVw03ve27ifDgfkEYe04kDIDC5zrtYKRa2WQgAh+QQJCwAAACwAAAAAEAALAAAFJGBhGAVgnqhpHIeRvsDawqns0qeN5+y967tYLyicBYE7EYkYAgAh+QQJCwAAACwAAAAAEAALAAAFNiAgjothLOOIJAkiGgxjpGKiKMkbz7SN6zIawJcDwIK9W/HISxGBzdHTuBNOmcJVCyoUlk7CEAAh+QQJCwAAACwAAAAAEAALAAAFNSAgjqQIRRFUAo3jNGIkSdHqPI8Tz3V55zuaDacDyIQ+YrBH+hWPzJFzOQQaeavWi7oqnVIhACH5BAkLAAAALAAAAAAQAAsAAAUyICCOZGme1rJY5kRRk7hI0mJSVUXJtF3iOl7tltsBZsNfUegjAY3I5sgFY55KqdX1GgIAIfkECQsAAAAsAAAAABAACwAABTcgII5kaZ4kcV2EqLJipmnZhWGXaOOitm2aXQ4g7P2Ct2ER4AMul00kj5g0Al8tADY2y6C+4FIIACH5BAkLAAAALAAAAAAQAAsAAAUvICCOZGme5ERRk6iy7qpyHCVStA3gNa/7txxwlwv2isSacYUc+l4tADQGQ1mvpBAAIfkECQsAAAAsAAAAABAACwAABS8gII5kaZ7kRFGTqLLuqnIcJVK0DeA1r/u3HHCXC/aKxJpxhRz6Xi0ANAZDWa+kEAA7AAAAAAAAAAAA);\n width: 16px;\n background-position: center center;\n background-repeat: no-repeat;\n }"], 118 | template: "\n
\n
\n \n {{ displayName() }} \u25BE\n \n \n Login \u25BE\n \n
\n
\n
Close
\n
\n
\n
\n {{ displayName() }}\n
\n Sign Out\n
\n
\n
\n Please wait...\n \n
\n
\n
" 119 | }), 120 | __metadata('design:paramtypes', [core_1.NgZone]) 121 | ], LoginButtons); 122 | return LoginButtons; 123 | }()); 124 | exports.LoginButtons = LoginButtons; 125 | -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .meteor/local 3 | npm-debug.log 4 | typings 5 | .idea 6 | .meteor/local 7 | -------------------------------------------------------------------------------- /demo/.meteor/.finished-upgraders: -------------------------------------------------------------------------------- 1 | # This file contains information which helps Meteor properly upgrade your 2 | # app when you run 'meteor update'. You should check it into version control 3 | # with your project. 4 | 5 | notices-for-0.9.0 6 | notices-for-0.9.1 7 | 0.9.4-platform-file 8 | notices-for-facebook-graph-api-2 9 | 1.2.0-standard-minifiers-package 10 | 1.2.0-meteor-platform-split 11 | 1.2.0-cordova-changes 12 | 1.2.0-breaking-changes 13 | 1.3.0-split-minifiers-package 14 | 1.3.5-remove-old-dev-bundle-link 15 | 1.4.0-remove-old-dev-bundle-link 16 | 1.4.1-add-shell-server-package 17 | -------------------------------------------------------------------------------- /demo/.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | dev_bundle 2 | local 3 | -------------------------------------------------------------------------------- /demo/.meteor/.id: -------------------------------------------------------------------------------- 1 | # This file contains a token that is unique to your project. 2 | # Check it into your repository along with the rest of this directory. 3 | # It can be used for purposes such as: 4 | # - ensuring you don't accidentally deploy one app on top of another 5 | # - providing package authors with aggregated statistics 6 | 7 | a4kqps1d5y31214lew0l 8 | -------------------------------------------------------------------------------- /demo/.meteor/packages: -------------------------------------------------------------------------------- 1 | # Meteor packages used by this project, one per line. 2 | # Check this file (and the other files in this directory) into your repository. 3 | # 4 | # 'meteor add' and 'meteor remove' will edit this file for you, 5 | # but you can also edit it by hand. 6 | 7 | meteor-base@1.0.4 # Packages every Meteor app needs to have 8 | mobile-experience@1.0.4 # Packages for a great mobile UX 9 | mongo@1.1.14 # The database Meteor supports right now 10 | reactive-var@1.0.11 # Reactive variable for tracker 11 | tracker@1.1.1 # Meteor's client-side reactive programming library 12 | 13 | standard-minifier-css@1.3.2 # CSS minifier run for production mode 14 | standard-minifier-js@1.2.1 # JS minifier run for production mode 15 | es5-shim@4.6.15 # ECMAScript 5 compatibility for older browsers. 16 | 17 | autopublish@1.0.7 # Publish all data to the clients (for prototyping) 18 | insecure@1.0.7 # Allow all DB writes from clients (for prototyping) 19 | angular2-compilers 20 | practicalmeteor:mocha 21 | xolvio:cleaner 22 | hwillson:stub-collections 23 | dispatch:mocha-phantomjs 24 | shell-server@0.2.1 25 | accounts-base 26 | accounts-password 27 | -------------------------------------------------------------------------------- /demo/.meteor/platforms: -------------------------------------------------------------------------------- 1 | server 2 | browser 3 | -------------------------------------------------------------------------------- /demo/.meteor/release: -------------------------------------------------------------------------------- 1 | METEOR@1.4.2 2 | -------------------------------------------------------------------------------- /demo/.meteor/versions: -------------------------------------------------------------------------------- 1 | accounts-base@1.2.14 2 | accounts-password@1.3.1 3 | allow-deny@1.0.5 4 | angular2-compilers@0.6.4 5 | autopublish@1.0.7 6 | autoupdate@1.3.12 7 | babel-compiler@6.13.0 8 | babel-runtime@0.1.12 9 | barbatus:css-compiler@0.3.3 10 | barbatus:scss-compiler@3.8.2 11 | barbatus:typescript@0.5.2 12 | barbatus:typescript-compiler@0.8.4 13 | barbatus:typescript-runtime@0.1.2 14 | base64@1.0.10 15 | binary-heap@1.0.10 16 | blaze@2.1.9 17 | blaze-tools@1.0.10 18 | boilerplate-generator@1.0.11 19 | caching-compiler@1.1.8 20 | caching-html-compiler@1.0.7 21 | callback-hook@1.0.10 22 | check@1.2.4 23 | coffeescript@1.11.1_2 24 | ddp@1.2.5 25 | ddp-client@1.3.2 26 | ddp-common@1.2.7 27 | ddp-rate-limiter@1.0.6 28 | ddp-server@1.3.11 29 | deps@1.0.12 30 | diff-sequence@1.0.7 31 | dispatch:mocha-phantomjs@0.1.7 32 | dispatch:phantomjs-tests@0.0.5 33 | ecmascript@0.5.9 34 | ecmascript-runtime@0.3.15 35 | ejson@1.0.13 36 | email@1.1.18 37 | es5-shim@4.6.15 38 | fastclick@1.0.13 39 | geojson-utils@1.0.10 40 | hot-code-push@1.0.4 41 | html-tools@1.0.11 42 | htmljs@1.0.11 43 | http@1.2.10 44 | hwillson:stub-collections@1.0.2 45 | id-map@1.0.9 46 | insecure@1.0.7 47 | jquery@1.11.10 48 | launch-screen@1.1.0 49 | livedata@1.0.18 50 | localstorage@1.0.12 51 | logging@1.1.16 52 | meteor@1.6.0 53 | meteor-base@1.0.4 54 | minifier-css@1.2.15 55 | minifier-js@1.2.15 56 | minimongo@1.0.18 57 | mobile-experience@1.0.4 58 | mobile-status-bar@1.0.13 59 | modules@0.7.7 60 | modules-runtime@0.7.7 61 | mongo@1.1.14 62 | mongo-id@1.0.6 63 | npm-bcrypt@0.9.2 64 | npm-mongo@2.2.11_2 65 | observe-sequence@1.0.14 66 | ordered-dict@1.0.9 67 | practicalmeteor:chai@2.1.0_1 68 | practicalmeteor:loglevel@1.2.0_2 69 | practicalmeteor:mocha@2.4.5_6 70 | practicalmeteor:mocha-core@1.0.1 71 | practicalmeteor:sinon@1.14.1_2 72 | promise@0.8.8 73 | random@1.0.10 74 | rate-limit@1.0.6 75 | reactive-var@1.0.11 76 | reload@1.1.11 77 | retry@1.0.9 78 | routepolicy@1.0.12 79 | service-configuration@1.0.11 80 | sha@1.0.9 81 | shell-server@0.2.1 82 | spacebars@1.0.13 83 | spacebars-compiler@1.0.13 84 | srp@1.0.10 85 | standard-minifier-css@1.3.2 86 | standard-minifier-js@1.2.1 87 | templating@1.2.15 88 | templating-compiler@1.2.15 89 | templating-runtime@1.2.15 90 | templating-tools@1.0.5 91 | tmeasday:test-reporter-helpers@0.2.1 92 | tracker@1.1.1 93 | ui@1.0.12 94 | underscore@1.0.10 95 | urigo:static-html-compiler@0.1.8 96 | url@1.0.11 97 | webapp@1.3.12 98 | webapp-hashing@1.0.9 99 | xolvio:cleaner@0.3.1 100 | -------------------------------------------------------------------------------- /demo/client/imports/app.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {BrowserModule} from '@angular/platform-browser'; 3 | import {AccountsModule} from "angular2-meteor-accounts-ui"; 4 | import {RouterModule} from "@angular/router"; 5 | import {GuardedComponent} from "./guarded/guarded.component"; 6 | import {AppComponent} from "./app/app.component"; 7 | import {APP_ROUTES} from "./app/app.routes"; 8 | import {MainComponent} from "./main/main.component"; 9 | 10 | 11 | @NgModule({ 12 | // Components, Pipes, Directive 13 | declarations: [ 14 | AppComponent, 15 | GuardedComponent, 16 | MainComponent 17 | ], 18 | // Entry Components 19 | entryComponents: [ 20 | AppComponent, 21 | GuardedComponent, 22 | MainComponent 23 | ], 24 | // Providers 25 | providers: [], 26 | // Modules 27 | imports: [ 28 | BrowserModule, 29 | AccountsModule, 30 | RouterModule.forRoot(APP_ROUTES) 31 | ], 32 | // Main Component 33 | bootstrap: [MainComponent] 34 | }) 35 | export class AppModule { 36 | } 37 | -------------------------------------------------------------------------------- /demo/client/imports/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 |

Angular2-Meteor-accounts-ui Demo

3 |
4 |

login-buttons example:

5 | 6 |
7 |
8 |

Current login status (Using @InjectUser)

9 | Not logged in 10 | Email: {{ user.emails[0].address }} 11 |
12 |
13 |

Protected route example

14 | /guarded - you can access only if logged in 15 |
16 |
17 | -------------------------------------------------------------------------------- /demo/client/imports/app/app.component.scss: -------------------------------------------------------------------------------- 1 | app { 2 | 3 | } -------------------------------------------------------------------------------- /demo/client/imports/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | import template from './app.component.html'; 4 | import style from './app.component.scss'; 5 | import {InjectUser} from "angular2-meteor-accounts-ui"; 6 | 7 | @Component({ 8 | selector: 'app', 9 | template, 10 | styles: [ style ] 11 | }) 12 | @InjectUser("user") 13 | export class AppComponent { 14 | private user: Meteor.User; 15 | 16 | constructor() { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /demo/client/imports/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import {AppComponent} from "./app.component"; 2 | import {GuardedComponent} from "../guarded/guarded.component"; 3 | import {AuthGuard} from "angular2-meteor-accounts-ui"; 4 | 5 | export const APP_ROUTES = [ 6 | { path: '', component: AppComponent}, 7 | { path: 'guarded', component: GuardedComponent, canActivate: [AuthGuard],}, 8 | ]; -------------------------------------------------------------------------------- /demo/client/imports/guarded/guarded.component.html: -------------------------------------------------------------------------------- 1 | This is a guarded component! -------------------------------------------------------------------------------- /demo/client/imports/guarded/guarded.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from "@angular/core"; 2 | import template from "./guarded.component.html"; 3 | 4 | @Component({ 5 | selector: "guarded", 6 | template 7 | }) 8 | export class GuardedComponent { 9 | constructor() { 10 | 11 | } 12 | } -------------------------------------------------------------------------------- /demo/client/imports/main/main.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from "@angular/core"; 2 | 3 | @Component({ 4 | selector: "main", 5 | template: "" 6 | }) 7 | export class MainComponent { 8 | constructor() { 9 | 10 | } 11 | } -------------------------------------------------------------------------------- /demo/client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | -------------------------------------------------------------------------------- /demo/client/main.ts: -------------------------------------------------------------------------------- 1 | import 'angular2-meteor-polyfills'; 2 | 3 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 4 | import { enableProdMode } from '@angular/core'; 5 | import { Meteor } from "meteor/meteor"; 6 | import {AppModule} from "./imports/app.module"; 7 | 8 | enableProdMode(); 9 | 10 | Meteor.startup(() => { 11 | platformBrowserDynamic().bootstrapModule(AppModule); 12 | }); 13 | -------------------------------------------------------------------------------- /demo/client/styles/main.scss: -------------------------------------------------------------------------------- 1 | body, html { 2 | 3 | } -------------------------------------------------------------------------------- /demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular2-meteor-accounts-ui-demo", 3 | "private": true, 4 | "scripts": { 5 | "start": "meteor run", 6 | "test": "meteor test --driver-package practicalmeteor:mocha", 7 | "test:ci": "meteor test --once --driver-package dispatch:mocha-phantomjs" 8 | }, 9 | "devDependencies": { 10 | "@types/chai": "^3.4.33", 11 | "@types/mocha": "^2.2.32", 12 | "chai": "3.5.0", 13 | "chai-spies": "0.7.1", 14 | "meteor-typings": "^1.3.1" 15 | }, 16 | "dependencies": { 17 | "@angular/common": "2.1.1", 18 | "@angular/compiler": "2.1.1", 19 | "@angular/core": "2.1.1", 20 | "@angular/forms": "2.1.1", 21 | "@angular/platform-browser": "2.1.1", 22 | "@angular/platform-browser-dynamic": "2.1.1", 23 | "@angular/router": "3.1.1", 24 | "angular2-meteor": "0.7.0", 25 | "angular2-meteor-polyfills": "0.1.1", 26 | "angular2-meteor-tests-polyfills": "0.0.2", 27 | "meteor-node-stubs": "0.2.3", 28 | "meteor-rxjs": "0.4.0", 29 | "reflect-metadata": "0.1.8", 30 | "rxjs": "5.0.0-beta.12", 31 | "zone.js": "0.6.26", 32 | "meteor-typings": "1.3.1", 33 | "angular2-meteor-accounts-ui": "../" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "es6", 6 | "dom" 7 | ], 8 | "module": "commonjs", 9 | "moduleResolution": "node", 10 | "experimentalDecorators": true, 11 | "emitDecoratorMetadata": true, 12 | "sourceMap": true 13 | }, 14 | "exclude": [ 15 | "node_modules" 16 | ], 17 | "files": [ 18 | "typings.d.ts" 19 | ], 20 | "compileOnSave": false, 21 | "angularCompilerOptions": { 22 | "genDir": "aot", 23 | "skipMetadataEmit": true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /demo/typings.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | 5 | declare module '*.html' { 6 | const template: string; 7 | export default template; 8 | } 9 | 10 | declare module '*.scss' { 11 | const style: string; 12 | export default style; 13 | } 14 | 15 | declare module '*.less' { 16 | const style: string; 17 | export default style; 18 | } 19 | 20 | declare module '*.css' { 21 | const style: string; 22 | export default style; 23 | } 24 | 25 | declare module '*.sass' { 26 | const style: string; 27 | export default style; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular2-meteor-accounts-ui", 3 | "version": "1.0.0", 4 | "repository": "https://github.com/Urigo/angular2-meteor-accounts-ui/", 5 | "description": "Angular 2.0 and Meteor - the perfect stack http://www.angular-meteor.com/", 6 | "author": "Angular2-Meteor (https://github.com/Urigo/angular2-meteor)", 7 | "license": "MIT", 8 | "scripts": { 9 | "build": "tsc; exit 0" 10 | }, 11 | "main": "build/index.js", 12 | "typings": "build/index.d.ts", 13 | "peerDependencies": { 14 | "@angular/core": "^2.0.0", 15 | "@angular/common": "^2.0.0" 16 | }, 17 | "devDependencies": { 18 | "@angular/common": "^2.0.0", 19 | "@angular/core": "^2.0.0", 20 | "@angular/forms": "^2.0.0", 21 | "@angular/router": "^3.0.0", 22 | "@types/mocha": "^2.2.32", 23 | "@types/node": "^6.0.39", 24 | "@types/underscore": "^1.7.33", 25 | "@types/zone.js": "0.0.27", 26 | "angular2-meteor-polyfills": "^0.1.1", 27 | "meteor-typings": "^1.3.1", 28 | "reflect-metadata": "^0.1.8", 29 | "rxjs": "5.0.0-beta.12", 30 | "tslint": "^3.6.0", 31 | "typescript": "2.0.0", 32 | "typings": "^1.3.0", 33 | "zone.js": "^0.6.23" 34 | }, 35 | "dependencies": {} 36 | } 37 | -------------------------------------------------------------------------------- /src/accounts-module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {FormsModule} from '@angular/forms'; 4 | import {LoginButtons} from './login-buttons'; 5 | import {AuthGuard} from "./annotations"; 6 | 7 | @NgModule({ 8 | imports: [ 9 | CommonModule, 10 | FormsModule 11 | ], 12 | declarations: [ 13 | LoginButtons 14 | ], 15 | providers: [ 16 | AuthGuard 17 | ], 18 | exports: [ 19 | LoginButtons 20 | ] 21 | }) 22 | export class AccountsModule { 23 | } 24 | -------------------------------------------------------------------------------- /src/annotations.ts: -------------------------------------------------------------------------------- 1 | import {TypeDecorator} from '@angular/core'; 2 | import {CanActivate} from '@angular/router'; 3 | import {Meteor} from 'meteor/meteor'; 4 | import {Observable, Subject, ReplaySubject, Observer} from "rxjs"; 5 | import {Tracker} from "meteor/tracker"; 6 | 7 | class InjectUserAnnotation { 8 | constructor(public propName: string = 'user') { 9 | } 10 | } 11 | 12 | export function InjectUser(propName?: string): (cls: any) => any { 13 | const annInstance = new InjectUserAnnotation(propName); 14 | const TypeDecorator: TypeDecorator = function TypeDecorator(cls) { 15 | const propName = annInstance.propName; 16 | const fieldName = `_${propName}`; 17 | const injected = `${fieldName}Injected`; 18 | 19 | Object.defineProperty(cls.prototype, propName, { 20 | get: function () { 21 | if (!this[injected]) { 22 | this[fieldName] = Meteor.user(); 23 | 24 | // If uses MeteorReactive / MeteorComponent 25 | if (this.autorun) { 26 | this.autorun(() => { 27 | this[fieldName] = Meteor.user(); 28 | }, true); 29 | } 30 | // If uses MeteorReactive or nothing 31 | else { 32 | let zone = Zone.current; 33 | 34 | Tracker.autorun(() => { 35 | zone.run(() => { 36 | this[fieldName] = Meteor.user(); 37 | }); 38 | }); 39 | } 40 | 41 | this[injected] = true; 42 | } 43 | return this[fieldName]; 44 | }, 45 | enumerable: true, 46 | configurable: false 47 | }); 48 | return cls; 49 | }; 50 | return TypeDecorator; 51 | } 52 | 53 | 54 | /** 55 | * A service to use as auth guard on the route. 56 | * 57 | */ 58 | export class AuthGuard implements CanActivate { 59 | canActivate(): Observable { 60 | return Observable.create((observer: Observer) => { 61 | Tracker.autorun((c) => { 62 | if (!Meteor.loggingIn()) { 63 | observer.next(!!Meteor.user()); 64 | observer.complete(); 65 | c.stop(); 66 | } 67 | }); 68 | }); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './login-buttons'; 2 | export * from './annotations'; 3 | export * from './accounts-module'; -------------------------------------------------------------------------------- /src/login-buttons.ts: -------------------------------------------------------------------------------- 1 | import { Component, NgZone } from '@angular/core'; 2 | import { Accounts } from 'meteor/accounts-base'; 3 | import { Tracker } from 'meteor/tracker'; 4 | import { Meteor } from 'meteor/meteor'; 5 | 6 | declare var Package; 7 | declare var _; 8 | 9 | export interface LoginCredentials { 10 | email: string; 11 | password: string; 12 | } 13 | 14 | @Component({ 15 | selector: 'login-buttons', 16 | styles: [ 17 | ` 18 | .login-buttons { 19 | position: relative; 20 | display: inline-block; 21 | } 22 | .login-buttons .dropdown-toggle span { 23 | cursor: pointer; 24 | } 25 | .login-buttons .accounts-close { 26 | position: absolute; 27 | top: 0; 28 | right: 5px; 29 | cursor: pointer; 30 | font-weight: bold; 31 | line-height: 20px; 32 | text-decoration: underline; 33 | opacity: 0.8; 34 | } 35 | .login-buttons .accounts-close:hover { 36 | opacity: 1; 37 | } 38 | .login-buttons .content-container { 39 | position: absolute; 40 | top: 0; 41 | left: 0; 42 | border: 1px solid #ccc; 43 | z-index: 1000; 44 | background: white; 45 | border-radius: 4px; 46 | padding: 8px 12px; 47 | margin: -8px -12px 0 -12px; 48 | width: 250px; 49 | box-shadow: 0 0 3px 0 rgba(0, 0, 0, 0.2); 50 | -webkit-box-shadow: 0 0 3px 0 rgba(0, 0, 0, 0.2); 51 | font-size: 16px; 52 | color: #333; 53 | } 54 | .login-buttons .content-container > * { 55 | line-height: 1.6; 56 | } 57 | .login-buttons .content-container > .login-close-text { 58 | line-height: inherit; 59 | font-size: inherit; 60 | font-family: inherit; 61 | } 62 | .login-buttons .content-container label, 63 | .login-buttons .content-container .title { 64 | font-size: 80%; 65 | margin-top: 7px; 66 | margin-bottom: -2px; 67 | } 68 | .login-buttons .content-container label { 69 | display: inline; 70 | } 71 | .login-buttons .content-container input[type=text], 72 | .login-buttons .content-container input[type=email], 73 | .login-buttons .content-container input[type=password] { 74 | -webkit-box-sizing: border-box; 75 | -moz-box-sizing: border-box; 76 | box-sizing: border-box; 77 | width: 100%; 78 | } 79 | .login-buttons .content-container input[type=text][type], 80 | .login-buttons .content-container input[type=email][type], 81 | .login-buttons .content-container input[type=password][type] { 82 | height: auto; 83 | } 84 | .login-buttons .loading { 85 | line-height: 1; 86 | background-image: url(data:image/gif;base64,R0lGODlhEAALAPQAAP///wAAANra2tDQ0Orq6gYGBgAAAC4uLoKCgmBgYLq6uiIiIkpKSoqKimRkZL6+viYmJgQEBE5OTubm5tjY2PT09Dg4ONzc3PLy8ra2tqCgoMrKyu7u7gAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCwAAACwAAAAAEAALAAAFLSAgjmRpnqSgCuLKAq5AEIM4zDVw03ve27ifDgfkEYe04kDIDC5zrtYKRa2WQgAh+QQJCwAAACwAAAAAEAALAAAFJGBhGAVgnqhpHIeRvsDawqns0qeN5+y967tYLyicBYE7EYkYAgAh+QQJCwAAACwAAAAAEAALAAAFNiAgjothLOOIJAkiGgxjpGKiKMkbz7SN6zIawJcDwIK9W/HISxGBzdHTuBNOmcJVCyoUlk7CEAAh+QQJCwAAACwAAAAAEAALAAAFNSAgjqQIRRFUAo3jNGIkSdHqPI8Tz3V55zuaDacDyIQ+YrBH+hWPzJFzOQQaeavWi7oqnVIhACH5BAkLAAAALAAAAAAQAAsAAAUyICCOZGme1rJY5kRRk7hI0mJSVUXJtF3iOl7tltsBZsNfUegjAY3I5sgFY55KqdX1GgIAIfkECQsAAAAsAAAAABAACwAABTcgII5kaZ4kcV2EqLJipmnZhWGXaOOitm2aXQ4g7P2Ct2ER4AMul00kj5g0Al8tADY2y6C+4FIIACH5BAkLAAAALAAAAAAQAAsAAAUvICCOZGme5ERRk6iy7qpyHCVStA3gNa/7txxwlwv2isSacYUc+l4tADQGQ1mvpBAAIfkECQsAAAAsAAAAABAACwAABS8gII5kaZ7kRFGTqLLuqnIcJVK0DeA1r/u3HHCXC/aKxJpxhRz6Xi0ANAZDWa+kEAA7AAAAAAAAAAAA); 87 | width: 16px; 88 | background-position: center center; 89 | background-repeat: no-repeat; 90 | }`], 91 | template: ` 92 | ` 148 | }) 149 | export class LoginButtons { 150 | autorunComputation: Tracker.Computation; 151 | currentUser: Meteor.User; 152 | currentUserId: string; 153 | isLoggingIn: boolean; 154 | isLoggedIn: boolean; 155 | services: Array; 156 | credentials: LoginCredentials; 157 | errors: Array; 158 | isPasswordRecovery: boolean; 159 | isSignup: boolean; 160 | isDropdownOpen: boolean; 161 | message: string; 162 | 163 | constructor(private zone: NgZone) { 164 | this._initAutorun(); 165 | this.services = this._getLoginServices(); 166 | this.resetErrors(); 167 | this.isPasswordRecovery = false; 168 | this.isSignup = false; 169 | this.isDropdownOpen = false; 170 | this._resetCredentialsFields(); 171 | } 172 | 173 | _resetCredentialsFields() { 174 | this.credentials = { email: '', password: '' }; 175 | } 176 | 177 | resetErrors() { 178 | this.errors = []; 179 | this.message = ""; 180 | } 181 | 182 | singleService(): Object { 183 | let services = this._getLoginServices(); 184 | 185 | return services[0]; 186 | } 187 | 188 | displayName(): string { 189 | let user : any = this.currentUser; 190 | 191 | if (!user) 192 | return ''; 193 | 194 | if (user.profile && user.profile.name) 195 | return user.profile.name; 196 | 197 | if (user.username) 198 | return user.username; 199 | 200 | if (user.emails && user.emails[0] && user.emails[0].address) 201 | return user.emails[0].address; 202 | 203 | return ''; 204 | }; 205 | 206 | login(): void { 207 | this.resetErrors(); 208 | 209 | let email: string = this.credentials.email; 210 | let password: string = this.credentials.password; 211 | 212 | Meteor.loginWithPassword(email, password, (error) => { 213 | if (error) { 214 | this.errors.push(error.reason || "Unknown error"); 215 | } 216 | else { 217 | this.isDropdownOpen = false; 218 | this._resetCredentialsFields(); 219 | } 220 | }); 221 | } 222 | 223 | recover() { 224 | this.resetErrors(); 225 | 226 | Accounts.forgotPassword({ email: this.credentials.email }, (error) => { 227 | if (error) { 228 | this.errors.push(error.reason || "Unknown error"); 229 | } 230 | else { 231 | this.message = "You will receive further instruction to you email address!"; 232 | this.isDropdownOpen = false; 233 | this._resetCredentialsFields(); 234 | } 235 | }); 236 | } 237 | 238 | logout(): void { 239 | Meteor.logout(); 240 | this.isDropdownOpen = false; 241 | } 242 | 243 | signup(): void { 244 | this.resetErrors(); 245 | 246 | Accounts.createUser(this.credentials, (error) => { 247 | if (error) { 248 | this.errors.push(error.reason || "Unknown error"); 249 | } 250 | else { 251 | this.isDropdownOpen = false; 252 | this._resetCredentialsFields(); 253 | } 254 | }); 255 | } 256 | 257 | _hasPasswordService(): boolean { 258 | return !!Package['accounts-password']; 259 | } 260 | 261 | _getLoginServices(): Array { 262 | let services = Package['accounts-oauth'] ? Accounts.oauth.serviceNames() : []; 263 | services.sort(); 264 | 265 | if (this._hasPasswordService()) 266 | services.push('password'); 267 | 268 | return _.map(services, function(name) { 269 | return { name: name }; 270 | }); 271 | } 272 | 273 | dropdown(): boolean { 274 | return this._hasPasswordService() || this._getLoginServices().length > 1; 275 | } 276 | 277 | _initAutorun(): void { 278 | this.autorunComputation = Tracker.autorun(() => { 279 | this.zone.run(() => { 280 | this.currentUser = Meteor.user(); 281 | this.currentUserId = Meteor.userId(); 282 | this.isLoggingIn = Meteor.loggingIn(); 283 | this.isLoggedIn = !!Meteor.user(); 284 | }) 285 | }); 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "experimentalDecorators": true, 4 | "module": "commonjs", 5 | "target": "es5", 6 | "lib": [ 7 | "es6", 8 | "dom" 9 | ], 10 | "noImplicitAny": false, 11 | "suppressImplicitAnyIndexErrors": true, 12 | "moduleResolution": "node", 13 | "emitDecoratorMetadata": true, 14 | "sourceMap": false, 15 | "noEmitHelpers": true, 16 | "declaration": true, 17 | "outDir": "./build", 18 | "rootDir": "./src" 19 | }, 20 | "files": [ 21 | "typings.d.ts", 22 | "src/index.ts" 23 | ], 24 | "exclude": [ 25 | "node_modules" 26 | ] 27 | } 28 | --------------------------------------------------------------------------------