Skip to content

Commit cc1c6e8

Browse files
committed
feat(challenge 7): begin ngrx notification work
1 parent d774abf commit cc1c6e8

25 files changed

+596
-0
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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+
# For the full list of supported browsers by the Angular framework, please see:
6+
# https://angular.io/guide/browser-support
7+
8+
# You can see what browsers were selected by your queries by running:
9+
# npx browserslist
10+
11+
last 1 Chrome version
12+
last 1 Firefox version
13+
last 2 Edge major versions
14+
last 2 Safari major versions
15+
last 2 iOS major versions
16+
Firefox ESR
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"extends": ["../../.eslintrc.json"],
3+
"ignorePatterns": ["!**/*"],
4+
"overrides": [
5+
{
6+
"files": ["*.ts"],
7+
"extends": [
8+
"plugin:@nrwl/nx/angular",
9+
"plugin:@angular-eslint/template/process-inline-templates"
10+
],
11+
"rules": {
12+
"@angular-eslint/directive-selector": [
13+
"error",
14+
{
15+
"type": "attribute",
16+
"prefix": "app",
17+
"style": "camelCase"
18+
}
19+
],
20+
"@angular-eslint/component-selector": [
21+
"error",
22+
{
23+
"type": "element",
24+
"prefix": "app",
25+
"style": "kebab-case"
26+
}
27+
]
28+
}
29+
},
30+
{
31+
"files": ["*.html"],
32+
"extends": ["plugin:@nrwl/nx/angular-template"],
33+
"rules": {}
34+
}
35+
]
36+
}

apps/ngrx-notification/README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<h1>Ngrx Notification</h1>
2+
3+
> Author: Thomas Laforge
4+
5+
<!-- TODO: add Information/Statement/Rules/Constraint/Steps -->
6+
7+
### Information
8+
9+
### Statement
10+
11+
### Step 1
12+
13+
### Step 2
14+
15+
### Constraints:
16+
17+
### Submitting your work
18+
19+
1. Fork the project
20+
2. clone it
21+
3. npm install
22+
4. **nx serve ngrx-notification**
23+
5. _...work on it_
24+
6. Commit your work
25+
7. Submit a PR with a title beginning with **Answer:7** that I will review and other dev can review.
26+
27+
<!-- TODO: add challenge number and project Name -->
28+
29+
<a href="https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A7+label%3Aanswer"><img src="https://img.shields.io/badge/-Solutions-green" alt="Ngrx notification"/></a>
30+
31+
<!-- TODO: uncomment when done late -->
32+
<!-- <a href='https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A{challenge number}+label%3A"answer+author"'><img src="https://img.shields.io/badge/-Author solution-important" alt="{Project name} solution author"/></a>
33+
<a href="{Blog post url}" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/-Blog post explanation-blue" alt="{Project name} blog article"/></a> -->
34+
35+
_You can ask any question on_ <a href="https://twitter.com/laforge_toma" target="_blank" rel="noopener noreferrer"><img src="./../../logo/twitter.svg" height=20px alt="twitter"/></a>
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
{
2+
"name": "ngrx-notification",
3+
"$schema": "../../node_modules/nx/schemas/project-schema.json",
4+
"projectType": "application",
5+
"sourceRoot": "apps/ngrx-notification/src",
6+
"prefix": "app",
7+
"targets": {
8+
"build": {
9+
"executor": "@angular-devkit/build-angular:browser",
10+
"outputs": ["{options.outputPath}"],
11+
"options": {
12+
"outputPath": "dist/apps/ngrx-notification",
13+
"index": "apps/ngrx-notification/src/index.html",
14+
"main": "apps/ngrx-notification/src/main.ts",
15+
"polyfills": "apps/ngrx-notification/src/polyfills.ts",
16+
"tsConfig": "apps/ngrx-notification/tsconfig.app.json",
17+
"inlineStyleLanguage": "scss",
18+
"assets": [
19+
"apps/ngrx-notification/src/favicon.ico",
20+
"apps/ngrx-notification/src/assets"
21+
],
22+
"styles": ["apps/ngrx-notification/src/styles.scss"],
23+
"scripts": [],
24+
"allowedCommonJsDependencies": ["seedrandom"]
25+
},
26+
"configurations": {
27+
"production": {
28+
"budgets": [
29+
{
30+
"type": "initial",
31+
"maximumWarning": "500kb",
32+
"maximumError": "1mb"
33+
},
34+
{
35+
"type": "anyComponentStyle",
36+
"maximumWarning": "2kb",
37+
"maximumError": "4kb"
38+
}
39+
],
40+
"fileReplacements": [
41+
{
42+
"replace": "apps/ngrx-notification/src/environments/environment.ts",
43+
"with": "apps/ngrx-notification/src/environments/environment.prod.ts"
44+
}
45+
],
46+
"outputHashing": "all"
47+
},
48+
"development": {
49+
"buildOptimizer": false,
50+
"optimization": false,
51+
"vendorChunk": true,
52+
"extractLicenses": false,
53+
"sourceMap": true,
54+
"namedChunks": true
55+
}
56+
},
57+
"defaultConfiguration": "production"
58+
},
59+
"serve": {
60+
"executor": "@angular-devkit/build-angular:dev-server",
61+
"configurations": {
62+
"production": {
63+
"browserTarget": "ngrx-notification:build:production"
64+
},
65+
"development": {
66+
"browserTarget": "ngrx-notification:build:development"
67+
}
68+
},
69+
"defaultConfiguration": "development"
70+
},
71+
"extract-i18n": {
72+
"executor": "@angular-devkit/build-angular:extract-i18n",
73+
"options": {
74+
"browserTarget": "ngrx-notification:build"
75+
}
76+
},
77+
"lint": {
78+
"executor": "@nrwl/linter:eslint",
79+
"options": {
80+
"lintFilePatterns": [
81+
"apps/ngrx-notification/**/*.ts",
82+
"apps/ngrx-notification/**/*.html"
83+
]
84+
}
85+
}
86+
},
87+
"tags": []
88+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Component } from '@angular/core';
2+
import { StudentComponent } from './student.component';
3+
import { TeacherComponent } from './teacher.component';
4+
5+
@Component({
6+
standalone: true,
7+
imports: [TeacherComponent, StudentComponent],
8+
selector: 'app-root',
9+
template: `
10+
<teacher></teacher>
11+
<student></student>
12+
`,
13+
styles: [
14+
`
15+
:host {
16+
display: flex;
17+
gap: 20px;
18+
}
19+
`,
20+
],
21+
})
22+
export class AppComponent {}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Injectable } from '@angular/core';
2+
import { BehaviorSubject, tap, timer } from 'rxjs';
3+
import { randStudent } from '../model/student.model';
4+
import { randTeacher } from '../model/teacher.model';
5+
6+
@Injectable({ providedIn: 'root' })
7+
export class PushService {
8+
private notificationSubject = new BehaviorSubject<any>(undefined);
9+
notification$ = this.notificationSubject.asObservable();
10+
11+
init() {
12+
this.startTeacherNotification();
13+
this.startStudentNotification();
14+
}
15+
16+
private startTeacherNotification() {
17+
timer(0, 4000)
18+
.pipe(tap(() => this.notificationSubject.next(randTeacher())))
19+
.subscribe();
20+
}
21+
22+
private startStudentNotification() {
23+
timer(1000, 3000)
24+
.pipe(tap(() => this.notificationSubject.next(randStudent())))
25+
.subscribe();
26+
}
27+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Injectable } from '@angular/core';
2+
import { filter } from 'rxjs';
3+
import { PushService } from '../backend/push.service';
4+
import { Push } from '../model/push.model';
5+
import { isStudent } from '../model/student.model';
6+
import { isTeacher } from '../model/teacher.model';
7+
import { StudentStore } from './student.store';
8+
import { TeacherStore } from './teacher.store';
9+
10+
@Injectable({ providedIn: 'root' })
11+
export class NotificationService {
12+
constructor(
13+
private pushService: PushService,
14+
private teacherStore: TeacherStore,
15+
private studentStore: StudentStore
16+
) {}
17+
18+
init() {
19+
this.pushService.notification$
20+
.pipe(filter(Boolean))
21+
.subscribe((notification: Push) => {
22+
console.log(notification);
23+
if (isTeacher(notification)) {
24+
this.teacherStore.addOne(notification);
25+
}
26+
if (isStudent(notification)) {
27+
this.studentStore.addOne(notification);
28+
}
29+
});
30+
}
31+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Injectable } from '@angular/core';
2+
import { BehaviorSubject } from 'rxjs';
3+
import { Student } from '../model/student.model';
4+
5+
@Injectable({
6+
providedIn: 'root',
7+
})
8+
export class StudentStore {
9+
private students = new BehaviorSubject<Student[]>([]);
10+
students$ = this.students.asObservable();
11+
12+
addAll(students: Student[]) {
13+
this.students.next(students);
14+
}
15+
16+
addOne(student: Student) {
17+
this.students.next([...this.students.value, student]);
18+
}
19+
20+
deleteOne(id: number) {
21+
this.students.next(this.students.value.filter((s) => s.id !== id));
22+
}
23+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Injectable } from '@angular/core';
2+
import { BehaviorSubject } from 'rxjs';
3+
import { Teacher } from '../model/teacher.model';
4+
5+
@Injectable({
6+
providedIn: 'root',
7+
})
8+
export class TeacherStore {
9+
private teachers = new BehaviorSubject<Teacher[]>([]);
10+
teachers$ = this.teachers.asObservable();
11+
12+
addAll(teachers: Teacher[]) {
13+
this.teachers.next(teachers);
14+
}
15+
16+
addOne(teacher: Teacher) {
17+
this.teachers.next([...this.teachers.value, teacher]);
18+
}
19+
20+
deleteOne(id: number) {
21+
this.teachers.next(this.teachers.value.filter((t) => t.id !== id));
22+
}
23+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export type PushType = 'teacher' | 'student';
2+
3+
export interface Push {
4+
type: PushType;
5+
}

0 commit comments

Comments
 (0)