Skip to content

Commit

Permalink
feat(core): using Inject(WINDOW) and new ServiceWorker
Browse files Browse the repository at this point in the history
  • Loading branch information
xmlking committed Nov 8, 2018
1 parent 048320d commit 5062e92
Show file tree
Hide file tree
Showing 24 changed files with 1,131 additions and 297 deletions.
20 changes: 13 additions & 7 deletions PLAYBOOK.md
Expand Up @@ -86,9 +86,9 @@ for nx help `npm run help`
# create workspace Ref: https://nrwl.io/nx/guide-nx-workspace
create-nx-workspace ngx-starter-kit --prefix=ngx --npm-scope=xmlking --package-manager=npm
# or
ng new ngx-starter-kit --collection=@nrwl/schematics --prefix=ngx --verbose
ng new ngx-starter-kit --collection=@nrwl/schematics --prefix=ngx --npm-scope=xmlking --package-manager=npm --verbose
# or if you want *bazel* builds instead of *webpack*
ng new ngx-starter-kit --collection=@nrwl/schematics --prefix=ngx --bazel --verbose
ng new ngx-starter-kit --collection=@nrwl/schematics --prefix=ngx --npm-scope=xmlking --package-manager=npm --bazel --verbose
cd ngx-starter-kit

> remove all ngrx NPM pagages from package.json
Expand All @@ -106,7 +106,7 @@ ng update --all
# also run `npm outdated` and update versions in package.json then run `npm install`

# generate webapp app
ng g app webapp --routing --style=scss --prefix=ngx --unit-test-runner=jest --tags=app-module
ng g app webapp --routing --style=scss --prefix=ngx --unit-test-runner=jest --e2e-test-runner=cypress --tags=app-module --dry-run
# or with ivy renderer
ng g app webapp --routing --style=scss --prefix=ngx --unit-test-runner=jest --e2e-test-runner=cypress --tags=app-module --experimental-ivy --dry-run

Expand Down Expand Up @@ -166,6 +166,9 @@ npm i @nestjs/{common,core,microservices,swagger,websockets,typeorm,passport,el
# tslint rules
npm i -D @nestjs/testing

# add lite-server to test PWA locally
npm i -D lite-server

# Add miscellaneous
npm i ngx-perfect-scrollbar smooth-scrollbar ngx-page-scroll screenfull immutable

Expand Down Expand Up @@ -204,11 +207,13 @@ npm i -D lint-staged

> update 3rd party modules/schematics
```bash
ng update @angular/core
ng update @angular/cli
ng update @angular/core
ng update @angular/material --force
ng update @angular/pwa
ng update @ngx-formly/schematics --ui-theme=material
ng update @nrwl/schematics --force
```

#### Generate Artifacts
Expand Down Expand Up @@ -237,6 +242,7 @@ ng g service services/PageTitle --project=core --dry-run
ng g service services/ServiceWorker --project=core --dry-run
ng g service services/MediaQuery --project=core --dry-run
ng g service services/DeepLink --project=core --dry-run
ng g service services/Feature --project=core --dry-run

# `material` module to encapulate material libs which is impoted into any `Lazy-loaded Feature Modules` that need material components
ng g lib material --spec=false --tags=shared-module --unit-test-runner=jest --dry-run
Expand Down Expand Up @@ -477,10 +483,10 @@ docker-compose up web
### Serve from dist
> use this to test service-workers
```bash
# 1st terminal - Start the build
ng build --app=webapp -oh=media --watch
# 2nd terminal - Start the web server (start server on port 4200)
npx lite-server --baseDir="dist/apps/webapp"
# 1st terminal - Start the build in (optional) watch mode
ng build --prod --watch
# 2nd in terminal - Start the static web server. this will use config from bs-config.json
npx lite-server
```

### Docs
Expand Down
2 changes: 1 addition & 1 deletion angular.json
Expand Up @@ -80,7 +80,7 @@
{
"type": "bundle",
"name": "polyfills",
"baseline": "40kb",
"baseline": "45kb",
"maximumError": "20%",
"maximumWarning": "10%"
},
Expand Down
46 changes: 0 additions & 46 deletions apps/webapp/ngsw-config-sumo.json

This file was deleted.

57 changes: 47 additions & 10 deletions apps/webapp/ngsw-config.json
Expand Up @@ -7,8 +7,13 @@
"files": [
"/favicon.ico",
"/index.html",
"/silent-refresh.html",
"/*.css",
"/*.js"
],
"urls": [
"https://cdnjs.cloudflare.com/ajax/libs/trianglify/1.2.0/trianglify.min.js",
"https://www.google-analytics.com/analytics.js"
]
}
}, {
Expand All @@ -18,17 +23,49 @@
"resources": {
"files": [
"/assets/**"
]
}
}, {
"name": "static-assets",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
],
"urls": [
"https://fonts.googleapis.com/icon?family=Material+Icons",
"https://fonts.googleapis.com/css?family=Roboto:300,400,500|Roboto+Mono:300|Dancing+Script:700"
"https://fonts.googleapis.com/**",
"https://fonts.gstatic.com/**"
]
}
}]
}],
"dataGroups": [
{
"name": "my-api",
"version": 1,
"urls": [
"http://localhost:3000/api/**"
],
"cacheConfig": {
"strategy": "freshness",
"maxSize": 100,
"maxAge": "12h",
"timeout": "2s"
}
},
{
"name": "random-user",
"urls": [
"https://randomuser.me/api/*"
],
"cacheConfig": {
"strategy": "freshness",
"maxSize": 100,
"maxAge": "1d",
"timeout": "5s"
}
},
{
"name": "keycloak",
"urls": [
"https://myroute-is360.a3c1.starter-us-west-1.openshiftapps.com/auth/realms/is360/**"
],
"cacheConfig": {
"strategy": "performance",
"maxSize": 100,
"maxAge": "90d"
}
}
]
}
4 changes: 3 additions & 1 deletion bs-config.json
@@ -1,3 +1,5 @@
{
"port": 4200
"port": 4200,
"files": ["dist/apps/webapp/*.{html,htm,css,js,json,png}"],
"server": { "baseDir": "dist/apps/webapp" }
}
9 changes: 5 additions & 4 deletions libs/chat-box/src/lib/services/speech-to-text.service.ts
@@ -1,4 +1,5 @@
import { Injectable } from '@angular/core';
import { Inject, Injectable } from '@angular/core';
import { BrowserFeatureKey, FeatureService, WINDOW } from '@ngx-starter-kit/core';

const SpeechRecognition =
(window as any).webkitSpeechRecognition ||
Expand All @@ -11,9 +12,9 @@ const SpeechRecognition =
export class SpeechToTextService {
public canUseSpeechRecognition = false;
private speechRecognition: any;
constructor() {
if (SpeechRecognition !== undefined) {
this.canUseSpeechRecognition = true;
constructor(private readonly featureService: FeatureService, @Inject(WINDOW) private window: Window) {
this.canUseSpeechRecognition = this.featureService.detectFeature(BrowserFeatureKey.SpeechRecognition).supported;
if (this.canUseSpeechRecognition) {
this.speechRecognition = new SpeechRecognition();
this.speechRecognition.continuous = false; // FIXME: Gecko?
this.speechRecognition.lang = 'en-US';
Expand Down
9 changes: 5 additions & 4 deletions libs/chat-box/src/lib/services/text-to-speech.service.ts
@@ -1,13 +1,14 @@
import { Injectable } from '@angular/core';
import { Inject, Injectable } from '@angular/core';
import { SynthesisVoice } from '../state/chat-box.actions';
import { BrowserFeatureKey, FeatureService, WINDOW } from '@ngx-starter-kit/core';

@Injectable()
export class TextToSpeechService {
public canUseSpeechSynthesis = false;
private speechSynthesis: SpeechSynthesis;
constructor() {
if ('speechSynthesis' in window) {
this.canUseSpeechSynthesis = true;
constructor(private readonly featureService: FeatureService, @Inject(WINDOW) private window: Window) {
this.canUseSpeechSynthesis = this.featureService.detectFeature(BrowserFeatureKey.SpeechSynthesis).supported;
if (this.canUseSpeechSynthesis) {
this.speechSynthesis = (window as any).speechSynthesis;
}
}
Expand Down
2 changes: 2 additions & 0 deletions libs/core/src/index.ts
Expand Up @@ -4,3 +4,5 @@ export { ServiceWorkerService } from './lib/services/service-worker.service';
export { MediaQueryService } from './lib/services/media-query.service';
export { DeepLinkService } from './lib/services/deep-link.service';
export { RouterStateData} from './lib/state/custom-router-state.serializer';
export { FeatureService, BrowserFeatureKey, BrowserFeature } from './lib/services/feature.service';
export { WINDOW } from './lib/services/window.token';
4 changes: 4 additions & 0 deletions libs/core/src/lib/core.module.ts
Expand Up @@ -23,6 +23,8 @@ import { InMemoryDataService } from './services/in-memory-data.service';
import { ErrorInterceptor } from './interceptors/error.interceptor';
import { JwtInterceptor } from './interceptors/jwt.interceptor';
import { CustomRouterStateSerializer } from './state/custom-router-state.serializer';
import { WINDOW, _window } from './services/window.token';
import {MatSnackBarModule} from '@angular/material/snack-bar';

// Noop handler for factory function
export function noop() {
Expand All @@ -41,6 +43,7 @@ library.add(faTwitter, faGithub, faGoogle);
CommonModule,
HttpClientModule,
FontAwesomeModule,
MatSnackBarModule,
NgxPageScrollModule,
NavigatorModule.forRoot(defaultMenu),
NgxsModule.forRoot([AuthState, MenuState, PreferenceState]),
Expand Down Expand Up @@ -84,6 +87,7 @@ library.add(faTwitter, faGithub, faGoogle);
provide: RouterStateSerializer,
useClass: CustomRouterStateSerializer,
},
{ provide: WINDOW, useFactory: _window },
],
})
export class CoreModule {
Expand Down
12 changes: 12 additions & 0 deletions libs/core/src/lib/services/feature.service.spec.ts
@@ -0,0 +1,12 @@
import { TestBed } from '@angular/core/testing';

import { FeatureService } from './feature.service';

describe('FeatureService', () => {
beforeEach(() => TestBed.configureTestingModule({}));

it('should be created', () => {
const service: FeatureService = TestBed.get(FeatureService);
expect(service).toBeTruthy();
});
});
80 changes: 80 additions & 0 deletions libs/core/src/lib/services/feature.service.ts
@@ -0,0 +1,80 @@
import { Inject, Injectable } from '@angular/core';
import { WINDOW } from './window.token';

export enum BrowserFeatureKey {
ServiceWorker = 'Service Worker',
Cache = 'Offline Capabilities',
PushAPI = 'Push data',
NotificationsAPI = 'Notifications',
BeforeInstallPromptEvent = 'Add to Homescreen',
BackgroundSync = 'Background sync',
NavigationPreloadManager = 'Navigation Preload',
BudgetAPI = 'Budget API',
StorageEstimation = 'Storage Estimation',
PersistentStorage = 'Persistent Storage',
WebShareAPI = 'Web Share',
MediaSessionAPI = 'Media Session',
MediaCapabilities = 'Media Capabilities',
DeviceMemory = 'Device Memory',
RelatedApps = 'Getting Installed Related Apps',
PaymentRequestAPI = 'Payment Request',
CredentialManagement = 'Credential Management',
WebBluetoothAPI = 'Web Bluetooth',
SpeechSynthesis = 'Speech Synthesis',
SpeechRecognition = 'Speech Recognition',
}

export class BrowserFeature {
constructor(public key: string, public supported: boolean) {}
}

@Injectable({
providedIn: 'root',
})
export class FeatureService {
private readonly navigator: Navigator;
private readonly features: any;

constructor(@Inject(WINDOW) private window: Window) {
this.navigator = this.window.navigator;

this.features = {
[BrowserFeatureKey.ServiceWorker]: 'serviceWorker' in this.window.navigator,
[BrowserFeatureKey.Cache]: 'caches' in this.window,
[BrowserFeatureKey.PushAPI]: 'PushManager' in this.window,
[BrowserFeatureKey.NotificationsAPI]: 'Notification' in this.window,
[BrowserFeatureKey.BeforeInstallPromptEvent]: 'BeforeInstallPromptEvent' in this.window,
[BrowserFeatureKey.BackgroundSync]: 'SyncManager' in this.window,
[BrowserFeatureKey.NavigationPreloadManager]: 'NavigationPreloadManager' in this.window,
[BrowserFeatureKey.BudgetAPI]: 'budget' in this.navigator && 'reserve' in (<any>this.navigator).budget,
[BrowserFeatureKey.StorageEstimation]: 'storage' in this.navigator && 'estimate' in (<any>this.navigator).storage,
[BrowserFeatureKey.PersistentStorage]: 'storage' in this.navigator && 'persist' in (<any>this.navigator).storage,
[BrowserFeatureKey.WebShareAPI]: 'share' in this.navigator,
[BrowserFeatureKey.MediaSessionAPI]: 'mediaSession' in this.navigator,
[BrowserFeatureKey.MediaCapabilities]: 'mediaCapabilities' in this.navigator,
[BrowserFeatureKey.DeviceMemory]: 'deviceMemory' in this.navigator,
[BrowserFeatureKey.RelatedApps]: 'getInstalledRelatedApps' in this.navigator,
[BrowserFeatureKey.PaymentRequestAPI]: 'PaymentRequest' in this.window,
[BrowserFeatureKey.CredentialManagement]: 'credentials' in this.navigator,
[BrowserFeatureKey.WebBluetoothAPI]: 'bluetooth' in this.navigator,
[BrowserFeatureKey.SpeechSynthesis]: 'speechSynthesis' in this.window,
[BrowserFeatureKey.SpeechRecognition]:
'webkitSpeechRecognition' in this.window ||
'mozSpeechRecognition' in this.window ||
'msSpeechRecognition' in this.window ||
'oSpeechRecognition' in this.window,
};
}

detectFeatures(): Array<BrowserFeature> {
return Object.keys(this.features).map(key => new BrowserFeature(key, this.features[key]));
}

detectFeature(feature: BrowserFeatureKey): BrowserFeature {
return new BrowserFeature(feature, this.features[feature]);
}

isMobileAndroid(): boolean {
return this.window.navigator.userAgent.toLowerCase().indexOf('android') > -1;
}
}

0 comments on commit 5062e92

Please sign in to comment.