├── .editorconfig ├── .github └── stale.yml ├── .gitignore ├── .vscode └── launch.json ├── README.md ├── angular.json ├── browserslist ├── e2e ├── app.e2e-spec.ts ├── app.po.ts ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts ├── tsconfig.e2e.json └── tsconfig.json ├── karma.conf.js ├── package-lock.json ├── package.json ├── protractor.conf.js ├── src ├── app │ ├── app-routing.module.ts │ ├── app.component.css │ ├── app.component.html │ ├── app.component.spec.ts │ ├── app.component.ts │ ├── app.module.ts │ ├── components │ │ ├── chat │ │ │ ├── chat.component.css │ │ │ ├── chat.component.html │ │ │ ├── chat.component.spec.ts │ │ │ └── chat.component.ts │ │ └── login │ │ │ ├── login.component.css │ │ │ ├── login.component.html │ │ │ ├── login.component.spec.ts │ │ │ └── login.component.ts │ ├── guards │ │ ├── auth.guard.spec.ts │ │ └── auth.guard.ts │ └── services │ │ ├── auth.service.spec.ts │ │ ├── auth.service.ts │ │ ├── data.service.spec.ts │ │ ├── data.service.ts │ │ ├── feathers.service.spec.ts │ │ └── feathers.service.ts ├── assets │ └── .gitkeep ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── main.ts ├── polyfills.ts ├── styles.scss └── test.ts ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.spec.json └── tslint.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://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 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 84 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - greenkeeper 8 | - bug 9 | - security 10 | - enhancement 11 | # Label to use when marking an issue as stale 12 | staleLabel: wontfix 13 | # Comment to post when marking an issue as stale. Set to `false` to disable 14 | markComment: > 15 | This issue has been automatically marked as stale because it has not had 16 | recent activity. It will be closed if no further activity occurs. 17 | Apologies if the issue could not be resolved. FeathersJS ecosystem 18 | modules are community maintained so there may be a chance that there isn't anybody 19 | available to address the issue at the moment. 20 | For other ways to get help [see here](https://docs.feathersjs.com/help/readme.html). 21 | # Comment to post when closing a stale issue. Set to `false` to disable 22 | closeComment: false 23 | # Only close stale issues 24 | only: issues 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /dist-server 6 | /tmp 7 | /out-tsc 8 | # Only exists if Bazel was run 9 | /bazel-out 10 | 11 | # dependencies 12 | /node_modules 13 | 14 | # profiling files 15 | chrome-profiler-events*.json 16 | speed-measure-plugin*.json 17 | 18 | # IDEs and editors 19 | /.idea 20 | .project 21 | .classpath 22 | .c9/ 23 | *.launch 24 | .settings/ 25 | *.sublime-workspace 26 | 27 | # IDE - VSCode 28 | .vscode/* 29 | !.vscode/settings.json 30 | !.vscode/tasks.json 31 | !.vscode/launch.json 32 | !.vscode/extensions.json 33 | .history/* 34 | 35 | # misc 36 | /.sass-cache 37 | /connect.lock 38 | /coverage 39 | /libpeerconnection.log 40 | npm-debug.log 41 | yarn-error.log 42 | testem.log 43 | /typings 44 | 45 | # e2e 46 | /e2e/*.js 47 | /e2e/*.map 48 | 49 | # System Files 50 | .DS_Store 51 | Thumbs.db 52 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "chrome", 9 | "request": "launch", 10 | "name": "Launch Chrome against localhost", 11 | "url": "http://localhost:4200", 12 | "webRoot": "${workspaceFolder}" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > __Note:__ All chat examples have been moved to https://github.com/feathersjs/feathers-chat/ 2 | 3 | # FeathersChatAngular 4 | 5 | This is an angular client for feathers' demo chat application [feathers-chat](https://github.com/feathersjs/feathers-chat). 6 | 7 | Some of the abstraction layers used are not strictly necessary for this app's (fairly limited) functionality. They were used to follow best-practice and provide an overview of what a more sophisticated application using feathers could look like. 8 | 9 | ## How to set this up 10 | 11 | The [feathers-chat](https://github.com/feathersjs/feathers-chat) server must be running. Check their repository for further instructions. 12 | 13 | # it's recommended install angular-cli globally, but you don't have to 14 | npm i -g @angular/cli 15 | 16 | # clone this repository 17 | git clone https://github.com/feathersjs/feathers-chat-angular.git 18 | 19 | # change directory 20 | cd feathers-chat-angular 21 | 22 | # install dependencies 23 | npm install 24 | 25 | # run 26 | # (if you have angular-cli installed globally) 27 | ng serve 28 | # (if you haven't) 29 | npm start 30 | # (also possible) 31 | node_modules/.bin/ng serve 32 | 33 | Then navigate to [localhost:4200](http://localhost:4200) 34 | 35 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.3.2. 36 | 37 | ## Development server 38 | 39 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 40 | 41 | ## Code scaffolding 42 | 43 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 44 | 45 | ## Build 46 | 47 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 48 | 49 | ## Running unit tests 50 | 51 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 52 | 53 | ## Running end-to-end tests 54 | 55 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 56 | 57 | ## Further help 58 | 59 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 60 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "feathers-chat-angular": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "architect": { 11 | "build": { 12 | "builder": "@angular-devkit/build-angular:browser", 13 | "options": { 14 | "outputPath": "dist", 15 | "index": "src/index.html", 16 | "main": "src/main.ts", 17 | "tsConfig": "tsconfig.app.json", 18 | "polyfills": "src/polyfills.ts", 19 | "assets": [ 20 | "src/assets", 21 | "src/favicon.ico" 22 | ], 23 | "styles": [ 24 | "src/styles.scss" 25 | ], 26 | "scripts": [] 27 | }, 28 | "configurations": { 29 | "production": { 30 | "fileReplacements": [ 31 | { 32 | "replace": "src/environments/environment.ts", 33 | "with": "src/environments/environment.prod.ts" 34 | } 35 | ], 36 | "optimization": true, 37 | "outputHashing": "all", 38 | "sourceMap": false, 39 | "extractCss": true, 40 | "namedChunks": false, 41 | "aot": true, 42 | "extractLicenses": true, 43 | "vendorChunk": false, 44 | "buildOptimizer": true, 45 | "budgets": [ 46 | { 47 | "type": "initial", 48 | "maximumWarning": "2mb", 49 | "maximumError": "5mb" 50 | }, 51 | { 52 | "type": "anyComponentStyle", 53 | "maximumWarning": "6kb", 54 | "maximumError": "10kb" 55 | } 56 | ] 57 | } 58 | } 59 | }, 60 | "serve": { 61 | "builder": "@angular-devkit/build-angular:dev-server", 62 | "options": { 63 | "browserTarget": "feathers-chat-angular:build" 64 | }, 65 | "configurations": { 66 | "production": { 67 | "browserTarget": "feathers-chat-angular:build:production" 68 | } 69 | } 70 | }, 71 | "extract-i18n": { 72 | "builder": "@angular-devkit/build-angular:extract-i18n", 73 | "options": { 74 | "browserTarget": "feathers-chat-angular:build" 75 | } 76 | }, 77 | "test": { 78 | "builder": "@angular-devkit/build-angular:karma", 79 | "options": { 80 | "main": "src/test.ts", 81 | "karmaConfig": "./karma.conf.js", 82 | "polyfills": "src/polyfills.ts", 83 | "tsConfig": "tsconfig.spec.json", 84 | "karmaConfig": "karma.conf.js", 85 | "assets": [ 86 | "src/favicon.ico", 87 | "src/assets" 88 | ], 89 | "styles": [ 90 | "src/styles.scss" 91 | ], 92 | "assets": [ 93 | "src/assets", 94 | "src/favicon.ico" 95 | ] 96 | } 97 | }, 98 | "lint": { 99 | "builder": "@angular-devkit/build-angular:tslint", 100 | "options": { 101 | "tsConfig": [ 102 | "tsconfig.app.json", 103 | "tsconfig.spec.json", 104 | "e2e/tsconfig.json" 105 | ], 106 | "exclude": [ 107 | "**/node_modules/**" 108 | ] 109 | } 110 | } 111 | } 112 | }, 113 | "feathers-chat-angular-e2e": { 114 | "root": "e2e", 115 | "sourceRoot": "e2e", 116 | "projectType": "application", 117 | "architect": { 118 | "e2e": { 119 | "builder": "@angular-devkit/build-angular:protractor", 120 | "options": { 121 | "protractorConfig": "./protractor.conf.js", 122 | "devServerTarget": "feathers-chat-angular:serve" 123 | } 124 | }, 125 | "lint": { 126 | "builder": "@angular-devkit/build-angular:tslint", 127 | "options": { 128 | "tsConfig": [ 129 | "e2e/tsconfig.e2e.json" 130 | ], 131 | "exclude": [ 132 | "**/node_modules/**" 133 | ] 134 | } 135 | } 136 | } 137 | } 138 | }, 139 | "defaultProject": "feathers-chat-angular", 140 | "schematics": { 141 | "@schematics/angular:component": { 142 | "prefix": "app", 143 | "styleext": "scss" 144 | }, 145 | "@schematics/angular:directive": { 146 | "prefix": "app" 147 | } 148 | } 149 | } -------------------------------------------------------------------------------- /browserslist: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('feathers-chat-angular 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 app!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /e2e/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/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('feathers-chat-angular app is running!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('app-root .content span')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": [ 9 | "jasmine", 10 | "jasminewd2", 11 | "node" 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /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/feathers-chat-angular'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | 24 | reporters: ['progress', 'kjhtml'], 25 | port: 9876, 26 | colors: true, 27 | logLevel: config.LOG_INFO, 28 | autoWatch: true, 29 | browsers: ['Chrome'], 30 | singleRun: false, 31 | restartOnFileChange: true 32 | }); 33 | }; 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "feathers-chat-angular", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "ng": "ng", 7 | "start": "ng serve", 8 | "build": "ng build --prod", 9 | "test": "ng test", 10 | "lint": "ng lint", 11 | "e2e": "ng e2e" 12 | }, 13 | "private": true, 14 | "dependencies": { 15 | "@angular/animations": "~8.2.4", 16 | "@angular/cdk": "~8.1.4", 17 | "@angular/common": "~8.2.4", 18 | "@angular/compiler": "~8.2.4", 19 | "@angular/core": "~8.2.4", 20 | "@angular/forms": "~8.2.4", 21 | "@angular/material": "^8.1.4", 22 | "@angular/platform-browser": "~8.2.4", 23 | "@angular/platform-browser-dynamic": "~8.2.4", 24 | "@angular/router": "~8.2.4", 25 | "@feathersjs/authentication": "^4.3.0", 26 | "@feathersjs/authentication-client": "^4.3.0", 27 | "@feathersjs/feathers": "^4.3.0", 28 | "@feathersjs/socketio-client": "^4.3.0", 29 | "@feathersjs/transport-commons": "^4.3.0", 30 | "@types/feathersjs__authentication-client": "^1.0.5", 31 | "@types/feathersjs__feathers": "^3.1.5", 32 | "@types/feathersjs__socketio-client": "^1.0.3", 33 | "core-js": "^3.2.1", 34 | "feathers-reactive": "^0.8.1", 35 | "getbase": "^3.5.1", 36 | "hammerjs": "^2.0.8", 37 | "rxjs": "~6.4.0", 38 | "tslib": "^1.10.0", 39 | "zone.js": "^0.10.2" 40 | }, 41 | "devDependencies": { 42 | "@angular-devkit/build-angular": "~0.803.2", 43 | "@angular/cli": "~8.3.2", 44 | "@angular/compiler-cli": "~8.2.4", 45 | "@angular/language-service": "~8.2.4", 46 | "@types/node": "~8.9.4", 47 | "@types/jasmine": "~3.3.8", 48 | "@types/jasminewd2": "~2.0.3", 49 | "codelyzer": "^5.0.0", 50 | "jasmine-core": "~3.4.0", 51 | "jasmine-spec-reporter": "~4.2.1", 52 | "karma": "~4.1.0", 53 | "karma-chrome-launcher": "~2.2.0", 54 | "karma-cli": "~1.0.1", 55 | "karma-coverage-istanbul-reporter": "~2.0.1", 56 | "karma-jasmine": "~2.0.1", 57 | "karma-jasmine-html-reporter": "^1.4.0", 58 | "protractor": "~5.4.0", 59 | "ts-node": "~7.0.0", 60 | "tslint": "~5.15.0", 61 | "typescript": "~3.5.3" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /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 | './e2e/**/*.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: 'e2e/tsconfig.e2e.json' 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { ChatComponent } from './components/chat/chat.component'; 4 | import { AuthGuard } from './guards/auth.guard'; 5 | import { LoginComponent } from './components/login/login.component'; 6 | 7 | /* 8 | Our app's routes. 9 | If you don't know what this means, check https://angular.io/docs/ts/latest/guide/router.html 10 | */ 11 | const routes: Routes = [ 12 | { 13 | path: '', 14 | children: [ 15 | { 16 | path: '', 17 | pathMatch: 'full', 18 | redirectTo: 'chat' 19 | }, 20 | { 21 | path: 'chat', 22 | component: ChatComponent, 23 | canActivate: [AuthGuard] 24 | }, 25 | { 26 | path: 'login', 27 | component: LoginComponent 28 | } 29 | ] 30 | } 31 | ]; 32 | 33 | @NgModule({ 34 | imports: [RouterModule.forRoot(routes)], 35 | exports: [RouterModule] 36 | }) 37 | export class AppRoutingModule {} 38 | -------------------------------------------------------------------------------- /src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feathersjs-ecosystem/feathers-chat-angular/7993ef5ecf412206d9a5f33abe4f89a85d35aa3b/src/app/app.component.css -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async(() => { 7 | TestBed.configureTestingModule({ 8 | imports: [ 9 | RouterTestingModule 10 | ], 11 | declarations: [ 12 | AppComponent 13 | ], 14 | }).compileComponents(); 15 | })); 16 | 17 | it('should create the app', () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.debugElement.componentInstance; 20 | expect(app).toBeTruthy(); 21 | }); 22 | 23 | it(`should have as title 'app works!'`, () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | const app = fixture.debugElement.componentInstance; 26 | expect(app.title).toEqual('app works!'); 27 | }); 28 | 29 | it('should render title in a h1 tag', () => { 30 | const fixture = TestBed.createComponent(AppComponent); 31 | fixture.detectChanges(); 32 | const compiled = fixture.debugElement.nativeElement; 33 | expect(compiled.querySelector('h1').textContent).toContain('app works!'); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'] 7 | }) 8 | export class AppComponent { 9 | constructor() {} 10 | } 11 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { FormsModule } from '@angular/forms'; 4 | 5 | import { AppRoutingModule } from './app-routing.module'; 6 | import { AppComponent } from './app.component'; 7 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 8 | import { Feathers } from './services/feathers.service'; 9 | import { LoginComponent } from './components/login/login.component'; 10 | import { ChatComponent } from './components/chat/chat.component'; 11 | import { DataService } from './services/data.service'; 12 | import { AuthService } from './services/auth.service'; 13 | import { AuthGuard } from './guards/auth.guard'; 14 | 15 | @NgModule({ 16 | declarations: [ 17 | AppComponent, 18 | LoginComponent, 19 | ChatComponent 20 | ], 21 | imports: [ 22 | BrowserModule, 23 | FormsModule, 24 | AppRoutingModule, 25 | BrowserAnimationsModule 26 | ], 27 | providers: [ 28 | Feathers, 29 | DataService, 30 | AuthService, 31 | AuthGuard 32 | ], 33 | bootstrap: [AppComponent] 34 | }) 35 | export class AppModule { } 36 | -------------------------------------------------------------------------------- /src/app/components/chat/chat.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feathersjs-ecosystem/feathers-chat-angular/7993ef5ecf412206d9a5f33abe4f89a85d35aa3b/src/app/components/chat/chat.component.css -------------------------------------------------------------------------------- /src/app/components/chat/chat.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 6 | Chat 7 |
8 |
9 | 10 |
11 | 32 | 33 |
34 |
35 |
36 | 37 |
38 |

39 | {{message.user.email}} 40 | {{message.createdAt | date:'MMM dd, hh:mm:ss'}} 41 |

42 |

{{message.text}}

43 |
44 |
45 |
46 | 47 |
48 | 49 | 50 |
51 |
52 |
53 |
54 | -------------------------------------------------------------------------------- /src/app/components/chat/chat.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ChatComponent } from './chat.component'; 4 | 5 | describe('ChatComponent', () => { 6 | let component: ChatComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ChatComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ChatComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/components/chat/chat.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectionStrategy, Component } from '@angular/core'; 2 | import { Paginated } from '@feathersjs/feathers'; 3 | import { Observable } from 'rxjs'; 4 | import { map } from 'rxjs/operators'; 5 | import { DataService } from '../../services/data.service'; 6 | import { AuthService } from '../../services/auth.service'; 7 | 8 | @Component({ 9 | selector: 'app-chat', 10 | templateUrl: './chat.component.html', 11 | styleUrls: ['./chat.component.css'], 12 | changeDetection: ChangeDetectionStrategy.OnPush 13 | }) 14 | export class ChatComponent { 15 | messages$: Observable; 16 | users$: Observable; 17 | 18 | constructor(private data: DataService, private auth: AuthService) { 19 | // get messages from data service 20 | this.messages$ = data.messages$().pipe( 21 | // our data is paginated, so map to .data 22 | map((m: Paginated) => m.data), 23 | // reverse the messages array, to have the most recent message at the end 24 | // necessary because we get a descendingly sorted array from the data service 25 | map((m: Array) => m.reverse()), 26 | ); 27 | 28 | // get users from data service 29 | this.users$ = data.users$().pipe( 30 | // our data is paginated, so map to .data 31 | map((u: Paginated) => u.data) 32 | ); 33 | } 34 | 35 | sendMessage(message: string) { 36 | this.data.sendMessage(message); 37 | } 38 | 39 | logOut() { 40 | this.auth.logOut(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/app/components/login/login.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feathersjs-ecosystem/feathers-chat-angular/7993ef5ecf412206d9a5f33abe4f89a85d35aa3b/src/app/components/login/login.component.css -------------------------------------------------------------------------------- /src/app/components/login/login.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Log in or signup

5 |
6 |
7 |
8 |
9 |
10 |
11 | 12 |
13 | 14 |
15 | 16 |
17 | 18 | 21 | 22 | 25 |
26 |
    27 |
  • {{m}}
  • 28 |
29 |
30 |
31 |
32 | -------------------------------------------------------------------------------- /src/app/components/login/login.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { LoginComponent } from './login.component'; 4 | 5 | describe('LoginComponent', () => { 6 | let component: LoginComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ LoginComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(LoginComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/components/login/login.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | import { Feathers } from '../../services/feathers.service'; 4 | 5 | @Component({ 6 | selector: 'app-login', 7 | templateUrl: './login.component.html', 8 | styleUrls: ['./login.component.css'] 9 | }) 10 | export class LoginComponent { 11 | messages: string[] = []; 12 | 13 | constructor(private feathers: Feathers, private router: Router) {} 14 | 15 | login(email: string, password: string) { 16 | if (!email || !password) { 17 | this.messages.push('Incomplete credentials!'); 18 | return; 19 | } 20 | 21 | // try to authenticate with feathers 22 | this.feathers.authenticate({ 23 | strategy: 'local', 24 | email, 25 | password 26 | }) 27 | // navigate to base URL on success 28 | .then(() => { 29 | this.router.navigate(['/']); 30 | }) 31 | .catch(err => { 32 | this.messages.unshift('Wrong credentials!'); 33 | }); 34 | } 35 | 36 | signup(email: string, password: string) { 37 | this.feathers.service('users') 38 | .create({email, password}) 39 | .then(() => this.messages.push('User created.')) 40 | .catch(err => this.messages.push('Could not create user!')) 41 | ; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/app/guards/auth.guard.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async, inject } from '@angular/core/testing'; 2 | 3 | import { AuthGuard } from './auth.guard'; 4 | 5 | describe('AuthGuard', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [AuthGuard] 9 | }); 10 | }); 11 | 12 | it('should ...', inject([AuthGuard], (guard: AuthGuard) => { 13 | expect(guard).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /src/app/guards/auth.guard.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router'; 3 | import { Observable } from 'rxjs'; 4 | import { AuthService } from '../services/auth.service'; 5 | 6 | 7 | @Injectable() 8 | export class AuthGuard implements CanActivate { 9 | constructor(private router: Router, private auth: AuthService) {} 10 | 11 | canActivate(next: ActivatedRouteSnapshot, 12 | state: RouterStateSnapshot): Observable | Promise | boolean { 13 | 14 | /* Try to auth with the server. If authed resolve to true, else resolve to false */ 15 | return this.auth 16 | .logIn() 17 | .then(() => true) 18 | .catch(() => { 19 | this.router.navigate(['/login']); 20 | return false; 21 | }); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/app/services/auth.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { AuthService } from './auth.service'; 4 | 5 | describe('AuthService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [AuthService] 9 | }); 10 | }); 11 | 12 | it('should ...', inject([AuthService], (service: AuthService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /src/app/services/auth.service.ts: -------------------------------------------------------------------------------- 1 | import { Feathers } from './feathers.service'; 2 | import { Injectable } from '@angular/core'; 3 | import { Router } from '@angular/router'; 4 | 5 | /** 6 | * Abstraction layer for auth. Nice to have when things get more complicated. 7 | */ 8 | @Injectable() 9 | export class AuthService { 10 | 11 | constructor(private feathers: Feathers, private router: Router) {} 12 | 13 | public logIn(credentials?): Promise { 14 | return this.feathers.authenticate(credentials); 15 | } 16 | 17 | public logOut() { 18 | this.feathers.logout(); 19 | this.router.navigate(['/']); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/app/services/data.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { DataService } from './data.service'; 4 | 5 | describe('DataService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [DataService] 9 | }); 10 | }); 11 | 12 | it('should ...', inject([DataService], (service: DataService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /src/app/services/data.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Feathers } from './feathers.service'; 3 | 4 | /** 5 | * Abstraction layer for data management 6 | * Technically this isn't needed for feathers-chat, 7 | * but you will need it for more complex tasks. 8 | */ 9 | @Injectable() 10 | export class DataService { 11 | constructor(private feathers: Feathers) { 12 | } 13 | 14 | messages$() { 15 | // just returning the observable will query the backend on every subscription 16 | // using some caching mechanism would be wise in more complex applications 17 | return (this.feathers // todo: remove 'any' assertion when feathers-reactive typings are up-to-date with buzzard 18 | .service('messages')) 19 | .watch() 20 | .find({ 21 | query: { 22 | $sort: {createdAt: -1}, 23 | $limit: 25 24 | } 25 | }); 26 | } 27 | 28 | users$() { 29 | // just returning the observable will query the backend on every subscription 30 | // using some caching mechanism would be wise in more complex applications 31 | return (this.feathers // todo: remove 'any' assertion when feathers-reactive typings are up-to-date with buzzard 32 | .service('users')) 33 | .watch() 34 | .find(); 35 | } 36 | 37 | sendMessage(message: string) { 38 | if (message === '') { 39 | return; 40 | } 41 | 42 | // feathers-reactive Observables are hot by default, 43 | // so we don't need to subscribe to make create() happen. 44 | this.feathers 45 | .service('messages') 46 | .create({ 47 | text: message 48 | }); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/app/services/feathers.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { Feathers } from './feathers.service'; 4 | 5 | describe('FeathersService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [Feathers] 9 | }); 10 | }); 11 | 12 | it('should ...', inject([Feathers], (service: Feathers) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /src/app/services/feathers.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import * as feathersRx from 'feathers-reactive'; 4 | import * as io from 'socket.io-client'; 5 | 6 | import feathers from '@feathersjs/feathers'; 7 | import feathersSocketIOClient from '@feathersjs/socketio-client'; 8 | import feathersAuthClient2 from '@feathersjs/authentication-client'; 9 | 10 | /** 11 | * Simple wrapper for feathers 12 | */ 13 | @Injectable() 14 | export class Feathers { 15 | private _feathers = feathers(); // init socket.io 16 | private _socket = io('http://localhost:3030'); // init feathers 17 | private feathersAuthClient = require('@feathersjs/authentication-client').default; 18 | 19 | constructor() { 20 | this._feathers 21 | .configure(feathersSocketIOClient(this._socket)) // add socket.io plugin 22 | .configure(this.feathersAuthClient({ // add authentication plugin 23 | storage: window.localStorage 24 | })) 25 | .configure(feathersRx({ // add feathers-reactive plugin 26 | idField: '_id' 27 | })); 28 | } 29 | 30 | // expose services 31 | public service(name: string) { 32 | return this._feathers.service(name); 33 | } 34 | 35 | // expose authentication 36 | public authenticate(credentials?): Promise { 37 | return this._feathers.authenticate(credentials); 38 | } 39 | 40 | // expose logout 41 | public logout() { 42 | return this._feathers.logout(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feathersjs-ecosystem/feathers-chat-angular/7993ef5ecf412206d9a5f33abe4f89a85d35aa3b/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `.angular-cli.json`. 5 | 6 | export const environment = { 7 | production: false 8 | }; 9 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feathersjs-ecosystem/feathers-chat-angular/7993ef5ecf412206d9a5f33abe4f89a85d35aa3b/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | FeathersChatAngular 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import 'hammerjs'; 2 | import { enableProdMode } from '@angular/core'; 3 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 4 | 5 | import { AppModule } from './app/app.module'; 6 | import { environment } from './environments/environment'; 7 | 8 | if (environment.production) { 9 | enableProdMode(); 10 | } 11 | 12 | platformBrowserDynamic().bootstrapModule(AppModule) 13 | .catch(err => console.log(err)); 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 | 47 | 48 | 49 | /** 50 | * Required to support Web Animations `@angular/platform-browser/animations`. 51 | * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation 52 | **/ 53 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 54 | 55 | 56 | 57 | /*************************************************************************************************** 58 | * Zone JS is required by default for Angular itself. 59 | */ 60 | import 'zone.js/dist/zone'; // Included with Angular CLI. 61 | 62 | 63 | 64 | /*************************************************************************************************** 65 | * APPLICATION IMPORTS 66 | */ 67 | 68 | (window as any).global = window; -------------------------------------------------------------------------------- /src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | 3 | @import url('~getbase/css/styles.css'); 4 | 5 | html, body { 6 | font-family: 'Helvetica Neue', 'Helevetica', 'Arial', 'sans-serif'; 7 | color: #333; 8 | height: 100%; 9 | } 10 | 11 | .flex-1 { 12 | flex: 1 1 0; 13 | } 14 | 15 | .flex-2 { 16 | flex: 2; 17 | } 18 | 19 | .row { 20 | padding-top: 10px; 21 | padding-bottom: 10px; 22 | } 23 | 24 | a { 25 | text-decoration: none; 26 | color: #31D8A0; 27 | } 28 | 29 | a:hover, 30 | a:focus { 31 | text-decoration: none; 32 | color: #27C895; 33 | } 34 | 35 | .button { 36 | text-align: center; 37 | } 38 | 39 | .button-primary { 40 | padding: 0.6em 1.2em; 41 | background: #31D8A0; 42 | color: #ffffff; 43 | border: none; 44 | border-radius: 5px; 45 | transition: background 0.2s; 46 | } 47 | 48 | .button-primary:hover, 49 | .button-primary:focus { 50 | background: #27C895; 51 | color: #ffffff; 52 | } 53 | 54 | /* ======================================= */ 55 | /* Home Page */ 56 | /* ======================================= */ 57 | 58 | main.home { 59 | padding-top: 100px; 60 | padding-bottom: 100px; 61 | } 62 | 63 | main.home img.logo { 64 | width: 100%; 65 | max-width: 400px; 66 | } 67 | 68 | main.home h3.title { 69 | color: #969696; 70 | font-weight: 100; 71 | text-transform: uppercase; 72 | margin-bottom: 40px; 73 | } 74 | 75 | main.home .button.login, 76 | main.home .button.signup { 77 | padding-top: 1.2em; 78 | padding-bottom: 1.2em; 79 | } 80 | 81 | main.home .button.login { 82 | background: none; 83 | border: 2px solid #CFCFCF; 84 | color: #999; 85 | } 86 | 87 | main.home .button.login:hover, 88 | main.home .button.login:focus { 89 | background: none; 90 | border: 2px solid #31D8A0; 91 | color: #31D8A0; 92 | } 93 | 94 | /* ======================================= */ 95 | /* Signup and Login */ 96 | /* ======================================= */ 97 | 98 | 99 | main.login fieldset { 100 | padding: 10px 0; 101 | } 102 | 103 | main.login fieldset input { 104 | width: 100%; 105 | padding: 1.4em 0.7em; 106 | } 107 | 108 | main.login .button-primary { 109 | padding-top: 1.2em; 110 | padding-bottom: 1.2em; 111 | margin-top: 40px; 112 | width: 100%; 113 | } 114 | 115 | 116 | /* ======================================= */ 117 | /* Chat App */ 118 | /* ======================================= */ 119 | 120 | #app { 121 | height: 100%; 122 | } 123 | 124 | /* Header */ 125 | header.title-bar { 126 | padding: 10px 0; 127 | border-bottom: 1px solid #f1f1f1; 128 | } 129 | 130 | header.title-bar img.logo { 131 | width: 100%; 132 | max-width: 140px; 133 | } 134 | 135 | header.title-bar span.title { 136 | color: #969696; 137 | font-weight: 100; 138 | text-transform: uppercase; 139 | font-size: 1.2em; 140 | margin-left: 7px; 141 | } 142 | 143 | /* User List Sidebar */ 144 | 145 | aside.sidebar { 146 | background: #f8f8f8; 147 | // height: 100%; 148 | max-width: 340px; 149 | padding: 15px; 150 | border-right: 1px solid #f1f1f1; 151 | } 152 | 153 | aside.sidebar .online-count { 154 | color: #31D8A0; 155 | margin-right: 5px; 156 | } 157 | 158 | aside.sidebar h4 { 159 | margin: 0 0 20px 0; 160 | color: #C3C3C3; 161 | } 162 | 163 | aside.sidebar .user-list { 164 | overflow-y: scroll; 165 | } 166 | 167 | aside.sidebar li { 168 | margin: 15px 0; 169 | } 170 | 171 | aside.sidebar li > a { 172 | color: #555555; 173 | } 174 | 175 | aside.sidebar li > a:hover > span, 176 | aside.sidebar li > a:focus > span { 177 | color: #31D8A0; 178 | } 179 | 180 | aside.sidebar img.avatar { 181 | border-radius: 100%; 182 | height: 45px; 183 | width: 45px; 184 | margin-right: 10px; 185 | } 186 | 187 | aside.sidebar .username { 188 | position: absolute; 189 | line-height: 45px; 190 | } 191 | 192 | aside.sidebar footer { 193 | // min-height: 60px; 194 | } 195 | 196 | /* Main Chat */ 197 | main.flex { 198 | height: 100%; 199 | } 200 | 201 | main.chat { 202 | padding: 10px; 203 | overflow-y: scroll; 204 | } 205 | 206 | main.chat img.avatar { 207 | border-radius: 100%; 208 | height: 45px; 209 | width: 45px; 210 | flex: none; 211 | } 212 | 213 | main.chat .message { 214 | flex: 0 0 auto; 215 | } 216 | 217 | main.chat .message img.avatar { 218 | margin-right: 10px; 219 | } 220 | 221 | main.chat .message p.message-header { 222 | margin: 0; 223 | font-size: 0.9em; 224 | } 225 | 226 | main.chat .message p.message-header span.sent-date { 227 | color: #969696; 228 | } 229 | 230 | /* Send Message Form */ 231 | 232 | form { 233 | min-height: 60px; 234 | padding: 10px 20px; 235 | } 236 | 237 | form input { 238 | height: auto !important; 239 | } 240 | 241 | #send-message button { 242 | margin-left: 10px; 243 | } 244 | 245 | [v-cloak] { 246 | display: none; 247 | } 248 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": [] 6 | }, 7 | "files": [ 8 | "src/main.ts", 9 | "src/polyfills.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.ts" 13 | ], 14 | "exclude": [ 15 | "src/test.ts", 16 | "src/**/*.spec.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "allowSyntheticDefaultImports": true, 6 | "outDir": "./dist/out-tsc", 7 | "sourceMap": true, 8 | "declaration": false, 9 | "downlevelIteration": true, 10 | "experimentalDecorators": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "importHelpers": true, 14 | "target": "es2015", 15 | "typeRoots": [ 16 | "node_modules/@types" 17 | ], 18 | "lib": [ 19 | "es2018", 20 | "dom" 21 | ] 22 | }, 23 | "angularCompilerOptions": { 24 | "fullTemplateTypeCheck": true, 25 | "strictInjectionParameters": true 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /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 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /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-shadowed-variable": true, 69 | "no-string-literal": false, 70 | "no-string-throw": true, 71 | "no-switch-case-fall-through": true, 72 | "no-trailing-whitespace": true, 73 | "no-unnecessary-initializer": true, 74 | "no-unused-expression": true, 75 | "no-use-before-declare": true, 76 | "no-var-keyword": true, 77 | "object-literal-sort-keys": false, 78 | "one-line": [ 79 | true, 80 | "check-open-brace", 81 | "check-catch", 82 | "check-else", 83 | "check-whitespace" 84 | ], 85 | "prefer-const": true, 86 | "quotemark": [ 87 | true, 88 | "single" 89 | ], 90 | "radix": true, 91 | "semicolon": [ 92 | true, 93 | "always" 94 | ], 95 | "triple-equals": [ 96 | true, 97 | "allow-null-check" 98 | ], 99 | "typedef-whitespace": [ 100 | true, 101 | { 102 | "call-signature": "nospace", 103 | "index-signature": "nospace", 104 | "parameter": "nospace", 105 | "property-declaration": "nospace", 106 | "variable-declaration": "nospace" 107 | } 108 | ], 109 | "unified-signatures": true, 110 | "variable-name": false, 111 | "whitespace": [ 112 | true, 113 | "check-branch", 114 | "check-decl", 115 | "check-operator", 116 | "check-separator", 117 | "check-type" 118 | ], 119 | "directive-selector": [ 120 | true, 121 | "attribute", 122 | "app", 123 | "camelCase" 124 | ], 125 | "component-selector": [ 126 | true, 127 | "element", 128 | "app", 129 | "kebab-case" 130 | ], 131 | "no-output-on-prefix": true, 132 | "use-input-property-decorator": true, 133 | "use-output-property-decorator": true, 134 | "use-host-property-decorator": true, 135 | "no-input-rename": true, 136 | "no-output-rename": true, 137 | "use-life-cycle-interface": true, 138 | "use-pipe-transform-interface": true, 139 | "component-class-suffix": true, 140 | "directive-class-suffix": true 141 | } 142 | } 143 | --------------------------------------------------------------------------------