Skip to content

Commit

Permalink
refactor(BlockUI): Rewrite block queueing in more cohesive way
Browse files Browse the repository at this point in the history
  • Loading branch information
kuuurt13 committed Jun 26, 2020
1 parent 20f785d commit cc9c2e6
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 100 deletions.
42 changes: 16 additions & 26 deletions lib/components/block-ui-content/block-ui-content.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ describe('block-ui-content component', () => {
method();
});

cf.detectChanges();
jasmine.clock().tick(1);
let blockWrapper = cf.debugElement.query(By.css('div.block-ui-wrapper'));
expect(blockWrapper.classes.active).toBeTruthy();
});
Expand Down Expand Up @@ -438,92 +440,87 @@ describe('block-ui-content component', () => {
testCmp.blockUI.start();
cf.detectChanges();

expect(blkContComp.active).toBeFalsy();
expect(blkContComp.state.blockCount).toBe(0);

jasmine.clock().tick(200);
cf.detectChanges();

expect(blkContComp.active).toBeFalsy();
expect(blkContComp.state.blockCount).toBe(0);

jasmine.clock().tick(300);
cf.detectChanges();

expect(blkContComp.active).toBeTruthy();
expect(blkContComp.state.blockCount).toBe(1);
}));

it('blocker is active on blockUI.stop() until delay has passed', fakeAsync(() => {
testCmp.blockUI.start();
jasmine.clock().tick(500);
cf.detectChanges();

expect(blkContComp.active).toBeTruthy();
expect(blkContComp.state.blockCount).toBeTruthy();

testCmp.blockUI.stop();
cf.detectChanges();

expect(blkContComp.active).toBeTruthy();
expect(blkContComp.state.blockCount).toBeTruthy();

jasmine.clock().tick(200);
cf.detectChanges();

expect(blkContComp.active).toBeTruthy();
expect(blkContComp.state.blockCount).toBeTruthy();

jasmine.clock().tick(300);
cf.detectChanges();

expect(blkContComp.active).toBeFalsy();
expectStateIsReset(blkContComp);
expect(blkContComp.state.blockCount).toBe(0);
}));

it('blocker is NOT active on blockUI.stop() and state is cleared if delayed start has not yet passed, ignoring delayStop', fakeAsync(() => {
testCmp.blockUI.start();
jasmine.clock().tick(300);
cf.detectChanges();

expect(blkContComp.active).toBeFalsy();
expect(blkContComp.state.blockCount).toBe(0);

testCmp.blockUI.stop();
cf.detectChanges();

expect(blkContComp.active).toBeFalsy();
expect(blkContComp.state.blockCount).toBe(0);

jasmine.clock().tick(1000);
cf.detectChanges();

expect(blkContComp.active).toBeFalsy();
expectStateIsReset(blkContComp);
expect(blkContComp.state.blockCount).toBe(0);
}));

it('blocker IS active on blockUI.stop() until all blocked calls have resolved', fakeAsync(() => {
it('blocker is active on blockUI.stop() until all blocked calls have resolved', fakeAsync(() => {
testCmp.blockUI.start();
testCmp.blockUI.start();
testCmp.blockUI.start();
jasmine.clock().tick(500);
cf.detectChanges();

expect(blkContComp.active).toBeTruthy();
expect(blkContComp.state.blockCount).toBe(3);

testCmp.blockUI.stop();
jasmine.clock().tick(500);
cf.detectChanges();

expect(blkContComp.active).toBeTruthy();
expect(blkContComp.state.blockCount).toBeTruthy();
expect(blkContComp.state.blockCount).toBe(2);

testCmp.blockUI.stop();
jasmine.clock().tick(500);
cf.detectChanges();

expect(blkContComp.active).toBeTruthy();
expect(blkContComp.state.blockCount).toBe(1);

testCmp.blockUI.stop();
jasmine.clock().tick(500);
cf.detectChanges();

expect(blkContComp.active).toBeFalsy();
expectStateIsReset(blkContComp);
expect(blkContComp.state.blockCount).toBe(0);
}));

it('blocker is no longer active on blockUI.reset(), ignoring any delays or outstanding calls', fakeAsync(() => {
Expand All @@ -533,20 +530,13 @@ describe('block-ui-content component', () => {
jasmine.clock().tick(500);
cf.detectChanges();

expect(blkContComp.active).toBeTruthy();
expect(blkContComp.state.blockCount).toBe(3);

testCmp.blockUI.reset();
cf.detectChanges();

expect(blkContComp.active).toBeFalsy();
expectStateIsReset(blkContComp);
expect(blkContComp.state.blockCount).toBe(0);
}));
});
});

function expectStateIsReset(blkContComp: BlockUIContentComponent) {
expect(blkContComp.state.startTimeout).toBeNull();
expect(blkContComp.state.stopTimeout).toBeNull();
expect(blkContComp.state.blockCount).toBe(0);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export const template = `
<div class="block-ui-wrapper {{name}} {{className}}" [ngClass]="{ 'active': active }">
<div class="block-ui-wrapper {{name}} {{className}}" [ngClass]="{ 'active': state.blockCount > 0 }">
<div class="block-ui-spinner" *ngIf="!templateCmp">
<div class="loader"></div>
<div *ngIf="message || defaultMessage" class="message">
Expand Down
115 changes: 43 additions & 72 deletions lib/components/block-ui-content/block-ui-content.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,14 @@ export class BlockUIContentComponent implements OnInit, AfterViewInit, AfterView
@ViewChild('templateOutlet', { read: ViewContainerRef })
templateOutlet: ViewContainerRef;

state: any = { startTimeout: null, stopTimeout: null, blockCount: 0 };
defaultState: any = {
startTimeout: null,
stopTimeout: null,
updateTimeout: null,
blockCount: 0
};
state: any = { ...this.defaultState };
className: string;
active: boolean = false;
templateCompRef: ComponentRef<{ message?: any }> | TemplateRef<{}>;
message: any;

Expand Down Expand Up @@ -69,7 +74,6 @@ export class BlockUIContentComponent implements OnInit, AfterViewInit, AfterView
} else {
const templateComp = this.resolver.resolveComponentFactory(this.templateCmp);
this.templateCompRef = this.templateOutlet.createComponent(templateComp);

this.updateBlockTemplate(this.message);
}
} catch (error) {
Expand All @@ -82,29 +86,28 @@ export class BlockUIContentComponent implements OnInit, AfterViewInit, AfterView
}

private subscribeToBlockUI(blockUI$: Observable<any>): Subscription {
return blockUI$
.subscribe(event => this.onDispatchedEvent(event));
return blockUI$.subscribe(event => this.onDispatchedEvent(event));
}

private onDispatchedEvent(event: BlockUIEvent) {
switch (event.action) {
case (BlockUIActions.START):
case BlockUIActions.START:
this.onStart(event);
break;

case (BlockUIActions.STOP):
case BlockUIActions.STOP:
this.onStop(event);
break;

case (BlockUIActions.UPDATE):
case BlockUIActions.UPDATE:
this.onUpdate(event);
break;

case (BlockUIActions.RESET):
this.onReset();
case BlockUIActions.RESET:
this.resetState();
break;

case (BlockUIActions.UNSUBSCRIBE):
case BlockUIActions.UNSUBSCRIBE:
this.onStop(event);
this.onUnsubscribe(event.name);
break;
Expand All @@ -115,99 +118,65 @@ export class BlockUIContentComponent implements OnInit, AfterViewInit, AfterView
if (name === this.name) {
const delay = this.delayStart || this.settings.delayStart || 0;

if (this.state.startTimeout === null) {
if (delay === 0) {
this.showBlock(message);
} else {
this.state.startTimeout = setTimeout(() => {
this.showBlock(message);
}, delay);
}
}

this.state.blockCount++;
this.updateInstanceBlockCount();
this.state.startTimeout = setTimeout(() => {
this.state.blockCount += 1;
this.showBlock(message);
this.updateInstanceBlockCount();
}, delay);
}
}

private onStop({ name }: BlockUIEvent) {
if (name === this.name) {
const delay = this.delayStop || this.settings.delayStop || 0;

if (this.state.blockCount > 1) {
this.state.blockCount--;
} else {
if (!this.active) {
this.clearState();
} else {
if (this.state.stopTimeout === null) {
if (delay === 0) {
this.hideBlock();
} else {
this.state.stopTimeout = setTimeout(() => {
this.hideBlock();
}, delay);
}
}
clearTimeout(this.state.stopTimeout);
this.state.stopTimeout = setTimeout(() => {
if (this.state.blockCount > 0) {
this.state.blockCount -= 1;
}
}

this.updateInstanceBlockCount();
this.updateInstanceBlockCount();
this.detectChanges();
}, delay);
}
}

private onReset() {
this.hideBlock();
}

private onUpdate({ name, message }: BlockUIEvent) {
if (name === this.name) {
const delay = this.delayStart || this.settings.delayStart || 0;

if (delay === 0) {
clearTimeout(this.state.updateTimeout);
this.state.updateTimeout = setTimeout(() => {
this.updateMessage(message);
} else {
setTimeout(() => {
this.updateMessage(message);
}, delay);
}
}, delay);
}
}

updateMessage(message: string) {
this.message = message || this.defaultMessage || this.settings.message;
this.updateBlockTemplate(this.message);
this.detectChanges();
private updateMessage(message: string) {
this.showBlock(message);
}

private showBlock(message: any) {
this.active = true;
this.message = message || this.defaultMessage || this.settings.message;
this.updateBlockTemplate(this.message);
this.detectChanges();
}

private hideBlock() {
this.clearState();
this.active = false;
this.detectChanges();
}

private clearState() {
this.state.startTimeout != null && clearTimeout(this.state.startTimeout);
this.state.stopTimeout != null && clearTimeout(this.state.stopTimeout);
this.state.blockCount = 0;
this.state.startTimeout = null;
this.state.stopTimeout = null;
this.updateInstanceBlockCount();
}

private updateBlockTemplate(msg: any): void {
if (this.templateCompRef && this.templateCompRef instanceof ComponentRef) {
this.templateCompRef.instance.message = msg;
}
}

private resetState() {
clearTimeout(this.state.startTimeout);
clearTimeout(this.state.stopTimeout);
clearTimeout(this.state.updateTimeout);
this.state = { ...this.defaultState };
this.updateInstanceBlockCount();
this.detectChanges();
}

private onUnsubscribe(name: string) {
if (this.blockUISubscription && name === this.name) {
this.blockUISubscription.unsubscribe();
Expand All @@ -216,7 +185,8 @@ export class BlockUIContentComponent implements OnInit, AfterViewInit, AfterView

private updateInstanceBlockCount() {
if (this.blockUI.blockUIInstances[this.name]) {
this.blockUI.blockUIInstances[this.name].blockCount = this.state.blockCount;
const { blockCount } = this.state;
this.blockUI.blockUIInstances[this.name].blockCount = blockCount;
}
}

Expand All @@ -227,6 +197,7 @@ export class BlockUIContentComponent implements OnInit, AfterViewInit, AfterView
}

ngOnDestroy() {
this.resetState();
this.onUnsubscribe(this.name);
}
}
2 changes: 1 addition & 1 deletion lib/models/block-ui-settings.model.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ComponentRef, TemplateRef } from '@angular/core';
import { ComponentRef } from '@angular/core';

export interface BlockUISettings {
message?: string;
Expand Down

0 comments on commit cc9c2e6

Please sign in to comment.