diff --git a/aio/scripts/_payload-limits.json b/aio/scripts/_payload-limits.json index 21f23edd38..b56b49d541 100755 --- a/aio/scripts/_payload-limits.json +++ b/aio/scripts/_payload-limits.json @@ -3,7 +3,7 @@ "master": { "uncompressed": { "inline": 1971, - "main": 595662, + "main": 584136, "polyfills": 40272, "prettify": 14888 } diff --git a/aio/src/app/app.component.spec.ts b/aio/src/app/app.component.spec.ts index ffc043d55b..3d35743cdd 100644 --- a/aio/src/app/app.component.spec.ts +++ b/aio/src/app/app.component.spec.ts @@ -7,7 +7,7 @@ import { MatProgressBar, MatSidenav } from '@angular/material'; import { By } from '@angular/platform-browser'; import { Observable, timer } from 'rxjs'; -import 'rxjs/add/operator/mapTo'; +import { mapTo } from 'rxjs/operators'; import { AppComponent } from './app.component'; import { AppModule } from './app.module'; @@ -1371,6 +1371,6 @@ class TestHttpClient { } // Preserve async nature of `HttpClient`. - return timer(1).mapTo(data); + return timer(1).pipe(mapTo(data)); } } diff --git a/aio/src/app/app.component.ts b/aio/src/app/app.component.ts index ae0b30e72a..24344af74a 100644 --- a/aio/src/app/app.component.ts +++ b/aio/src/app/app.component.ts @@ -14,7 +14,7 @@ import { SearchService } from 'app/search/search.service'; import { TocService } from 'app/shared/toc.service'; import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; -import 'rxjs/add/operator/first'; +import { first, map } from 'rxjs/operators'; const sideNavView = 'SideNav'; @@ -143,7 +143,7 @@ export class AppComponent implements OnInit { // Compute the version picker list from the current version and the versions in the navigation map combineLatest( this.navigationService.versionInfo, - this.navigationService.navigationViews.map(views => views['docVersions'])) + this.navigationService.navigationViews.pipe(map(views => views['docVersions']))) .subscribe(([versionInfo, versions]) => { // TODO(pbd): consider whether we can lookup the stable and next versions from the internet const computedVersions: NavigationNode[] = [ @@ -171,7 +171,7 @@ export class AppComponent implements OnInit { this.navigationService.versionInfo.subscribe(vi => this.versionInfo = vi); - const hasNonEmptyToc = this.tocService.tocList.map(tocList => tocList.length > 0); + const hasNonEmptyToc = this.tocService.tocList.pipe(map(tocList => tocList.length > 0)); combineLatest(hasNonEmptyToc, this.showFloatingToc) .subscribe(([hasToc, showFloatingToc]) => this.hasFloatingToc = hasToc && showFloatingToc); @@ -183,7 +183,7 @@ export class AppComponent implements OnInit { combineLatest( this.documentService.currentDocument, // ...needed to determine host classes this.navigationService.currentNodes) // ...needed to determine `sidenav` state - .first() + .pipe(first()) .subscribe(() => this.updateShell()); } diff --git a/aio/src/app/custom-elements/announcement-bar/announcement-bar.component.ts b/aio/src/app/custom-elements/announcement-bar/announcement-bar.component.ts index abd88d0b2d..6698ec19bb 100644 --- a/aio/src/app/custom-elements/announcement-bar/announcement-bar.component.ts +++ b/aio/src/app/custom-elements/announcement-bar/announcement-bar.component.ts @@ -1,5 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { HttpClient } from '@angular/common/http'; +import { catchError, map } from 'rxjs/operators'; import { Logger } from 'app/shared/logger.service'; import { CONTENT_URL_PREFIX } from 'app/documents/document.service'; const announcementsPath = CONTENT_URL_PREFIX + 'announcements.json'; @@ -58,15 +59,17 @@ export class AnnouncementBarComponent implements OnInit { ngOnInit() { this.http.get(announcementsPath) - .catch(error => { - this.logger.error(new Error(`${announcementsPath} request failed: ${error.message}`)); - return []; - }) - .map(announcements => this.findCurrentAnnouncement(announcements)) - .catch(error => { - this.logger.error(new Error(`${announcementsPath} contains invalid data: ${error.message}`)); - return []; - }) + .pipe( + catchError(error => { + this.logger.error(new Error(`${announcementsPath} request failed: ${error.message}`)); + return []; + }), + map(announcements => this.findCurrentAnnouncement(announcements)), + catchError(error => { + this.logger.error(new Error(`${announcementsPath} contains invalid data: ${error.message}`)); + return []; + }), + ) .subscribe(announcement => this.announcement = announcement); } diff --git a/aio/src/app/custom-elements/api/api.service.ts b/aio/src/app/custom-elements/api/api.service.ts index b982ca53bc..6d9ae4926b 100644 --- a/aio/src/app/custom-elements/api/api.service.ts +++ b/aio/src/app/custom-elements/api/api.service.ts @@ -2,9 +2,7 @@ import { Injectable, OnDestroy } from '@angular/core'; import { HttpClient, HttpErrorResponse } from '@angular/common/http'; import { ReplaySubject, Subject } from 'rxjs'; -import 'rxjs/add/operator/do'; -import 'rxjs/add/operator/map'; -import 'rxjs/add/operator/takeUntil'; +import { takeUntil, tap } from 'rxjs/operators'; import { Logger } from 'app/shared/logger.service'; import { DOC_CONTENT_URL_PREFIX } from 'app/documents/document.service'; @@ -34,7 +32,7 @@ export class ApiService implements OnDestroy { private firstTime = true; private onDestroy = new Subject(); private sectionsSubject = new ReplaySubject(1); - private _sections = this.sectionsSubject.takeUntil(this.onDestroy); + private _sections = this.sectionsSubject.pipe(takeUntil(this.onDestroy)); /** * Return a cached observable of API sections from a JSON file. @@ -70,8 +68,10 @@ export class ApiService implements OnDestroy { // TODO: get URL by configuration? const url = this.apiBase + (src || this.apiListJsonDefault); this.http.get(url) - .takeUntil(this.onDestroy) - .do(() => this.logger.log(`Got API sections from ${url}`)) + .pipe( + takeUntil(this.onDestroy), + tap(() => this.logger.log(`Got API sections from ${url}`)), + ) .subscribe( sections => this.sectionsSubject.next(sections), (err: HttpErrorResponse) => { diff --git a/aio/src/app/custom-elements/code/code.component.spec.ts b/aio/src/app/custom-elements/code/code.component.spec.ts index 423ccd2593..68eaa80d00 100644 --- a/aio/src/app/custom-elements/code/code.component.spec.ts +++ b/aio/src/app/custom-elements/code/code.component.spec.ts @@ -3,8 +3,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { MatSnackBar } from '@angular/material'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import 'rxjs/add/operator/first'; -import 'rxjs/add/operator/toPromise'; +import { first } from 'rxjs/operators'; import { CodeComponent } from './code.component'; import { CodeModule } from './code.module'; @@ -65,7 +64,7 @@ describe('CodeComponent', () => { describe('pretty printing', () => { const untilCodeFormatted = () => { const emitter = hostComponent.codeComponent.codeFormatted; - return emitter.first().toPromise(); + return emitter.pipe(first()).toPromise(); }; const hasLineNumbers = async () => { // presence of `
  • `s are a tell-tale for line numbers diff --git a/aio/src/app/custom-elements/code/code.component.ts b/aio/src/app/custom-elements/code/code.component.ts index 5324fd37aa..534f93fdd5 100644 --- a/aio/src/app/custom-elements/code/code.component.ts +++ b/aio/src/app/custom-elements/code/code.component.ts @@ -3,7 +3,7 @@ import { Logger } from 'app/shared/logger.service'; import { PrettyPrinter } from './pretty-printer.service'; import { CopierService } from 'app/shared/copier.service'; import { MatSnackBar } from '@angular/material/snack-bar'; -import 'rxjs/add/operator/do'; +import { tap } from 'rxjs/operators'; /** * If linenums is not set, this is the default maximum number of lines that @@ -120,7 +120,7 @@ export class CodeComponent implements OnChanges { this.pretty .formatCode(leftAlignedCode, this.language, this.getLinenums(leftAlignedCode)) - .do(() => this.codeFormatted.emit()) + .pipe(tap(() => this.codeFormatted.emit())) .subscribe(c => this.setCodeHtml(c), err => { /* ignore failure to format */ } ); } diff --git a/aio/src/app/custom-elements/code/pretty-printer.service.ts b/aio/src/app/custom-elements/code/pretty-printer.service.ts index 0f1f60f370..d56a3a27fc 100644 --- a/aio/src/app/custom-elements/code/pretty-printer.service.ts +++ b/aio/src/app/custom-elements/code/pretty-printer.service.ts @@ -1,8 +1,7 @@ import { Injectable } from '@angular/core'; import { from as fromPromise, Observable } from 'rxjs'; -import 'rxjs/add/operator/map'; -import 'rxjs/add/operator/first'; +import { first, map, share } from 'rxjs/operators'; import { Logger } from 'app/shared/logger.service'; @@ -21,7 +20,7 @@ export class PrettyPrinter { private prettyPrintOne: Observable; constructor(private logger: Logger) { - this.prettyPrintOne = fromPromise(this.getPrettyPrintOne()).share(); + this.prettyPrintOne = fromPromise(this.getPrettyPrintOne()).pipe(share()); } private getPrettyPrintOne(): Promise { @@ -50,15 +49,17 @@ export class PrettyPrinter { * @returns Observable - Observable of formatted code */ formatCode(code: string, language?: string, linenums?: number | boolean) { - return this.prettyPrintOne.map(ppo => { - try { - return ppo(code, language, linenums); - } catch (err) { - const msg = `Could not format code that begins '${code.substr(0, 50)}...'.`; - console.error(msg, err); - throw new Error(msg); - } - }) - .first(); // complete immediately + return this.prettyPrintOne.pipe( + map(ppo => { + try { + return ppo(code, language, linenums); + } catch (err) { + const msg = `Could not format code that begins '${code.substr(0, 50)}...'.`; + console.error(msg, err); + throw new Error(msg); + } + }), + first(), // complete immediately + ); } } diff --git a/aio/src/app/custom-elements/contributor/contributor.service.ts b/aio/src/app/custom-elements/contributor/contributor.service.ts index c84067450d..9cc29c6f47 100644 --- a/aio/src/app/custom-elements/contributor/contributor.service.ts +++ b/aio/src/app/custom-elements/contributor/contributor.service.ts @@ -1,9 +1,8 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; -import { Observable } from 'rxjs'; -import 'rxjs/add/operator/map'; -import 'rxjs/add/operator/publishLast'; +import { ConnectableObservable, Observable } from 'rxjs'; +import { map, publishLast } from 'rxjs/operators'; import { Contributor, ContributorGroup } from './contributors.model'; @@ -22,9 +21,9 @@ export class ContributorService { } private getContributors() { - const contributors = this.http.get<{[key: string]: Contributor}>(contributorsPath) + const contributors = this.http.get<{[key: string]: Contributor}>(contributorsPath).pipe( // Create group map - .map(contribs => { + map(contribs => { const contribMap: { [name: string]: Contributor[]} = {}; Object.keys(contribs).forEach(key => { const contributor = contribs[key]; @@ -38,10 +37,10 @@ export class ContributorService { }); return contribMap; - }) + }), // Flatten group map into sorted group array of sorted contributors - .map(cmap => { + map(cmap => { return Object.keys(cmap).map(key => { const order = knownGroups.indexOf(key); return { @@ -51,10 +50,12 @@ export class ContributorService { } as ContributorGroup; }) .sort(compareGroups); - }) - .publishLast(); + }), - contributors.connect(); + publishLast(), + ); + + (contributors as ConnectableObservable).connect(); return contributors; } } diff --git a/aio/src/app/custom-elements/resource/resource.service.ts b/aio/src/app/custom-elements/resource/resource.service.ts index d372cf4a8c..9fc921e092 100644 --- a/aio/src/app/custom-elements/resource/resource.service.ts +++ b/aio/src/app/custom-elements/resource/resource.service.ts @@ -1,9 +1,8 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; -import { Observable } from 'rxjs'; -import 'rxjs/add/operator/map'; -import 'rxjs/add/operator/publishLast'; +import { ConnectableObservable, Observable } from 'rxjs'; +import { map, publishLast } from 'rxjs/operators'; import { Category, Resource, SubCategory } from './resource.model'; import { CONTENT_URL_PREFIX } from 'app/documents/document.service'; @@ -20,11 +19,12 @@ export class ResourceService { private getCategories(): Observable { - const categories = this.http.get(resourcesPath) - .map(data => mkCategories(data)) - .publishLast(); + const categories = this.http.get(resourcesPath).pipe( + map(data => mkCategories(data)), + publishLast(), + ); - categories.connect(); + (categories as ConnectableObservable).connect(); return categories; }; } diff --git a/aio/src/app/custom-elements/search/file-not-found-search.component.ts b/aio/src/app/custom-elements/search/file-not-found-search.component.ts index b4b62ccdff..e8af858b49 100644 --- a/aio/src/app/custom-elements/search/file-not-found-search.component.ts +++ b/aio/src/app/custom-elements/search/file-not-found-search.component.ts @@ -1,5 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; +import { switchMap } from 'rxjs/operators'; import { LocationService } from 'app/shared/location.service'; import { SearchResults } from 'app/search/interfaces'; import { SearchService } from 'app/search/search.service'; @@ -15,9 +16,9 @@ export class FileNotFoundSearchComponent implements OnInit { constructor(private location: LocationService, private search: SearchService) {} ngOnInit() { - this.searchResults = this.location.currentPath.switchMap(path => { + this.searchResults = this.location.currentPath.pipe(switchMap(path => { const query = path.split(/\W+/).join(' '); return this.search.search(query); - }); + })); } } diff --git a/aio/src/app/documents/document.service.ts b/aio/src/app/documents/document.service.ts index 23536502d1..432e1c4ab2 100644 --- a/aio/src/app/documents/document.service.ts +++ b/aio/src/app/documents/document.service.ts @@ -2,8 +2,7 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpErrorResponse } from '@angular/common/http'; import { AsyncSubject, Observable, of } from 'rxjs'; -import 'rxjs/add/operator/catch'; -import 'rxjs/add/operator/switchMap'; +import { catchError, switchMap, tap } from 'rxjs/operators'; import { DocumentContents } from './document-contents'; export { DocumentContents } from './document-contents'; @@ -41,7 +40,7 @@ export class DocumentService { private http: HttpClient, location: LocationService) { // Whenever the URL changes we try to get the appropriate doc - this.currentDocument = location.currentPath.switchMap(path => this.getDocument(path)); + this.currentDocument = location.currentPath.pipe(switchMap(path => this.getDocument(path))); } private getDocument(url: string) { @@ -60,15 +59,17 @@ export class DocumentService { this.logger.log('fetching document from', requestPath); this.http .get(requestPath, {responseType: 'json'}) - .do(data => { - if (!data || typeof data !== 'object') { - this.logger.log('received invalid data:', data); - throw Error('Invalid data'); - } - }) - .catch((error: HttpErrorResponse) => { - return error.status === 404 ? this.getFileNotFoundDoc(id) : this.getErrorDoc(id, error); - }) + .pipe( + tap(data => { + if (!data || typeof data !== 'object') { + this.logger.log('received invalid data:', data); + throw Error('Invalid data'); + } + }), + catchError((error: HttpErrorResponse) => { + return error.status === 404 ? this.getFileNotFoundDoc(id) : this.getErrorDoc(id, error); + }), + ) .subscribe(subject); return subject.asObservable(); @@ -90,7 +91,7 @@ export class DocumentService { private getErrorDoc(id: string, error: HttpErrorResponse): Observable { this.logger.error(new Error(`Error fetching document '${id}': (${error.message})`)); this.cache.delete(id); - return Observable.of({ + return of({ id: FETCHING_ERROR_ID, contents: FETCHING_ERROR_CONTENTS }); diff --git a/aio/src/app/layout/doc-viewer/doc-viewer.component.ts b/aio/src/app/layout/doc-viewer/doc-viewer.component.ts index 855fdc7dae..f9766085dc 100644 --- a/aio/src/app/layout/doc-viewer/doc-viewer.component.ts +++ b/aio/src/app/layout/doc-viewer/doc-viewer.component.ts @@ -2,10 +2,7 @@ import { Component, ElementRef, EventEmitter, Input, OnDestroy, Output } from '@ import { Title, Meta } from '@angular/platform-browser'; import { Observable, of, timer } from 'rxjs'; -import 'rxjs/add/operator/catch'; -import 'rxjs/add/operator/do'; -import 'rxjs/add/operator/switchMap'; -import 'rxjs/add/operator/takeUntil'; +import { catchError, switchMap, takeUntil, tap } from 'rxjs/operators'; import { DocumentContents, FILE_NOT_FOUND_ID, FETCHING_ERROR_ID } from 'app/documents/document.service'; import { Logger } from 'app/shared/logger.service'; @@ -80,8 +77,10 @@ export class DocViewerComponent implements OnDestroy { } this.docContents$ - .switchMap(newDoc => this.render(newDoc)) - .takeUntil(this.onDestroy$) + .pipe( + switchMap(newDoc => this.render(newDoc)), + takeUntil(this.onDestroy$), + ) .subscribe(); } @@ -132,22 +131,23 @@ export class DocViewerComponent implements OnDestroy { this.setNoIndex(doc.id === FILE_NOT_FOUND_ID || doc.id === FETCHING_ERROR_ID); - return this.void$ + return this.void$.pipe( // Security: `doc.contents` is always authored by the documentation team // and is considered to be safe. - .do(() => this.nextViewContainer.innerHTML = doc.contents || '') - .do(() => addTitleAndToc = this.prepareTitleAndToc(this.nextViewContainer, doc.id)) - .switchMap(() => this.elementsLoader.loadContainingCustomElements(this.nextViewContainer)) - .do(() => this.docReady.emit()) - .switchMap(() => this.swapViews(addTitleAndToc)) - .do(() => this.docRendered.emit()) - .catch(err => { + tap(() => this.nextViewContainer.innerHTML = doc.contents || ''), + tap(() => addTitleAndToc = this.prepareTitleAndToc(this.nextViewContainer, doc.id)), + switchMap(() => this.elementsLoader.loadContainingCustomElements(this.nextViewContainer)), + tap(() => this.docReady.emit()), + switchMap(() => this.swapViews(addTitleAndToc)), + tap(() => this.docRendered.emit()), + catchError(err => { const errorMessage = (err instanceof Error) ? err.stack : err; this.logger.error(new Error(`[DocViewer] Error preparing document '${doc.id}': ${errorMessage}`)); this.nextViewContainer.innerHTML = ''; this.setNoIndex(true); return this.void$; - }); + }), + ); } /** @@ -199,16 +199,17 @@ export class DocViewerComponent implements OnDestroy { } elem.style.transition = ''; return animationsDisabled - ? this.void$.do(() => elem.style[prop] = to) - : this.void$ + ? this.void$.pipe(tap(() => elem.style[prop] = to)) + : this.void$.pipe( // In order to ensure that the `from` value will be applied immediately (i.e. // without transition) and that the `to` value will be affected by the // `transition` style, we need to ensure an animation frame has passed between // setting each style. - .switchMap(() => raf$).do(() => elem.style[prop] = from) - .switchMap(() => raf$).do(() => elem.style.transition = `all ${duration}ms ease-in-out`) - .switchMap(() => raf$).do(() => (elem.style as any)[prop] = to) - .switchMap(() => timer(getActualDuration(elem))).switchMap(() => this.void$); + switchMap(() => raf$), tap(() => elem.style[prop] = from), + switchMap(() => raf$), tap(() => elem.style.transition = `all ${duration}ms ease-in-out`), + switchMap(() => raf$), tap(() => (elem.style as any)[prop] = to), + switchMap(() => timer(getActualDuration(elem))), switchMap(() => this.void$), + ); }; const animateLeave = (elem: HTMLElement) => animateProp(elem, 'opacity', '1', '0.1'); @@ -217,25 +218,27 @@ export class DocViewerComponent implements OnDestroy { let done$ = this.void$; if (this.currViewContainer.parentElement) { - done$ = done$ + done$ = done$.pipe( // Remove the current view from the viewer. - .switchMap(() => animateLeave(this.currViewContainer)) - .do(() => this.currViewContainer.parentElement!.removeChild(this.currViewContainer)) - .do(() => this.docRemoved.emit()); + switchMap(() => animateLeave(this.currViewContainer)), + tap(() => this.currViewContainer.parentElement!.removeChild(this.currViewContainer)), + tap(() => this.docRemoved.emit()), + ); } - return done$ + return done$.pipe( // Insert the next view into the viewer. - .do(() => this.hostElement.appendChild(this.nextViewContainer)) - .do(() => onInsertedCb()) - .do(() => this.docInserted.emit()) - .switchMap(() => animateEnter(this.nextViewContainer)) + tap(() => this.hostElement.appendChild(this.nextViewContainer)), + tap(() => onInsertedCb()), + tap(() => this.docInserted.emit()), + switchMap(() => animateEnter(this.nextViewContainer)), // Update the view references and clean up unused nodes. - .do(() => { + tap(() => { const prevViewContainer = this.currViewContainer; this.currViewContainer = this.nextViewContainer; this.nextViewContainer = prevViewContainer; this.nextViewContainer.innerHTML = ''; // Empty to release memory. - }); + }), + ); } } diff --git a/aio/src/app/layout/toc/toc.component.ts b/aio/src/app/layout/toc/toc.component.ts index 9e05d8c76c..9be72171c0 100644 --- a/aio/src/app/layout/toc/toc.component.ts +++ b/aio/src/app/layout/toc/toc.component.ts @@ -1,8 +1,6 @@ import { AfterViewInit, Component, ElementRef, OnDestroy, OnInit, QueryList, ViewChildren } from '@angular/core'; -import { asapScheduler as asap, Observable, Subject } from 'rxjs'; -import 'rxjs/add/observable/combineLatest'; -import 'rxjs/add/operator/subscribeOn'; -import 'rxjs/add/operator/takeUntil'; +import { asapScheduler as asap, combineLatest, Subject } from 'rxjs'; +import { startWith, subscribeOn, takeUntil } from 'rxjs/operators'; import { ScrollService } from 'app/shared/scroll.service'; import { TocItem, TocService } from 'app/shared/toc.service'; @@ -34,7 +32,7 @@ export class TocComponent implements OnInit, AfterViewInit, OnDestroy { ngOnInit() { this.tocService.tocList - .takeUntil(this.onDestroy) + .pipe(takeUntil(this.onDestroy)) .subscribe(tocList => { this.tocList = tocList; const itemCount = count(this.tocList, item => item.level !== 'h1'); @@ -54,8 +52,8 @@ export class TocComponent implements OnInit, AfterViewInit, OnDestroy { // We use the `asap` scheduler because updates to `activeItemIndex` are triggered by DOM changes, // which, in turn, are caused by the rendering that happened due to a ChangeDetection. // Without asap, we would be updating the model while still in a ChangeDetection handler, which is disallowed by Angular. - Observable.combineLatest(this.tocService.activeItemIndex.subscribeOn(asap), this.items.changes.startWith(this.items)) - .takeUntil(this.onDestroy) + combineLatest(this.tocService.activeItemIndex.pipe(subscribeOn(asap)), this.items.changes.pipe(startWith(this.items))) + .pipe(takeUntil(this.onDestroy)) .subscribe(([index, items]) => { this.activeIndex = index; if (index === null || index >= items.length) { diff --git a/aio/src/app/navigation/navigation.service.ts b/aio/src/app/navigation/navigation.service.ts index c2e9fe62ce..dbee4e367c 100644 --- a/aio/src/app/navigation/navigation.service.ts +++ b/aio/src/app/navigation/navigation.service.ts @@ -1,10 +1,8 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; -import { combineLatest, Observable } from 'rxjs'; -import 'rxjs/add/operator/map'; -import 'rxjs/add/operator/publishLast'; -import 'rxjs/add/operator/publishReplay'; +import { combineLatest, ConnectableObservable, Observable } from 'rxjs'; +import { map, publishLast, publishReplay } from 'rxjs/operators'; import { LocationService } from 'app/shared/location.service'; import { CONTENT_URL_PREFIX } from 'app/documents/document.service'; @@ -56,30 +54,32 @@ export class NavigationService { */ private fetchNavigationInfo(): Observable { const navigationInfo = this.http.get(navigationPath) - .publishLast(); - navigationInfo.connect(); + .pipe(publishLast()); + (navigationInfo as ConnectableObservable).connect(); return navigationInfo; } private getVersionInfo(navigationInfo: Observable) { - const versionInfo = navigationInfo - .map(response => response.__versionInfo) - .publishLast(); - versionInfo.connect(); + const versionInfo = navigationInfo.pipe( + map(response => response.__versionInfo), + publishLast(), + ); + (versionInfo as ConnectableObservable).connect(); return versionInfo; } private getNavigationViews(navigationInfo: Observable): Observable { - const navigationViews = navigationInfo - .map(response => { + const navigationViews = navigationInfo.pipe( + map(response => { const views = Object.assign({}, response); Object.keys(views).forEach(key => { if (key[0] === '_') { delete views[key]; } }); return views as NavigationViews; - }) - .publishLast(); - navigationViews.connect(); + }), + publishLast(), + ); + (navigationViews as ConnectableObservable).connect(); return navigationViews; } @@ -91,15 +91,15 @@ export class NavigationService { */ private getCurrentNodes(navigationViews: Observable): Observable { const currentNodes = combineLatest( - navigationViews.map(views => this.computeUrlToNavNodesMap(views)), + navigationViews.pipe(map(views => this.computeUrlToNavNodesMap(views))), this.location.currentPath, (navMap, url) => { const urlKey = url.startsWith('api/') ? 'api' : url; return navMap.get(urlKey) || { '' : { view: '', url: urlKey, nodes: [] }}; }) - .publishReplay(1); - currentNodes.connect(); + .pipe(publishReplay(1)); + (currentNodes as ConnectableObservable).connect(); return currentNodes; } diff --git a/aio/src/app/search/search-box/search-box.component.ts b/aio/src/app/search/search-box/search-box.component.ts index 27f6f385d5..af50c6fd58 100644 --- a/aio/src/app/search/search-box/search-box.component.ts +++ b/aio/src/app/search/search-box/search-box.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit, ViewChild, ElementRef, EventEmitter, Output } from '@angular/core'; import { LocationService } from 'app/shared/location.service'; import { Subject } from 'rxjs'; -import 'rxjs/add/operator/distinctUntilChanged'; +import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; /** * This component provides a text box to type a search query that will be sent to the SearchService. @@ -30,7 +30,7 @@ export class SearchBoxComponent implements OnInit { private searchSubject = new Subject(); @ViewChild('searchBox') searchBox: ElementRef; - @Output() onSearch = this.searchSubject.distinctUntilChanged().debounceTime(this.searchDebounce); + @Output() onSearch = this.searchSubject.pipe(distinctUntilChanged(), debounceTime(this.searchDebounce)); @Output() onFocus = new EventEmitter(); constructor(private locationService: LocationService) { } diff --git a/aio/src/app/search/search.service.spec.ts b/aio/src/app/search/search.service.spec.ts index 507b010564..27ededb7bf 100644 --- a/aio/src/app/search/search.service.spec.ts +++ b/aio/src/app/search/search.service.spec.ts @@ -1,7 +1,6 @@ import { ReflectiveInjector, NgZone } from '@angular/core'; import { fakeAsync, tick } from '@angular/core/testing'; -import { Observable } from 'rxjs'; -import 'rxjs/add/observable/of'; +import { of } from 'rxjs'; import { SearchService } from './search.service'; import { WebWorkerClient } from 'app/shared/web-worker'; @@ -13,7 +12,7 @@ describe('SearchService', () => { let mockWorker: WebWorkerClient; beforeEach(() => { - sendMessageSpy = jasmine.createSpy('sendMessage').and.returnValue(Observable.of({})); + sendMessageSpy = jasmine.createSpy('sendMessage').and.returnValue(of({})); mockWorker = { sendMessage: sendMessageSpy } as any; spyOn(WebWorkerClient, 'create').and.returnValue(mockWorker); @@ -40,7 +39,7 @@ describe('SearchService', () => { // We must initialize the service before calling connectSearches service.initWorker('some/url', 1000); // Simulate the index being ready so that searches get sent to the worker - (service as any).ready = Observable.of(true); + (service as any).ready = of(true); }); it('should trigger a `loadIndex` synchronously (not waiting for the delay)', () => { @@ -57,7 +56,7 @@ describe('SearchService', () => { it('should push the response to the returned observable', () => { const mockSearchResults = { results: ['a', 'b'] }; let actualSearchResults: any; - (mockWorker.sendMessage as jasmine.Spy).and.returnValue(Observable.of(mockSearchResults)); + (mockWorker.sendMessage as jasmine.Spy).and.returnValue(of(mockSearchResults)); service.search('some query').subscribe(results => actualSearchResults = results); expect(actualSearchResults).toEqual(mockSearchResults); }); diff --git a/aio/src/app/search/search.service.ts b/aio/src/app/search/search.service.ts index b3dbef0123..cd37900402 100644 --- a/aio/src/app/search/search.service.ts +++ b/aio/src/app/search/search.service.ts @@ -5,10 +5,8 @@ can be found in the LICENSE file at http://angular.io/license */ import { NgZone, Injectable } from '@angular/core'; -import { Observable, race, ReplaySubject, timer } from 'rxjs'; -import 'rxjs/add/operator/concatMap'; -import 'rxjs/add/operator/first'; -import 'rxjs/add/operator/publishReplay'; +import { ConnectableObservable, Observable, race, ReplaySubject, timer } from 'rxjs'; +import { concatMap, first, publishReplay } from 'rxjs/operators'; import { WebWorkerClient } from 'app/shared/web-worker'; import { SearchResults } from 'app/search/interfaces'; @@ -31,16 +29,19 @@ export class SearchService { // Wait for the initDelay or the first search const ready = this.ready = race( timer(initDelay), - (this.searchesSubject.asObservable()).first() + this.searchesSubject.asObservable().pipe(first()), ) - .concatMap(() => { - // Create the worker and load the index - this.worker = WebWorkerClient.create(workerUrl, this.zone); - return this.worker.sendMessage('load-index'); - }).publishReplay(1); + .pipe( + concatMap(() => { + // Create the worker and load the index + this.worker = WebWorkerClient.create(workerUrl, this.zone); + return this.worker.sendMessage('load-index'); + }), + publishReplay(1), + ); // Connect to the observable to kick off the timer - ready.connect(); + (ready as ConnectableObservable).connect(); return ready; } @@ -53,6 +54,6 @@ export class SearchService { // Trigger the searches subject to override the init delay timer this.searchesSubject.next(query); // Once the index has loaded, switch to listening to the searches coming in. - return this.ready.concatMap(() => this.worker.sendMessage('query-index', query)); + return this.ready.pipe(concatMap(() => this.worker.sendMessage('query-index', query))); } } diff --git a/aio/src/app/shared/location.service.ts b/aio/src/app/shared/location.service.ts index 285b761989..1f975ef7bc 100644 --- a/aio/src/app/shared/location.service.ts +++ b/aio/src/app/shared/location.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { Location, PlatformLocation } from '@angular/common'; import { ReplaySubject } from 'rxjs'; -import 'rxjs/add/operator/do'; +import { map, tap } from 'rxjs/operators'; import { GaService } from 'app/shared/ga.service'; import { SwUpdatesService } from 'app/sw-updates/sw-updates.service'; @@ -15,11 +15,12 @@ export class LocationService { private swUpdateActivated = false; currentUrl = this.urlSubject - .map(url => this.stripSlashes(url)); + .pipe(map(url => this.stripSlashes(url))); - currentPath = this.currentUrl - .map(url => (url.match(/[^?#]*/) || [])[0]) // strip query and hash - .do(path => this.gaService.locationChanged(path)); + currentPath = this.currentUrl.pipe( + map(url => (url.match(/[^?#]*/) || [])[0]), // strip query and hash + tap(path => this.gaService.locationChanged(path)), + ); constructor( private gaService: GaService, diff --git a/aio/src/app/shared/scroll-spy.service.ts b/aio/src/app/shared/scroll-spy.service.ts index df46488afd..6b16ae6db6 100644 --- a/aio/src/app/shared/scroll-spy.service.ts +++ b/aio/src/app/shared/scroll-spy.service.ts @@ -1,10 +1,7 @@ import { Inject, Injectable } from '@angular/core'; import { DOCUMENT } from '@angular/platform-browser'; -import { Observable, ReplaySubject, Subject } from 'rxjs'; -import 'rxjs/add/observable/fromEvent'; -import 'rxjs/add/operator/auditTime'; -import 'rxjs/add/operator/distinctUntilChanged'; -import 'rxjs/add/operator/takeUntil'; +import { fromEvent, Observable, ReplaySubject, Subject } from 'rxjs'; +import { auditTime, distinctUntilChanged, takeUntil } from 'rxjs/operators'; import { ScrollService } from 'app/shared/scroll.service'; @@ -122,8 +119,8 @@ export class ScrollSpiedElementGroup { export class ScrollSpyService { private spiedElementGroups: ScrollSpiedElementGroup[] = []; private onStopListening = new Subject(); - private resizeEvents = Observable.fromEvent(window, 'resize').auditTime(300).takeUntil(this.onStopListening); - private scrollEvents = Observable.fromEvent(window, 'scroll').auditTime(10).takeUntil(this.onStopListening); + private resizeEvents = fromEvent(window, 'resize').pipe(auditTime(300), takeUntil(this.onStopListening)); + private scrollEvents = fromEvent(window, 'scroll').pipe(auditTime(10), takeUntil(this.onStopListening)); private lastContentHeight: number; private lastMaxScrollTop: number; @@ -159,7 +156,7 @@ export class ScrollSpyService { this.spiedElementGroups.push(spiedGroup); return { - active: spiedGroup.activeScrollItem.asObservable().distinctUntilChanged(), + active: spiedGroup.activeScrollItem.asObservable().pipe(distinctUntilChanged()), unspy: () => this.unspy(spiedGroup) }; } diff --git a/aio/src/app/sw-updates/sw-updates.service.spec.ts b/aio/src/app/sw-updates/sw-updates.service.spec.ts index b9bf55b533..d32eb23524 100644 --- a/aio/src/app/sw-updates/sw-updates.service.spec.ts +++ b/aio/src/app/sw-updates/sw-updates.service.spec.ts @@ -2,7 +2,7 @@ import { ReflectiveInjector } from '@angular/core'; import { fakeAsync, tick } from '@angular/core/testing'; import { NgServiceWorker } from '@angular/service-worker'; import { Subject } from 'rxjs'; -import 'rxjs/add/operator/take'; +import { take } from 'rxjs/operators'; import { Logger } from 'app/shared/logger.service'; import { SwUpdatesService } from './sw-updates.service'; @@ -153,8 +153,8 @@ class MockNgServiceWorker { updates = this.$$updatesSubj.asObservable(); activateUpdate = jasmine.createSpy('MockNgServiceWorker.activateUpdate') - .and.callFake(() => this.$$activateUpdateSubj.take(1)); + .and.callFake(() => this.$$activateUpdateSubj.pipe(take(1))); checkForUpdate = jasmine.createSpy('MockNgServiceWorker.checkForUpdate') - .and.callFake(() => this.$$checkForUpdateSubj.take(1)); + .and.callFake(() => this.$$checkForUpdateSubj.pipe(take(1))); } diff --git a/aio/src/app/sw-updates/sw-updates.service.ts b/aio/src/app/sw-updates/sw-updates.service.ts index 9dfae1c924..cbc373aeef 100644 --- a/aio/src/app/sw-updates/sw-updates.service.ts +++ b/aio/src/app/sw-updates/sw-updates.service.ts @@ -1,15 +1,7 @@ import { Injectable, OnDestroy } from '@angular/core'; import { NgServiceWorker } from '@angular/service-worker'; -import { Observable, Subject } from 'rxjs'; -import 'rxjs/add/observable/of'; -import 'rxjs/add/operator/concat'; -import 'rxjs/add/operator/debounceTime'; -import 'rxjs/add/operator/filter'; -import 'rxjs/add/operator/map'; -import 'rxjs/add/operator/startWith'; -import 'rxjs/add/operator/take'; -import 'rxjs/add/operator/takeUntil'; - +import { concat, of, Subject } from 'rxjs'; +import { debounceTime, filter, map, startWith, take, takeUntil, tap } from 'rxjs/operators'; import { Logger } from 'app/shared/logger.service'; @@ -28,19 +20,22 @@ import { Logger } from 'app/shared/logger.service'; @Injectable() export class SwUpdatesService implements OnDestroy { private checkInterval = 1000 * 60 * 60 * 6; // 6 hours - private onDestroy = new Subject(); - private checkForUpdateSubj = new Subject(); - updateActivated = this.sw.updates - .takeUntil(this.onDestroy) - .do(evt => this.log(`Update event: ${JSON.stringify(evt)}`)) - .filter(({type}) => type === 'activation') - .map(({version}) => version); + private onDestroy = new Subject(); + private checkForUpdateSubj = new Subject(); + updateActivated = this.sw.updates.pipe( + takeUntil(this.onDestroy), + tap(evt => this.log(`Update event: ${JSON.stringify(evt)}`)), + filter(({type}) => type === 'activation'), + map(({version}) => version), + ); constructor(private logger: Logger, private sw: NgServiceWorker) { this.checkForUpdateSubj - .debounceTime(this.checkInterval) - .startWith(null) - .takeUntil(this.onDestroy) + .pipe( + debounceTime(this.checkInterval), + startWith(undefined), + takeUntil(this.onDestroy), + ) .subscribe(() => this.checkForUpdate()); } @@ -56,11 +51,13 @@ export class SwUpdatesService implements OnDestroy { private checkForUpdate() { this.log('Checking for update...'); - this.sw.checkForUpdate() - // Temp workaround for https://github.com/angular/mobile-toolkit/pull/137. - // TODO (gkalpak): Remove once #137 is fixed. - .concat(Observable.of(false)).take(1) - .do(v => this.log(`Update available: ${v}`)) + // Temp workaround for https://github.com/angular/mobile-toolkit/pull/137. + // TODO (gkalpak): Remove once #137 is fixed. + concat(this.sw.checkForUpdate(), of(false)) + .pipe( + take(1), + tap(v => this.log(`Update available: ${v}`)), + ) .subscribe(v => v ? this.activateUpdate() : this.scheduleCheckForUpdate()); } diff --git a/aio/src/main.ts b/aio/src/main.ts index 40593a4b23..de675719f7 100644 --- a/aio/src/main.ts +++ b/aio/src/main.ts @@ -1,5 +1,6 @@ import { enableProdMode, ApplicationRef } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { first } from 'rxjs/operators'; import { AppModule } from './app/app.module'; import { environment } from './environments/environment'; @@ -11,7 +12,7 @@ if (environment.production) { platformBrowserDynamic().bootstrapModule(AppModule).then(ref => { if (environment.production && 'serviceWorker' in (navigator as any)) { const appRef: ApplicationRef = ref.injector.get(ApplicationRef); - appRef.isStable.first().subscribe(() => { + appRef.isStable.pipe(first()).subscribe(() => { (navigator as any).serviceWorker.register('/worker-basic.min.js'); }); } diff --git a/aio/src/testing/location.service.ts b/aio/src/testing/location.service.ts index a729b6a2f7..f51ff294dc 100644 --- a/aio/src/testing/location.service.ts +++ b/aio/src/testing/location.service.ts @@ -1,10 +1,11 @@ import { BehaviorSubject } from 'rxjs'; +import { map } from 'rxjs/operators'; export class MockLocationService { urlSubject = new BehaviorSubject(this.initialUrl); - currentUrl = this.urlSubject.asObservable().map(url => this.stripSlashes(url)); + currentUrl = this.urlSubject.asObservable().pipe(map(url => this.stripSlashes(url))); // strip off query and hash - currentPath = this.currentUrl.map(url => url.match(/[^?#]*/)![0]); + currentPath = this.currentUrl.pipe(map(url => url.match(/[^?#]*/)![0])); search = jasmine.createSpy('search').and.returnValue({}); setSearch = jasmine.createSpy('setSearch'); go = jasmine.createSpy('Location.go').and