From 425cc0f2862af5580a81f192143f454489c7a854 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Sun, 30 Jul 2017 15:46:51 +0200 Subject: [PATCH] Performance improvements. --- .../Api/Assets/Models/AssetReplacedDto.cs | 1 - .../angular/image-source.directive.ts | 55 +++++++--------- .../angular/panel-container.directive.ts | 17 +++-- .../app/framework/angular/panel.component.ts | 8 +++ .../app/framework/angular/sorted.directive.ts | 2 +- src/Squidex/app/shared/components/pipes.ts | 6 +- .../services/apps-store.service.spec.ts | 2 +- .../app/shared/services/auth.service.ts | 65 +++++++++++++++---- 8 files changed, 100 insertions(+), 56 deletions(-) diff --git a/src/Squidex/Controllers/Api/Assets/Models/AssetReplacedDto.cs b/src/Squidex/Controllers/Api/Assets/Models/AssetReplacedDto.cs index 6f4f8113a..c4e1dc5de 100644 --- a/src/Squidex/Controllers/Api/Assets/Models/AssetReplacedDto.cs +++ b/src/Squidex/Controllers/Api/Assets/Models/AssetReplacedDto.cs @@ -8,7 +8,6 @@ using System.ComponentModel.DataAnnotations; using Squidex.Domain.Apps.Write.Assets.Commands; -using Squidex.Infrastructure; using Squidex.Infrastructure.CQRS.Commands; namespace Squidex.Controllers.Api.Assets.Models diff --git a/src/Squidex/app/framework/angular/image-source.directive.ts b/src/Squidex/app/framework/angular/image-source.directive.ts index 3601e1d06..aef4eb390 100644 --- a/src/Squidex/app/framework/angular/image-source.directive.ts +++ b/src/Squidex/app/framework/angular/image-source.directive.ts @@ -13,8 +13,9 @@ import { MathHelper } from './../utils/math-helper'; selector: '[sqxImageSource]' }) export class ImageSourceDirective implements OnChanges, OnInit, AfterViewInit { - private retries = 0; - private query: string | null = null; + private size: any; + private loadRetries = 0; + private loadQuery: string | null = null; @Input('sqxImageSource') public imageSource: string; @@ -32,8 +33,8 @@ export class ImageSourceDirective implements OnChanges, OnInit, AfterViewInit { } public ngOnChanges() { - this.query = null; - this.retries = 0; + this.loadQuery = null; + this.loadRetries = 0; this.setImageSource(); } @@ -43,19 +44,20 @@ export class ImageSourceDirective implements OnChanges, OnInit, AfterViewInit { } public ngOnInit() { - this.renderer.setElementStyle(this.element.nativeElement, 'display', 'inline-block'); - if (this.parent === null) { this.parent = this.element.nativeElement.parentElement; } - this.resize(this.parent); - this.renderer.listen(this.parent, 'resize', () => { this.resize(this.parent); }); } + @HostListener('load') + public onLoad() { + this.renderer.setElementStyle(this.element.nativeElement, 'visibility', 'visible'); + } + @HostListener('error') public onError() { this.renderer.setElementStyle(this.element.nativeElement, 'visibility', 'hidden'); @@ -63,36 +65,29 @@ export class ImageSourceDirective implements OnChanges, OnInit, AfterViewInit { this.retryLoadingImage(); } - @HostListener('resize') - public onResize() { - this.setImageSource(); - } - - @HostListener('load') - public onLoad() { - this.renderer.setElementStyle(this.element.nativeElement, 'visibility', 'visible'); - } - private resize(parent: any) { - const size = parent.getBoundingClientRect(); + this.size = this.parent.getBoundingClientRect(); - this.renderer.setElementStyle(this.element.nativeElement, 'width', size.width + 'px'); - this.renderer.setElementStyle(this.element.nativeElement, 'height', size.height + 'px'); + this.renderer.setElementStyle(this.element.nativeElement, 'display', 'inline-block'); + this.renderer.setElementStyle(this.element.nativeElement, 'width', this.size.width + 'px'); + this.renderer.setElementStyle(this.element.nativeElement, 'height', this.size.height + 'px'); this.setImageSource(); } private setImageSource() { - const size = this.element.nativeElement.getBoundingClientRect(); + if (!this.size) { + return; + } - const w = Math.round(size.width); - const h = Math.round(size.height); + const w = Math.round(this.size.width); + const h = Math.round(this.size.height); if (w > 0 && h > 0) { let source = `${this.imageSource}&width=${w}&height=${h}&mode=Crop`; - if (this.query !== null) { - source += `&q=${this.query}`; + if (this.loadQuery !== null) { + source += `&q=${this.loadQuery}`; } this.renderer.setElementAttribute(this.element.nativeElement, 'src', source); @@ -100,14 +95,14 @@ export class ImageSourceDirective implements OnChanges, OnInit, AfterViewInit { } private retryLoadingImage() { - this.retries++; + this.loadRetries++; - if (this.retries <= 10) { + if (this.loadRetries <= 10) { setTimeout(() => { - this.query = MathHelper.guid(); + this.loadQuery = MathHelper.guid(); this.setImageSource(); - }, this.retries * 1000); + }, this.loadRetries * 1000); } } } \ No newline at end of file diff --git a/src/Squidex/app/framework/angular/panel-container.directive.ts b/src/Squidex/app/framework/angular/panel-container.directive.ts index 2619d5a9f..b96e2912d 100644 --- a/src/Squidex/app/framework/angular/panel-container.directive.ts +++ b/src/Squidex/app/framework/angular/panel-container.directive.ts @@ -14,6 +14,7 @@ import { PanelComponent } from './panel.component'; }) export class PanelContainerDirective implements AfterViewInit, OnDestroy { private readonly panels: PanelComponent[] = []; + private containerWidth = 0; private isInit = false; constructor( @@ -28,7 +29,7 @@ export class PanelContainerDirective implements AfterViewInit, OnDestroy { } public ngAfterViewInit() { - this.invalidate(true); + this.invalidate({ force: true, resize: true }); } public ngOnDestroy() { @@ -47,14 +48,16 @@ export class PanelContainerDirective implements AfterViewInit, OnDestroy { this.invalidate(); } - public invalidate(force = false) { - this.isInit = this.isInit || force; + public invalidate(params?: { force: boolean, resize: boolean }) { + this.isInit = this.isInit || (params && params.force); if (!this.isInit) { return; } - const containerWidth = this.element.nativeElement.getBoundingClientRect().width; + if (params && params.resize) { + this.containerWidth = this.element.nativeElement.getBoundingClientRect().width; + } let currentPosition = 0; let currentLayer = this.panels.length * 10; @@ -64,10 +67,10 @@ export class PanelContainerDirective implements AfterViewInit, OnDestroy { for (let panel of this.panels) { const panelRoot = panel.panel.nativeElement; - let width = panelRoot.getBoundingClientRect().width; + let width = panel.clientWidth; if (panel.expand && panel === last) { - width = containerWidth - currentPosition; + width = this.containerWidth - currentPosition; panel.panelWidth = width + 'px'; } @@ -82,7 +85,7 @@ export class PanelContainerDirective implements AfterViewInit, OnDestroy { currentLayer -= 10; } - const diff = currentPosition - containerWidth; + const diff = currentPosition - this.containerWidth; if (diff > 0) { this.element.nativeElement.scrollLeft = diff; diff --git a/src/Squidex/app/framework/angular/panel.component.ts b/src/Squidex/app/framework/angular/panel.component.ts index 54d869546..578d3e92a 100644 --- a/src/Squidex/app/framework/angular/panel.component.ts +++ b/src/Squidex/app/framework/angular/panel.component.ts @@ -24,6 +24,8 @@ import { PanelContainerDirective } from './panel-container.directive'; ] }) export class PanelComponent implements AfterViewInit, OnDestroy, OnInit { + private clientWidthValue = 0; + @Input() public theme = 'light'; @@ -36,6 +38,10 @@ export class PanelComponent implements AfterViewInit, OnDestroy, OnInit { @ViewChild('panel') public panel: ElementRef; + public get clientWidth() { + return this.clientWidthValue; + } + constructor( private readonly container: PanelContainerDirective ) { @@ -50,6 +56,8 @@ export class PanelComponent implements AfterViewInit, OnDestroy, OnInit { } public ngAfterViewInit() { + this.clientWidthValue = this.panel.nativeElement.getBoundingClientRect().width; + this.container.invalidate(); } } \ No newline at end of file diff --git a/src/Squidex/app/framework/angular/sorted.directive.ts b/src/Squidex/app/framework/angular/sorted.directive.ts index 9d2aaaed9..36e041d8f 100644 --- a/src/Squidex/app/framework/angular/sorted.directive.ts +++ b/src/Squidex/app/framework/angular/sorted.directive.ts @@ -30,7 +30,7 @@ export class SortedDirective { ) { const oldDragStartCallback = sortableComponent._onDragStartCallback.bind(sortableComponent); - sortableComponent._onDragStartCallback = (event: Event) => { + sortableComponent._onDragStartCallback = () => { oldDragStartCallback(); this.oldArray = [...sortableContainer.sortableData]; diff --git a/src/Squidex/app/shared/components/pipes.ts b/src/Squidex/app/shared/components/pipes.ts index fc97fe540..1d138fe68 100644 --- a/src/Squidex/app/shared/components/pipes.ts +++ b/src/Squidex/app/shared/components/pipes.ts @@ -196,7 +196,7 @@ export class UserPictureRefPipe extends UserAsyncPipe implements PipeTransform { @Pipe({ name: 'sqxAssetUrl', - pure: false + pure: true }) export class AssetUrlPipe implements PipeTransform { constructor( @@ -211,7 +211,7 @@ export class AssetUrlPipe implements PipeTransform { @Pipe({ name: 'sqxAssetPreviewUrl', - pure: false + pure: true }) export class AssetPreviewUrlPipe implements PipeTransform { constructor( @@ -226,7 +226,7 @@ export class AssetPreviewUrlPipe implements PipeTransform { @Pipe({ name: 'sqxFileIcon', - pure: false + pure: true }) export class FileIconPipe implements PipeTransform { public transform(asset: { mimeType: string, fileType: string }): string { diff --git a/src/Squidex/app/shared/services/apps-store.service.spec.ts b/src/Squidex/app/shared/services/apps-store.service.spec.ts index b90f80a0c..5e9bd36e7 100644 --- a/src/Squidex/app/shared/services/apps-store.service.spec.ts +++ b/src/Squidex/app/shared/services/apps-store.service.spec.ts @@ -70,7 +70,7 @@ describe('AppsStoreService', () => { result1 = x; }).unsubscribe(); - store.createApp(new CreateAppDto('new-name'), now).subscribe(() => { /* Do Nothing */ }); + store.createApp(new CreateAppDto('new-name'), now).subscribe(); store.apps.subscribe(x => { result2 = x; diff --git a/src/Squidex/app/shared/services/auth.service.ts b/src/Squidex/app/shared/services/auth.service.ts index 308bce9e2..6ec12056b 100644 --- a/src/Squidex/app/shared/services/auth.service.ts +++ b/src/Squidex/app/shared/services/auth.service.ts @@ -6,7 +6,7 @@ */ import { Injectable } from '@angular/core'; -import { Observable, ReplaySubject } from 'rxjs'; +import { Observable, Observer, ReplaySubject, TimeoutError } from 'rxjs'; import { Log, @@ -101,28 +101,67 @@ export class AuthService { this.checkState(this.userManager.getUser()); } - public logoutRedirect(): Observable { - return Observable.fromPromise(this.userManager.signoutRedirect()); + public logoutRedirect() { + this.userManager.signoutRedirect(); } - public logoutRedirectComplete(): Observable { - return Observable.fromPromise(this.userManager.signoutRedirectCallback()); + public loginRedirect() { + this.userManager.signinRedirect(); } - public loginPopup(): Observable { - return Observable.fromPromise(this.userManager.signinPopup()).map(u => this.createProfile(u)); + public logoutRedirectComplete(): Observable { + return Observable.create((observer: Observer) => { + this.userManager.signoutRedirectCallback() + .then(x => { + observer.next(x); + observer.complete(); + }, err => { + observer.error(err); + observer.complete(); + }); + }); } - public loginSilent(): Observable { - return Observable.fromPromise(this.userManager.signinSilent()).map(u => this.createProfile(u)); + public loginPopup(): Observable { + return Observable.create((observer: Observer) => { + this.userManager.signinPopup() + .then(x => { + observer.next(this.createProfile(x)); + observer.complete(); + }, err => { + observer.error(err); + observer.complete(); + }); + }); } - public loginRedirect(): Observable { - return Observable.fromPromise(this.userManager.signinRedirect()); + public loginRedirectComplete(): Observable { + return Observable.create((observer: Observer) => { + this.userManager.signinRedirectCallback() + .then(x => { + observer.next(this.createProfile(x)); + observer.complete(); + }, err => { + observer.error(err); + observer.complete(); + }); + }); } - public loginRedirectComplete(): Observable { - return Observable.fromPromise(this.userManager.signinRedirectCallback()).map(u => this.createProfile(u)); + public loginSilent(): Observable { + const observable: Observable = + Observable.create((observer: Observer) => { + this.userManager.signinSilent() + .then(x => { + observer.next(this.createProfile(x)); + observer.complete(); + }, err => { + observer.error(err); + observer.complete(); + }); + }); + + return observable.timeout(1000).retryWhen(errors => errors.filter(e => e instanceof TimeoutError)); } private createProfile(user: User) {