Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: new @ng-web-apis/notification package (Notification API) #123

Merged
merged 9 commits into from
Oct 10, 2023
Merged
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/[email protected]
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) {}
}
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 {}
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