Skip to content

Commit

Permalink
docs(articles): prevent flickering (#2910)
Browse files Browse the repository at this point in the history
  • Loading branch information
evtkhvch committed Nov 15, 2021
1 parent 73cdec0 commit 9a5a0f6
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 19 deletions.
12 changes: 8 additions & 4 deletions docs/app/@theme/services/article.service.ts
@@ -1,13 +1,17 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map, shareReplay } from 'rxjs/operators';
import { NgdMdSection, NgdTextService } from './text.service';

@Injectable()
export class NgdArticleService {
constructor(private http: HttpClient, private textService: NgdTextService) {}

constructor(private http: HttpClient) { }

getArticle(source: string): Observable<string> {
return this.http.get(`articles/${source}`, { responseType: 'text' });
getArticle(source: string): Observable<NgdMdSection[]> {
return this.http.get(`articles/${source}`, { responseType: 'text' }).pipe(
map((article) => this.textService.mdToSectionsHTML(article)),
shareReplay(1),
);
}
}
1 change: 1 addition & 0 deletions docs/app/@theme/services/index.ts
Expand Up @@ -12,3 +12,4 @@ export * from './analytics.service';
export * from './menu.service';
export * from './metadata.service';
export * from './article.service';
export * from './last-viewed-section.service';
22 changes: 22 additions & 0 deletions docs/app/@theme/services/last-viewed-section.service.ts
@@ -0,0 +1,22 @@
import { Injectable } from '@angular/core';
import { Observable, ReplaySubject } from 'rxjs';
import { NgdMdSection } from './text.service';

/**
* We store the last viewed section here to prevent flickering when switching between articles.
* When a user clicks on an article link, `ngd-md-block` is destroyed and a new one is created.
* This service replays the last viewed article to the newly created `ngd-md-block` to show it
* while a requested article is loading.
* */
@Injectable()
export class NgdLastViewedSectionService {
private _sections = new ReplaySubject<NgdMdSection[]>(1);

setSection(section: NgdMdSection[]): void {
this._sections.next(section);
}

getSections(): Observable<NgdMdSection[]> {
return this._sections.asObservable();
}
}
4 changes: 1 addition & 3 deletions docs/app/@theme/services/structure.service.ts
Expand Up @@ -62,9 +62,7 @@ export class NgdStructureService {
}

if (item.block === 'markdown') {
item.sections = this.articleService.getArticle(item.source).pipe(
map((article) => this.textService.mdToSectionsHTML(article)),
);
item.sections = this.articleService.getArticle(item.source);
}

if (item.children) {
Expand Down
2 changes: 2 additions & 0 deletions docs/app/@theme/theme.module.ts
Expand Up @@ -59,6 +59,7 @@ import {
NgdMenuService,
NgdMetadataService,
NgdArticleService,
NgdLastViewedSectionService,
} from './services';
import { AkveoServicesBanner } from './components/hubspot-cta/akveo-services-banner.component';

Expand Down Expand Up @@ -144,6 +145,7 @@ export class NgdThemeModule {
NgdVisibilityService,
NgdMetadataService,
NgdArticleService,
NgdLastViewedSectionService,
],
};
}
Expand Down
3 changes: 2 additions & 1 deletion docs/app/blocks/blocks.module.ts
Expand Up @@ -7,7 +7,7 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { CommonModule } from '@angular/common';
import { NbInputModule } from '@nebular/theme';
import { NbInputModule, NbSpinnerModule } from '@nebular/theme';

import { NgdThemeModule } from '../@theme/theme.module';

Expand Down Expand Up @@ -63,6 +63,7 @@ const blocks = [
RouterModule,
NgdThemeModule,
NbInputModule,
NbSpinnerModule,
],
declarations: [
...blocks,
Expand Down
42 changes: 32 additions & 10 deletions docs/app/blocks/components/md-block/md-block.component.ts
Expand Up @@ -4,28 +4,50 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*/

import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input } from '@angular/core';
import { ChangeDetectionStrategy, Component, Input, OnDestroy } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { Observable, Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

import { NgdMdSection } from '../../../@theme/services/text.service';
import { NgdLastViewedSectionService } from '../../../@theme/services';

@Component({
selector: 'ngd-md-block',
template: `
<nb-card *ngFor="let section of sections;" [ngdFragment]="section.fragment">
<nb-card-body>
<div [innerHtml]="getTemplate(section.html)"></div>
</nb-card-body>
</nb-card>
<div [nbSpinner]="loading">
<nb-card *ngFor="let section of sections$ | async" [ngdFragment]="section.fragment">
<nb-card-body>
<div [innerHtml]="getTemplate(section.html)"></div>
</nb-card-body>
</nb-card>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NgdMdBLockComponent {
@Input() sections: NgdMdSection[] = []
export class NgdMdBLockComponent implements OnDestroy {
private destroy$ = new Subject<void>();
loading = true;

@Input()
get sections$(): Observable<NgdMdSection[]> {
return this.lastViewedSectionService.getSections();
}
set sections$(value: Observable<NgdMdSection[]>) {
value.pipe(takeUntil(this.destroy$)).subscribe((sections) => {
this.loading = false;
this.lastViewedSectionService.setSection(sections);
});
}

constructor(
private cd: ChangeDetectorRef,
private readonly domSanitizer: DomSanitizer) {
private readonly domSanitizer: DomSanitizer,
private lastViewedSectionService: NgdLastViewedSectionService,
) {}

ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}

getTemplate(content: string): SafeHtml {
Expand Down
2 changes: 1 addition & 1 deletion docs/app/documentation/page/page.component.html
Expand Up @@ -14,7 +14,7 @@ <h1 class="page-header h4">

<ng-container *ngFor="let block of currentItem?.children">
<ng-container [ngSwitch]="block.block">
<ngd-md-block *ngSwitchCase="'markdown'" [sections]="block.sections | async"></ngd-md-block>
<ngd-md-block *ngSwitchCase="'markdown'" [sections$]="block.sections"></ngd-md-block>
<ngd-component-block *ngSwitchCase="'component'" [source]="block.source"></ngd-component-block>
<ngd-tabbed-block *ngSwitchCase="'tabbed'" [source]="block.children" [tabs]="currentItem.tabs"></ngd-tabbed-block>
<ngd-theme-block *ngSwitchCase="'theme'" [block]="block"></ngd-theme-block>
Expand Down

0 comments on commit 9a5a0f6

Please sign in to comment.