Skip to content

Commit

Permalink
feat: new @ng-web-apis/notification package (Notification API) (#123)
Browse files Browse the repository at this point in the history
  • Loading branch information
nsbarsukov authored Oct 10, 2023
1 parent 8bc8eca commit b2bdc30
Show file tree
Hide file tree
Showing 38 changed files with 1,074 additions and 3 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ jobs:
storage,
workers,
view-transition,
notification,
]
name: ${{ matrix.project }}
steps:
Expand Down Expand Up @@ -128,6 +129,11 @@ jobs:
directory: ./coverage/view-transition/
flags: summary,view-transition
name: view-transition
- uses: codecov/codecov-action@v3.1.3
with:
directory: ./coverage/notification/
flags: summary,notification
name: notification

concurrency:
group: test-${{ github.workflow }}-${{ github.ref }}
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,4 @@ testem.log
Thumbs.db

apps/demo/routesFile.txt
.ssl
20 changes: 20 additions & 0 deletions apps/demo/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@
},
"defaultConfiguration": "development"
},
"serve-ssl": {
"executor": "nx:run-commands",
"options": {
"command": "nx mkcert demo && nx serve demo --ssl --open --host 0.0.0.0 --disable-host-check"
}
},
"generate-routes-file": {
"executor": "nx:run-commands",
"options": {
Expand Down Expand Up @@ -190,6 +196,20 @@
"params": "ignore"
}
]
},
"mkcert": {
"executor": "nx:run-commands",
"options": {
"parallel": false,
"commands": [
"echo \"mkcert is a simple tool for making locally-trusted development certificates\"",
"echo \"Read about installation and more: https://github.com/FiloSottile/mkcert\"",
"echo ------",
"mkcert -install",
"mkdir -p .ssl",
"mkcert -key-file .ssl/localhost-key.pem -cert-file .ssl/localhost.pem localhost 127.0.0.1 $(ifconfig | grep \"inet \" | grep -Fv 127.0.0.1 | awk '{print $2}' | tr '\\n' ' ') ::1"
]
}
}
}
}
6 changes: 6 additions & 0 deletions apps/demo/src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ export const appRoutes: Routes = [
(await import(`./pages/view-transition/view-transition-page.module`))
.ViewTransitionPageModule,
},
{
path: DemoPath.Notification,
loadChildren: async () =>
(await import(`./pages/notification/notification-page.module`))
.NotificationPageModule,
},
{
path: '',
redirectTo: DemoPath.HomePage,
Expand Down
1 change: 1 addition & 0 deletions apps/demo/src/app/constants/demo-path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ export enum DemoPath {
StoragePage = `storage`,
WorkersPage = `workers`,
ViewTransitionPage = `view-transition`,
Notification = `notification`,
}
20 changes: 20 additions & 0 deletions apps/demo/src/app/pages/home/home-page.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -250,3 +250,23 @@ <h2>View Transition</h2>
class="icon"
/>
</a>

<a
class="link"
[routerLink]="['/', link.Notification]"
[class.not-supported]="!notificationSupport"
>
<div>
<h2>Notification</h2>
A library for declarative use of
<strong>Notification API</strong>
with Angular
</div>
<img
src="assets/logos/notification/logo.svg"
width="64"
height="64"
alt="Notification API logo"
class="icon"
/>
</a>
4 changes: 3 additions & 1 deletion apps/demo/src/app/pages/home/home-page.component.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import {ChangeDetectionStrategy, Component, Inject} from '@angular/core';
import {DemoPath} from '@demo/constants';
import {WEB_AUDIO_SUPPORT} from '@ng-web-apis/audio';
import {GEOLOCATION_SUPPORT} from '@ng-web-apis/geolocation';
import {INTERSECTION_OBSERVER_SUPPORT} from '@ng-web-apis/intersection-observer';
import {MIDI_SUPPORT} from '@ng-web-apis/midi';
import {NOTIFICATION_SUPPORT} from '@ng-web-apis/notification';
import {PAYMENT_REQUEST_SUPPORT} from '@ng-web-apis/payment-request';
import {PERMISSIONS_SUPPORT} from '@ng-web-apis/permissions';
import {RESIZE_OBSERVER_SUPPORT} from '@ng-web-apis/resize-observer';
import {DemoPath} from '@demo/constants';

@Component({
selector: `home-page`,
Expand All @@ -25,5 +26,6 @@ export class HomePageComponent {
@Inject(MIDI_SUPPORT) readonly midiSupport: boolean,
@Inject(WEB_AUDIO_SUPPORT) readonly audioSupport: boolean,
@Inject(PERMISSIONS_SUPPORT) readonly permissionsSupport: boolean,
@Inject(NOTIFICATION_SUPPORT) readonly notificationSupport: boolean,
) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<ng-container [ngSwitch]="notificationPermissionState$ | async">
<tui-badge
*ngSwitchCase="'granted'"
status="success"
value="Permission is granted"
></tui-badge>

<tui-badge
*ngSwitchCase="'denied'"
status="error"
value="Permission is denied"
></tui-badge>

<button *ngSwitchDefault tuiButton (click)="requestPermission()">
Request permission
</button>
</ng-container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {ChangeDetectionStrategy, Component} from '@angular/core';
import {NotificationService} from '@ng-web-apis/notification';
import {PermissionsService} from '@ng-web-apis/permissions';

@Component({
selector: 'notification-page-example-1',
templateUrl: './index.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NotificationPageExample1 {
readonly notificationPermissionState$ = this.permissions.state('notifications');

constructor(
private readonly notifications: NotificationService,
private readonly permissions: PermissionsService,
) {}

requestPermission(): void {
this.notifications.requestPermission().subscribe({
next: permission =>
console.info(
'Permission status:',
permission, // 'denied' | 'granted'
),
error: err =>
// e.g. 'Notification API is not supported in your browser'
console.error(err),
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<button tuiButton [disabled]="(denied$ | async)!" (click)="sendNotification()">
Send notification
</button>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {ChangeDetectionStrategy, Component} from '@angular/core';
import {NotificationService} from '@ng-web-apis/notification';
import {isDenied, isGranted, PermissionsService} from '@ng-web-apis/permissions';
import {filter, map, switchMap} from 'rxjs/operators';

@Component({
selector: 'notification-page-example-2',
templateUrl: './index.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NotificationPageExample2 {
readonly denied$ = this.permissions.state('notifications').pipe(map(isDenied));

constructor(
private readonly notifications: NotificationService,
private readonly permissions: PermissionsService,
) {}

sendNotification(): void {
this.notifications
.requestPermission()
.pipe(
filter(isGranted),
switchMap(() =>
this.notifications.open('Web APIs for Angular', {
body: 'High quality lightweight wrappers for native Web APIs for idiomatic use with Angular',
icon: 'assets/images/web-api.svg',
}),
),
)
.subscribe();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<button tuiButton [disabled]="(denied$ | async)!" (click)="sendNotification()">
Send notification
</button>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {ChangeDetectionStrategy, Component} from '@angular/core';
import {NotificationService} from '@ng-web-apis/notification';
import {isDenied, isGranted, PermissionsService} from '@ng-web-apis/permissions';
import {timer} from 'rxjs';
import {filter, map, switchMap, takeUntil} from 'rxjs/operators';

@Component({
selector: 'notification-page-example-3',
templateUrl: './index.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NotificationPageExample3 {
readonly denied$ = this.permissions.state('notifications').pipe(map(isDenied));

constructor(
private readonly notifications: NotificationService,
private readonly permissions: PermissionsService,
) {}

sendNotification(): void {
this.notifications
.requestPermission()
.pipe(
filter(isGranted),
switchMap(() =>
this.notifications.open('Close me, please!', {
requireInteraction: true,
}),
),
takeUntil(timer(5_000)), // close stream after 5 seconds
)
.subscribe({
complete: () => console.info('Notification closed!'),
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<button tuiButton [disabled]="(denied$ | async)!" (click)="sendNotification()">
Send notification
</button>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {ChangeDetectionStrategy, Component} from '@angular/core';
import {NotificationService} from '@ng-web-apis/notification';
import {isDenied, isGranted, PermissionsService} from '@ng-web-apis/permissions';
import {fromEvent} from 'rxjs';
import {filter, map, switchMap} from 'rxjs/operators';

@Component({
selector: 'notification-page-example-4',
templateUrl: './index.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NotificationPageExample4 {
readonly denied$ = this.permissions.state('notifications').pipe(map(isDenied));

constructor(
private readonly notifications: NotificationService,
private readonly permissions: PermissionsService,
) {}

sendNotification(): void {
this.notifications
.requestPermission()
.pipe(
filter(isGranted),
switchMap(() =>
this.notifications.open(`Click me, please`, {
body: `Then open console and investigate property "target"`,
requireInteraction: true,
data: `Randomly generated number: ${Math.random().toFixed(2)}`,
}),
),
switchMap(notification => fromEvent(notification, 'click')),
)
.subscribe(console.info);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {ChangeDetectionStrategy, Component} from '@angular/core';
import {PermissionsService} from '@ng-web-apis/permissions';
import {TuiDocExample} from '@taiga-ui/addon-doc';

@Component({
selector: 'notification-page',
templateUrl: './notification-page.template.html',
styleUrls: ['./notification-page.style.less'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NotificationPageComponent {
readonly notificationPermissionState$ = this.permissions.state('notifications');

readonly deniedPermissionNotification =
'You have denied notification permission. Please, change it in browser settings.';

readonly gettingPermissionExample: TuiDocExample = {
'index.ts': import('./examples/01-getting-permission/index.ts?raw'),
'index.html': import('./examples/01-getting-permission/index.html?raw'),
};

readonly createNotificationExample: TuiDocExample = {
'index.ts': import('./examples/02-create-notification/index.ts?raw'),
'index.html': import('./examples/02-create-notification/index.html?raw'),
};

readonly closeNotificationExample: TuiDocExample = {
'index.ts': import('./examples/03-close-notification/index.ts?raw'),
'index.html': import('./examples/03-close-notification/index.html?raw'),
};

readonly listenNotificationEventsExample: TuiDocExample = {
'index.ts': import('./examples/04-listen-notification-events/index.ts?raw'),
'index.html': import('./examples/04-listen-notification-events/index.html?raw'),
};

constructor(private readonly permissions: PermissionsService) {}
}
30 changes: 30 additions & 0 deletions apps/demo/src/app/pages/notification/notification-page.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {CommonModule} from '@angular/common';
import {NgModule} from '@angular/core';
import {RouterModule} from '@angular/router';
import {TuiAddonDocModule} from '@taiga-ui/addon-doc';
import {TuiButtonModule, TuiNotificationModule} from '@taiga-ui/core';
import {TuiBadgeModule} from '@taiga-ui/kit';
import {NotificationPageExample1} from './examples/01-getting-permission';
import {NotificationPageExample2} from './examples/02-create-notification';
import {NotificationPageExample3} from './examples/03-close-notification';
import {NotificationPageExample4} from './examples/04-listen-notification-events';
import {NotificationPageComponent} from './notification-page.component';

@NgModule({
imports: [
CommonModule,
TuiAddonDocModule,
TuiBadgeModule,
TuiButtonModule,
TuiNotificationModule,
RouterModule.forChild([{path: '', component: NotificationPageComponent}]),
],
declarations: [
NotificationPageComponent,
NotificationPageExample1,
NotificationPageExample2,
NotificationPageExample3,
NotificationPageExample4,
],
})
export class NotificationPageModule {}
21 changes: 21 additions & 0 deletions apps/demo/src/app/pages/notification/notification-page.style.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
:host {
display: block;
max-width: 900px;
margin: 0 auto;
font: var(--tui-font-text-m);
}

tui-notification {
margin-bottom: 1rem;
}

.header {
font: var(--tui-font-heading-4);
display: flex;
align-items: center;
gap: 1rem;
}

.description {
margin-bottom: 2rem;
}
Loading

0 comments on commit b2bdc30

Please sign in to comment.