From 938f3def1fbdb4e4c4f554b334b83e4aff6142bb Mon Sep 17 00:00:00 2001 From: schmelczerandras Date: Sun, 1 Sep 2019 10:44:17 +0200 Subject: [PATCH] Make app work again --- src/app/app.module.ts | 4 +- .../create-block/create-block.component.html | 2 +- .../create-block/create-block.component.ts | 2 +- .../edit-block/edit-block.component.html | 2 +- .../remove-tower/remove-tower.component.html | 2 +- .../page/tower/block/block.component.html | 2 +- .../pages/page/tower/block/block.component.ts | 13 ++-- .../page/tower/tasks/tasks.component.html | 4 +- .../pages/page/tower/tasks/tasks.component.ts | 18 +++--- .../pages/page/tower/tower.component.html | 4 +- .../pages/page/tower/tower.component.ts | 17 +++-- src/app/interfaces/color.ts | 5 ++ src/app/interfaces/persistance/block.ts | 5 +- src/app/interfaces/persistance/color.ts | 8 --- src/app/interfaces/persistance/page.ts | 4 +- src/app/interfaces/persistance/tower.ts | 6 +- src/app/interfaces/persistance/typed.ts | 3 - src/app/interfaces/vector.ts | 4 -- src/app/model/block.ts | 20 ++++-- src/app/model/page.ts | 23 +++---- src/app/model/serializable.ts | 11 ++-- src/app/model/tower.ts | 60 +++++++++-------- src/app/pipes/color.pipe.ts | 12 ++++ src/app/services/data.service.ts | 13 +++- src/app/services/store.service.ts | 39 +++++------ src/app/{storage => store}/cloneable.ts | 64 +++++++++++++------ src/app/{storage => store}/inner-node.ts | 30 +++++++-- src/app/{storage => store}/node.ts | 6 ++ src/app/{storage => store}/root.ts | 4 ++ src/app/utils/color.ts | 4 +- 30 files changed, 236 insertions(+), 155 deletions(-) create mode 100644 src/app/interfaces/color.ts delete mode 100644 src/app/interfaces/persistance/color.ts delete mode 100644 src/app/interfaces/persistance/typed.ts delete mode 100644 src/app/interfaces/vector.ts create mode 100644 src/app/pipes/color.pipe.ts rename src/app/{storage => store}/cloneable.ts (56%) rename src/app/{storage => store}/inner-node.ts (67%) rename src/app/{storage => store}/node.ts (93%) rename src/app/{storage => store}/root.ts (96%) diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 7a2c363..4fdc9d0 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -22,6 +22,7 @@ import { CreateBlockComponent } from './components/modal/modals/create-block/cre import { RemoveBlockComponent } from './components/modal/modals/remove-block/remove-block.component'; import { ToggleComponent } from './components/shared/toggle/toggle.component'; import { TasksComponent } from './components/pages/page/tower/tasks/tasks.component'; +import { ColorPipe } from './pipes/color.pipe'; @NgModule({ declarations: [ @@ -41,7 +42,8 @@ import { TasksComponent } from './components/pages/page/tower/tasks/tasks.compon CreateBlockComponent, RemoveBlockComponent, ToggleComponent, - TasksComponent + TasksComponent, + ColorPipe ], imports: [BrowserModule, AppRoutingModule, FormsModule, BrowserAnimationsModule, DragDropModule], providers: [], diff --git a/src/app/components/modal/modals/create-block/create-block.component.html b/src/app/components/modal/modals/create-block/create-block.component.html index ac8762f..cf5a62b 100644 --- a/src/app/components/modal/modals/create-block/create-block.component.html +++ b/src/app/components/modal/modals/create-block/create-block.component.html @@ -24,7 +24,7 @@ diff --git a/src/app/components/modal/modals/create-block/create-block.component.ts b/src/app/components/modal/modals/create-block/create-block.component.ts index bbf3d84..5f9fed9 100644 --- a/src/app/components/modal/modals/create-block/create-block.component.ts +++ b/src/app/components/modal/modals/create-block/create-block.component.ts @@ -9,7 +9,7 @@ import { ModalService } from '../../../../services/modal.service'; export class CreateBlockComponent { selected: string; description: string = null; - isDone: boolean; + isDone: boolean = !this.modalService.active.input.isTask; constructor(public modalService: ModalService) {} diff --git a/src/app/components/modal/modals/edit-block/edit-block.component.html b/src/app/components/modal/modals/edit-block/edit-block.component.html index 265fb8f..8e967c6 100644 --- a/src/app/components/modal/modals/edit-block/edit-block.component.html +++ b/src/app/components/modal/modals/edit-block/edit-block.component.html @@ -8,7 +8,7 @@ You are trying to remove - {{ + {{ modalService.active.input.name ? modalService.active.input.name : 'an unnamed tower' }}. diff --git a/src/app/components/pages/page/tower/block/block.component.html b/src/app/components/pages/page/tower/block/block.component.html index 8b984b3..59c0d82 100644 --- a/src/app/components/pages/page/tower/block/block.component.html +++ b/src/app/components/pages/page/tower/block/block.component.html @@ -1 +1 @@ -
+
diff --git a/src/app/components/pages/page/tower/block/block.component.ts b/src/app/components/pages/page/tower/block/block.component.ts index 2aae178..b539ec1 100644 --- a/src/app/components/pages/page/tower/block/block.component.ts +++ b/src/app/components/pages/page/tower/block/block.component.ts @@ -1,7 +1,7 @@ import { Component, Input } from '@angular/core'; import { Block } from '../../../../../model/block'; import { ModalService } from '../../../../../services/modal.service'; -import { Tower } from '../../../../../model/tower'; +import { ColoredBlock, Tower } from '../../../../../model/tower'; @Component({ selector: 'app-block', @@ -9,7 +9,7 @@ import { Tower } from '../../../../../model/tower'; styleUrls: ['./block.component.scss'] }) export class BlockComponent { - @Input() block: Block; + @Input() block: ColoredBlock; @Input() tower: Tower; constructor(private modalService: ModalService) {} @@ -22,9 +22,12 @@ export class BlockComponent { description: this.block.description, isDone: this.block.isDone }); - this.block.tag = selected; - this.block.description = description; - this.block.isDone = isDone; + console.log(description); + this.block.changeProperties({ + tag: selected, + description, + isDone + }); } catch { // pass } diff --git a/src/app/components/pages/page/tower/tasks/tasks.component.html b/src/app/components/pages/page/tower/tasks/tasks.component.html index 7712a63..afe2543 100644 --- a/src/app/components/pages/page/tower/tasks/tasks.component.html +++ b/src/app/components/pages/page/tower/tasks/tasks.component.html @@ -6,8 +6,8 @@ {{ tasks.length == 0 ? '​' : tasks.length == 1 ? 'task' : 'tasks' }}

-
-
+
+

diff --git a/src/app/components/pages/page/tower/tasks/tasks.component.ts b/src/app/components/pages/page/tower/tasks/tasks.component.ts index 39b845f..17eb6e8 100644 --- a/src/app/components/pages/page/tower/tasks/tasks.component.ts +++ b/src/app/components/pages/page/tower/tasks/tasks.component.ts @@ -3,8 +3,8 @@ import { Block } from '../../../../../model/block'; import { Tower } from '../../../../../model/tower'; import { ModalService } from '../../../../../services/modal.service'; import { CancelService } from '../../../../../services/cancel.service'; -import { toHslString } from '../../../../../utils/color'; -import { IColor } from '../../../../../interfaces/persistance/color'; +import { IColor } from '../../../../../interfaces/color'; +import { IBlock } from '../../../../../interfaces/persistance/block'; @Component({ selector: 'app-tasks', @@ -12,8 +12,6 @@ import { IColor } from '../../../../../interfaces/persistance/color'; styleUrls: ['./tasks.component.scss'] }) export class TasksComponent implements OnInit { - readonly toHslString = toHslString; - @Input() tasks: Array; @Input() tower: Tower; @@ -48,12 +46,16 @@ export class TasksComponent implements OnInit { isDone: block.isDone }); - block.tag = selected; - block.description = description; + const change: Partial = { + tag: selected, + description, + isDone + }; if (!block.isDone && isDone) { - block.created = new Date(); + change.created = new Date(); } - block.isDone = isDone; + + block.changeProperties(change); } catch { // pass } diff --git a/src/app/components/pages/page/tower/tower.component.html b/src/app/components/pages/page/tower/tower.component.html index 1a6f196..de6bcaf 100644 --- a/src/app/components/pages/page/tower/tower.component.html +++ b/src/app/components/pages/page/tower/tower.component.html @@ -18,7 +18,7 @@ id="tower-name" type="text" placeholder="name…" - [(ngModel)]="tower.name" - [ngStyle]="{ color: toHslString(tower?.baseColor) }" + [(ngModel)]="towerName" + [ngStyle]="{ color: tower?.baseColor | color }" />
diff --git a/src/app/components/pages/page/tower/tower.component.ts b/src/app/components/pages/page/tower/tower.component.ts index c3400ce..0f463c2 100644 --- a/src/app/components/pages/page/tower/tower.component.ts +++ b/src/app/components/pages/page/tower/tower.component.ts @@ -1,9 +1,6 @@ import { Component, Input } from '@angular/core'; -import { Tower } from '../../../../model/tower'; +import { ColoredBlock, Tower } from '../../../../model/tower'; import { ModalService } from '../../../../services/modal.service'; -import { Block } from '../../../../model/block'; -import { IColor } from '../../../../interfaces/persistance/color'; -import { toHslString } from '../../../../utils/color'; @Component({ selector: 'app-tower', @@ -11,7 +8,13 @@ import { toHslString } from '../../../../utils/color'; styleUrls: ['./tower.component.scss'] }) export class TowerComponent { - readonly toHslString = toHslString; + get towerName(): string { + return this.tower.name; + } + + set towerName(value: string) { + this.tower.changeName(value); + } @Input() set dateRange(value: { from: Date; to: Date }) { if (this.dateRange !== undefined && this.dateRange.from === value.from && this.dateRange.to === value.to) { @@ -26,13 +29,13 @@ export class TowerComponent { public constructor(private modalService: ModalService) {} - get drawableBlocks(): Array { + get drawableBlocks(): Array { return this.tower.coloredBlocks.filter( block => this.dateRange.from <= block.created && block.created <= this.dateRange.to && block.isDone ); } - get tasks(): Array { + get tasks(): Array { return this.tower.coloredBlocks.filter( block => this.dateRange.from <= block.created && block.created <= this.dateRange.to && !block.isDone ); diff --git a/src/app/interfaces/color.ts b/src/app/interfaces/color.ts new file mode 100644 index 0000000..a414a2a --- /dev/null +++ b/src/app/interfaces/color.ts @@ -0,0 +1,5 @@ +export interface IColor { + h: number; + s: number; + l: number; +} diff --git a/src/app/interfaces/persistance/block.ts b/src/app/interfaces/persistance/block.ts index 75be455..5021ea3 100644 --- a/src/app/interfaces/persistance/block.ts +++ b/src/app/interfaces/persistance/block.ts @@ -1,7 +1,4 @@ -import { Typed } from './typed'; - -export interface IBlock extends Typed { - type: 'Block'; +export interface IBlock { created: Date; tag: string; isDone: boolean; diff --git a/src/app/interfaces/persistance/color.ts b/src/app/interfaces/persistance/color.ts deleted file mode 100644 index 1a49186..0000000 --- a/src/app/interfaces/persistance/color.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Typed } from './typed'; - -export interface IColor extends Typed { - type: 'Color'; - h: number; - s: number; - l: number; -} diff --git a/src/app/interfaces/persistance/page.ts b/src/app/interfaces/persistance/page.ts index ba8ed10..43dd10a 100644 --- a/src/app/interfaces/persistance/page.ts +++ b/src/app/interfaces/persistance/page.ts @@ -1,8 +1,6 @@ import { ITower } from './tower'; -import { Typed } from './typed'; -export interface IPage extends Typed { - type: 'Page'; +export interface IPage { name: string; towers: ITower[]; diff --git a/src/app/interfaces/persistance/tower.ts b/src/app/interfaces/persistance/tower.ts index 352a674..4776161 100644 --- a/src/app/interfaces/persistance/tower.ts +++ b/src/app/interfaces/persistance/tower.ts @@ -1,9 +1,7 @@ import { IBlock } from './block'; -import { IColor } from './color'; -import { Typed } from './typed'; +import { IColor } from '../color'; -export interface ITower extends Typed { - type: 'Tower'; +export interface ITower { name: string; blocks: IBlock[]; baseColor: IColor; diff --git a/src/app/interfaces/persistance/typed.ts b/src/app/interfaces/persistance/typed.ts deleted file mode 100644 index 7d34e2e..0000000 --- a/src/app/interfaces/persistance/typed.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface Typed { - type: string; -} diff --git a/src/app/interfaces/vector.ts b/src/app/interfaces/vector.ts deleted file mode 100644 index 332d2bf..0000000 --- a/src/app/interfaces/vector.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface Vector { - x: number; - y: number; -} diff --git a/src/app/model/block.ts b/src/app/model/block.ts index 06fd0f0..cf75711 100644 --- a/src/app/model/block.ts +++ b/src/app/model/block.ts @@ -1,20 +1,30 @@ import { Serializable } from './serializable'; import { IBlock } from '../interfaces/persistance/block'; -import { Node } from '../storage/node'; +import { Node } from '../store/node'; export class Block extends Serializable implements IBlock { constructor(parent: Node, props: IBlock) { super(parent, props); + this.onAfterClone(); + } + protected onAfterClone(): void { if (this.created.constructor.name !== 'Date') { this.created = new Date(this.created); } + + // TODO: remove. + if (this.isDone === null || this.isDone === undefined) { + this.isDone = false; + } + } + + changeProperties(values: Partial) { + this.changeKeys(values); } - // Only here to prevent ts warnings. - type: 'Block'; created: Date; isDone: boolean; - description: string; - tag: string; + readonly description: string; + readonly tag: string; } diff --git a/src/app/model/page.ts b/src/app/model/page.ts index dd9424d..bad43c3 100644 --- a/src/app/model/page.ts +++ b/src/app/model/page.ts @@ -1,21 +1,19 @@ import { Serializable } from './serializable'; import { IPage } from '../interfaces/persistance/page'; import { Tower } from './tower'; -import { Node } from '../storage/node'; +import { Node } from '../store/node'; export class Page extends Serializable implements IPage { constructor(parent: Node, props: IPage) { super(parent, props); } - // Only here to prevent ts warnings. - name: string; + readonly name: string; get towers(): Array { return this.children as Array; } - type: 'Page'; - userData: { + readonly userData: { hideCreateTowerButton: boolean; defaultDateRange: { from: Date; @@ -38,10 +36,14 @@ export class Page extends Serializable implements IPage { return; } - this.map(page => { - const tower = page.towers[previousIndex]; - page.towers.splice(previousIndex, 1); - page.towers.splice(currentIndex, 0, tower); + const towers = [...this.towers]; + const tower = towers[previousIndex]; + towers.splice(previousIndex, 1); + towers.splice(currentIndex, 0, tower); + + this.changeValue({ + oldValue: this.towers, + newValue: towers }); } @@ -52,10 +54,9 @@ export class Page extends Serializable implements IPage { } while (30 <= hue && hue <= 200); new Tower(this, { - type: 'Tower', name, blocks: [], - baseColor: { h: hue, s: 100, l: 50, type: 'Color' } + baseColor: { h: hue, s: 100, l: 50 } }); } diff --git a/src/app/model/serializable.ts b/src/app/model/serializable.ts index 36f1967..41f0e2b 100644 --- a/src/app/model/serializable.ts +++ b/src/app/model/serializable.ts @@ -1,8 +1,7 @@ -import { Cloneable } from '../storage/cloneable'; -import { Node } from '../storage/node'; +import { Cloneable } from '../store/cloneable'; +import { Node } from '../store/node'; export class Serializable extends Cloneable { - type: string; private static propertyList: any = {}; static childrenMap: { [type: string]: { @@ -11,7 +10,11 @@ export class Serializable extends Cloneable { }; }; - constructor(parent: Node, properties: any) { + protected onAfterClone(): void { + // pass + } + + protected constructor(parent: Node, properties: any) { super(parent); const type = this.constructor.name; diff --git a/src/app/model/tower.ts b/src/app/model/tower.ts index 90c1323..e0d7a24 100644 --- a/src/app/model/tower.ts +++ b/src/app/model/tower.ts @@ -3,44 +3,39 @@ import { lighten } from '../utils/color'; import { Block } from './block'; import { Serializable } from './serializable'; import { hash } from '../utils/hash'; -import { Node } from '../storage/node'; -import { IColor } from '../interfaces/persistance/color'; +import { Node } from '../store/node'; +import { IColor } from '../interfaces/color'; + +export type ColoredBlock = Block & { color: IColor }; export class Tower extends Serializable implements ITower { - constructor(parent: Node, props: ITower) { - super(parent, props); - - this.blocks.sort((a, b) => a.created.getTime() - b.created.getTime()); - this.calculateTagList(); - } - tags: string[]; - - // Only here to prevent ts warnings. name: string; - type: 'Tower'; + get blocks(): Array { return this.children as Array; } - baseColor: IColor; - get coloredBlocks(): Array { - return this.children.map(b => { - const coloredBlock = b as Block & { color: IColor }; + coloredBlocks: Array; + + readonly baseColor: IColor; + + constructor(parent: Node, props: ITower) { + super(parent, props); + this.onAfterClone(); + } + + protected onAfterClone(): void { + this.blocks.sort((a, b) => { + return a.created.getTime() - b.created.getTime(); + }); + + this.coloredBlocks = this.blocks.map(b => { + const coloredBlock = b as ColoredBlock; coloredBlock.color = lighten((hash(coloredBlock.tag) - 0.5) * 50, this.baseColor); return coloredBlock; }); - } - addBlock(props: { tag: string; description: string; isDone: boolean }) { - new Block(this, { - created: new Date(), - ...props, - type: 'Block' - }); - } - - private calculateTagList() { this.tags = []; for (const block of this.blocks) { if (!this.tags.includes(block.tag)) { @@ -48,4 +43,17 @@ export class Tower extends Serializable implements ITower { } } } + + addBlock(props: { tag: string; description: string; isDone: boolean }) { + new Block(this, { + created: new Date(), + ...props + }); + } + + changeName(newName: string) { + // For optimization purposes. + this.name = newName; + this.mutatedUpdate(); + } } diff --git a/src/app/pipes/color.pipe.ts b/src/app/pipes/color.pipe.ts new file mode 100644 index 0000000..d68241f --- /dev/null +++ b/src/app/pipes/color.pipe.ts @@ -0,0 +1,12 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { IColor } from '../interfaces/color'; +import { toHslString } from '../utils/color'; + +@Pipe({ + name: 'color' +}) +export class ColorPipe implements PipeTransform { + transform(color: IColor, args?: any): string { + return toHslString(color); + } +} diff --git a/src/app/services/data.service.ts b/src/app/services/data.service.ts index 95ce3cf..e939076 100644 --- a/src/app/services/data.service.ts +++ b/src/app/services/data.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { StoreService } from './store.service'; import { Page } from '../model/page'; -import { Root } from '../storage/root'; +import { Root } from '../store/root'; import { Serializable } from '../model/serializable'; import { Tower } from '../model/tower'; import { Block } from '../model/block'; @@ -47,15 +47,22 @@ export class DataService extends Root { this.children$.subscribe(value => { this.log(); this._safeChildren.next(value); - this.storeService.scheduleSave(this.pages); + this.save(0); }); } + mutatedUpdate() { + this.save(2500); + } + + save(timeout: number) { + this.storeService.scheduleSave(this.pages, timeout); + } + addPage(name: string) { new Page(this, { name, userData: {}, - type: 'Page', towers: [] }); } diff --git a/src/app/services/store.service.ts b/src/app/services/store.service.ts index 8b677dc..2a029a8 100644 --- a/src/app/services/store.service.ts +++ b/src/app/services/store.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { Page } from '../model/page'; import { IPage } from '../interfaces/persistance/page'; -const LOCAL_STORAGE_KEY = 'life-towers.data.v.3'; +const LOCAL_STORAGE_KEY = 'life-towers.data.v.2'; @Injectable({ providedIn: 'root' @@ -15,84 +15,77 @@ export class StoreService { { name: 'Work & life', userData: {}, - type: 'Page', towers: [ { name: 'work', - baseColor: { h: 0, s: 100, l: 50, type: 'Color' }, - type: 'Tower', + baseColor: { h: 0, s: 100, l: 50 }, blocks: [ { created: new Date(2015, 2, 13), tag: 'a', description: 'done it', - isDone: true, - type: 'Block' + isDone: true }, { created: new Date(2016, 2, 15), tag: 'go to school', description: 'done it', - type: 'Block' + isDone: false }, { created: new Date(2017, 2, 15), tag: 'go to work', - isDone: true, - type: 'Block' + isDone: true }, { created: new Date(2018, 2, 13), tag: 'go to work', description: 'done it', - isDone: true, - type: 'Block' + isDone: true }, { created: new Date(2019, 3, 13), tag: 'go to work', - type: 'Block' + isDone: false }, { created: new Date(2020, 2, 15), tag: 'go to school', description: 'done it', - isDone: true, - type: 'Block' + isDone: true }, { created: new Date(2021, 2, 15), tag: 'go to school', - type: 'Block' + isDone: false } ] }, { - baseColor: { h: 180, s: 100, l: 50, type: 'Color' }, + baseColor: { h: 180, s: 100, l: 50 }, name: 'life', - type: 'Tower', blocks: [ { created: new Date(2019, 3, 13), tag: 'go home', description: 'done it', - type: 'Block' + isDone: false }, { created: new Date(2019, 4, 13), tag: 'go home', - type: 'Block' + isDone: false }, { created: new Date(2019, 4, 15), tag: 'go to work', description: 'done it', - type: 'Block' + isDone: false }, { created: new Date(2019, 4, 15, 14), tag: 'go to work', - type: 'Block' + isDone: false } ] } @@ -105,14 +98,14 @@ export class StoreService { this.storedData = JSON.parse(localStorageData ? localStorageData : this.mockData) as T; } - scheduleSave(data: T) { + scheduleSave(data: T, timeout: number) { this.dataToSave = data; if (!this.saveScheduled) { this.saveScheduled = true; setTimeout(() => { this.saveScheduled = false; this.save(this.dataToSave).catch(); - }, 0); + }, timeout); } } diff --git a/src/app/storage/cloneable.ts b/src/app/store/cloneable.ts similarity index 56% rename from src/app/storage/cloneable.ts rename to src/app/store/cloneable.ts index 767b5f0..317e279 100644 --- a/src/app/storage/cloneable.ts +++ b/src/app/store/cloneable.ts @@ -1,11 +1,13 @@ import { InnerNode } from './inner-node'; import { Node } from './node'; -export class Cloneable extends InnerNode { - constructor(parent: Node) { +export abstract class Cloneable extends InnerNode { + protected constructor(parent: Node) { super(parent); } + protected abstract onAfterClone(): void; + protected cloneWithMap(map: (node: this) => void): this { const insides = Object.getOwnPropertyDescriptors(this); @@ -20,8 +22,8 @@ export class Cloneable extends InnerNode { return value.bind(proxy); } return value; - } else if (this.hasOwnProperty(prop)) { - const value = this[prop]; + } else if (target.prototype.hasOwnProperty(prop)) { + const value = target.prototype[prop]; if (typeof value === 'function') { return value.bind(proxy); } @@ -34,28 +36,44 @@ export class Cloneable extends InnerNode { }); map(insidesProxy); - (insidesProxy.__target__).id.value = Node.id++; - (insidesProxy.__target__).copyCount.value++; - Node.sumCopyCount++; - - return Object.create(Object.getPrototypeOf(this), insidesProxy.__target__); + return this.cloneFromInsides(insidesProxy.__target__); } - protected cloneWithAdd({ value, propertyName }: { value: any; propertyName: string }): this { + protected cloneWithAdd({ propertyName, value }: { value: any; propertyName: string }): this { + if (this[propertyName] === value) { + return this; + } + const insides = Object.getOwnPropertyDescriptors(this); insides[propertyName].value = value; - insides.id.value = Node.id++; - insides.copyCount.value++; - Node.sumCopyCount++; + return this.cloneFromInsides(insides); + } - return Object.create(Object.getPrototypeOf(this), insides); + protected cloneWithChangedKeys(props: { [propertyName: string]: any }): this { + const insides = Object.getOwnPropertyDescriptors(this); + + for (let key in props) { + if (props.hasOwnProperty(key)) { + if (insides.hasOwnProperty(key)) { + insides[key].value = props[key]; + } else { + // @ts-ignore + insides[key] = { + value: props[key] + }; + } + } + } + + return this.cloneFromInsides(insides); } protected cloneWithModify({ oldValue, newValue }: { oldValue: any; newValue: any }): this { + if (oldValue === newValue) { + return this; + } + const insides = Object.getOwnPropertyDescriptors(this); - insides.id.value = Node.id++; - insides.copyCount.value++; - Node.sumCopyCount++; let wasMatch = false; for (let name in insides) { @@ -69,6 +87,16 @@ export class Cloneable extends InnerNode { throw new TypeError(`Object has no property with value: ${oldValue.toString()}`); } - return Object.create(Object.getPrototypeOf(this), insides); + return this.cloneFromInsides(insides); + } + + private cloneFromInsides(insides): this { + insides.id.value = Node.id++; + insides.copyCount.value++; + Node.sumCopyCount++; + + const clone = Object.create(Object.getPrototypeOf(this), insides); + clone.onAfterClone(); + return clone; } } diff --git a/src/app/storage/inner-node.ts b/src/app/store/inner-node.ts similarity index 67% rename from src/app/storage/inner-node.ts rename to src/app/store/inner-node.ts index 06425d6..655ca69 100644 --- a/src/app/storage/inner-node.ts +++ b/src/app/store/inner-node.ts @@ -1,4 +1,5 @@ import { Node } from './node'; +import { observableToBeFn } from 'rxjs/internal/testing/TestScheduler'; export abstract class InnerNode extends Node { parent: Node; @@ -17,21 +18,31 @@ export abstract class InnerNode extends Node { protected constructor(parent: Node) { super(); - parent.addChild({ - value: this - }); + new Promise(r => r()).then(() => + parent.addChild({ + value: this + }) + ); + } + + mutatedUpdate() { + this.parent.mutatedUpdate(); } map(map: (a: this) => void) { return this.update((self: this) => this.cloneWithMap.call(self, map)); } - changeKey(update: { propertyName: string; value: any }): this { - return this.update((self: this) => this.cloneWithAdd.call(self, update)); + changeKey({ propertyName, value }: { propertyName: string; value: any }): this { + return this.update((self: this) => this.cloneWithAdd.call(self, { propertyName, value })); } - changeValue(update: { oldValue: any; newValue: any }): this { - return this.update((self: this) => this.cloneWithModify.call(self, update)); + changeKeys(props: { [propertyName: string]: any }): this { + return this.update((self: this) => this.cloneWithChangedKeys.call(self, props)); + } + + changeValue({ oldValue, newValue }: { oldValue: any; newValue: any }): this { + return this.update((self: this) => this.cloneWithModify.call(self, { oldValue, newValue })); } addChild(update: { value: InnerNode }) { @@ -44,6 +55,7 @@ export abstract class InnerNode extends Node { protected abstract cloneWithMap(map: (a: this) => void): this; protected abstract cloneWithAdd(update: { value: any; propertyName: string }): this; + protected abstract cloneWithChangedKeys(props: { [propertyName: string]: any }): this; protected abstract cloneWithModify(update: { oldValue: any; newValue: any }): this; private update(cloneMethod: (self: this) => this): this { @@ -52,6 +64,10 @@ export abstract class InnerNode extends Node { } const clone = cloneMethod(this); + if (clone === this) { + return this; + } + for (let child of clone.children) { child.parent = clone; } diff --git a/src/app/storage/node.ts b/src/app/store/node.ts similarity index 93% rename from src/app/storage/node.ts rename to src/app/store/node.ts index 81e29f6..c9d1384 100644 --- a/src/app/storage/node.ts +++ b/src/app/store/node.ts @@ -14,6 +14,8 @@ export abstract class Node { Node.sumCopyCount++; } + abstract mutatedUpdate(): void; + addChild(update: { value: InnerNode }) { this.changeValue({ oldValue: this.children, @@ -22,6 +24,10 @@ export abstract class Node { } changeChild({ oldValue, newValue }: { oldValue: InnerNode; newValue: InnerNode }) { + if (oldValue === newValue) { + return; + } + this.changeValue({ oldValue: this.children, newValue: this.children.map(c => (c === oldValue ? newValue : c)) diff --git a/src/app/storage/root.ts b/src/app/store/root.ts similarity index 96% rename from src/app/storage/root.ts rename to src/app/store/root.ts index 1cbeed9..54c62e8 100644 --- a/src/app/storage/root.ts +++ b/src/app/store/root.ts @@ -15,6 +15,10 @@ export class Root extends Node { this._children.next(value); } + mutatedUpdate() { + // pass + } + changeValue({ oldValue, newValue }: { oldValue: any; newValue: any }) { if (this.children !== oldValue) { throw new TypeError('Only children can be changed.'); diff --git a/src/app/utils/color.ts b/src/app/utils/color.ts index 7d7eb85..3e15f5e 100644 --- a/src/app/utils/color.ts +++ b/src/app/utils/color.ts @@ -1,4 +1,4 @@ -import { IColor } from '../interfaces/persistance/color'; +import { IColor } from '../interfaces/color'; export const lighten = (by: number, { h, s, l }: IColor): IColor => { let newL = l + by; @@ -8,7 +8,7 @@ export const lighten = (by: number, { h, s, l }: IColor): IColor => { newL = 0; } - return { h, s, l: newL, type: 'Color' }; + return { h, s, l: newL }; }; export const toHslString = ({ h, s, l }: IColor): string => {