From 3861c1e72f3d83e650ad733a599bdd8b6836a726 Mon Sep 17 00:00:00 2001 From: garrettmills Date: Sat, 8 Feb 2020 11:19:00 -0600 Subject: [PATCH] Task #11, Task #6 - add click_link type, saving/restore support to the editor --- .../editor/host/host.component.html | 12 +-- .../editor/host/host.component.scss | 9 +++ .../components/editor/host/host.component.ts | 8 ++ src/app/pages/editor/editor.page.html | 11 ++- src/app/pages/editor/editor.page.ts | 49 +++++++++++- src/app/service/api.service.spec.ts | 12 --- src/app/service/api.service.ts | 4 +- src/app/service/page.service.ts | 74 +++++++++++++++++++ src/app/structures/HostRecord.ts | 47 +++++++++++- src/app/structures/PageRecord.ts | 58 +++++++++++++++ src/environments/environment.prod.ts | 2 +- 11 files changed, 255 insertions(+), 31 deletions(-) delete mode 100644 src/app/service/api.service.spec.ts create mode 100644 src/app/service/page.service.ts create mode 100644 src/app/structures/PageRecord.ts diff --git a/src/app/components/editor/host/host.component.html b/src/app/components/editor/host/host.component.html index c42ebd0..089317b 100644 --- a/src/app/components/editor/host/host.component.html +++ b/src/app/components/editor/host/host.component.html @@ -1,13 +1,13 @@
- {{ record.value }} -
+ [ngClass]="{'paragraph': record.type === 'paragraph', 'header1': record.type === 'header1', 'header2': record.type === 'header2', 'header3': record.type === 'header3', 'header4': record.type === 'header4', 'block_code': record.type === 'block_code', 'click_link': record.type === 'click_link'}" + [innerHTML]="record.value.replace('\n', '
')" + >
diff --git a/src/app/components/editor/host/host.component.scss b/src/app/components/editor/host/host.component.scss index 2d6a910..fa5627c 100644 --- a/src/app/components/editor/host/host.component.scss +++ b/src/app/components/editor/host/host.component.scss @@ -24,3 +24,12 @@ background-color: #ddd; margin-bottom: 15px; } + +.host-host.click_link { + color: #0141b0; + cursor: pointer; + + &:hover { + text-decoration: underline; + } +} diff --git a/src/app/components/editor/host/host.component.ts b/src/app/components/editor/host/host.component.ts index 3cdcca5..cdde888 100644 --- a/src/app/components/editor/host/host.component.ts +++ b/src/app/components/editor/host/host.component.ts @@ -40,9 +40,17 @@ export class HostComponent implements OnInit { this.record.type = 'header4'; } else if ( innerText.startsWith('```') ) { this.record.type = 'block_code'; + } else if ( innerText.startsWith('http') ) { + this.record.type = 'click_link'; } else { this.record.type = 'paragraph'; } } + onHostDblClick() { + if ( this.record.type === 'click_link' ) { + window.open(this.record.value.trim(), '_blank'); + } + } + } diff --git a/src/app/pages/editor/editor.page.html b/src/app/pages/editor/editor.page.html index 4fe70b8..d761f94 100644 --- a/src/app/pages/editor/editor.page.html +++ b/src/app/pages/editor/editor.page.html @@ -1,17 +1,12 @@ - Note Editor + {{ pageRecord.Name }} -
- Save -
- Hello, world! -
+
+ Add Node + Save +
diff --git a/src/app/pages/editor/editor.page.ts b/src/app/pages/editor/editor.page.ts index 5d6f13e..5d8c37e 100644 --- a/src/app/pages/editor/editor.page.ts +++ b/src/app/pages/editor/editor.page.ts @@ -1,5 +1,7 @@ -import {Component, OnInit, ViewChildren} from '@angular/core'; +import {Component, Host, OnInit, ViewChild, ViewChildren} from '@angular/core'; import HostRecord from '../../structures/HostRecord'; +import PageRecord from '../../structures/PageRecord'; +import {PageService} from '../../service/page.service'; @Component({ selector: 'app-editor', @@ -7,16 +9,43 @@ import HostRecord from '../../structures/HostRecord'; styleUrls: ['./editor.page.scss'], }) export class EditorPage implements OnInit { - public hostRecords: Array = [new HostRecord('I am a record.')]; + public hostRecords: Array = [new HostRecord('Click to edit page...')]; + public pageRecord: PageRecord = new PageRecord(); @ViewChildren('editorHosts') editorHosts; + @ViewChild('titleBar', {static: false}) titleBar; - constructor() { } + constructor( + protected pages: PageService, + ) { } ngOnInit() { console.log('Editor component: ', this); } + ionViewDidEnter() { + const pageId = prompt('What is the ID of the page to load?'); + this.pages.load(pageId).subscribe(pageRecord => { + this.pageRecord = pageRecord; + this.pages.get_nodes(pageRecord).subscribe((hosts: Array) => { + this.hostRecords = hosts; + }); + }); + } + + onAddClick() { + this.hostRecords.push(new HostRecord('')); + setTimeout(() => { + const host = this.editorHosts.toArray().reverse()[0].hostContainer.nativeElement; + const s = window.getSelection(); + const r = document.createRange(); + r.setStart(host, 0); + r.setEnd(host, 0); + s.removeAllRanges(); + s.addRange(r); + }, 0); + } + onNewHostRequested($event) { const insertAfter = this.getIndexFromRecord($event.record); const record = new HostRecord(''); @@ -87,4 +116,18 @@ export class EditorPage implements OnInit { return index; } + onSaveClick() { + this.pageRecord.Name = this.titleBar.el.innerText.trim(); + + // First, save the page record itself + this.pages.save(this.pageRecord).subscribe(pageRecord => { + this.pageRecord = pageRecord; + + // Now, save the nodes + this.pages.save_nodes(pageRecord, this.hostRecords).subscribe(result => { + this.hostRecords = result; + }); + }); + } + } diff --git a/src/app/service/api.service.spec.ts b/src/app/service/api.service.spec.ts deleted file mode 100644 index 9eb2c83..0000000 --- a/src/app/service/api.service.spec.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { ApiService } from './api.service'; - -describe('ApiService', () => { - beforeEach(() => TestBed.configureTestingModule({})); - - it('should be created', () => { - const service: ApiService = TestBed.get(ApiService); - expect(service).toBeTruthy(); - }); -}); diff --git a/src/app/service/api.service.ts b/src/app/service/api.service.ts index fa94de1..1fced0b 100644 --- a/src/app/service/api.service.ts +++ b/src/app/service/api.service.ts @@ -24,11 +24,11 @@ export class ApiService { public request(endpoint, params = {}, method: 'get'|'post' = 'get'): Observable { return new Observable(sub => { - const data: any = {} + let data: any = {} if ( method === 'get' ) { data.params = params; } else { - data.body = params; + data = params; } this.http[method](this._build_url(endpoint), data).subscribe({ diff --git a/src/app/service/page.service.ts b/src/app/service/page.service.ts new file mode 100644 index 0000000..a6580f0 --- /dev/null +++ b/src/app/service/page.service.ts @@ -0,0 +1,74 @@ +import {Host, Injectable} from '@angular/core'; +import {Observable} from 'rxjs'; +import PageRecord from '../structures/PageRecord'; +import {ApiService} from './api.service'; +import HostRecord from '../structures/HostRecord'; + +@Injectable({ + providedIn: 'root' +}) +export class PageService { + + constructor( + protected api: ApiService, + ) { } + + load(id: string): Observable { + return new Observable(sub => { + this.api.get(`/page/${id}`).subscribe(response => { + if ( response.status === 200 ) { + sub.next(new PageRecord(response.data)); + sub.complete(); + } else { + throw new Error(response.message); + } + }); + }); + } + + save(page: PageRecord): Observable { + return new Observable(sub => { + this.api.post(`/page/${page.UUID}/save`, page.toSave()).subscribe(res => { + sub.next(new PageRecord(res.data)); + sub.complete(); + }); + }); + } + + get_nodes(page: PageRecord): Observable> { + return new Observable>(sub => { + this.api.get(`/page/${page.UUID}/nodes`).subscribe(res => { + const returns = []; + res.data.forEach(rec => { + const host = new HostRecord(rec.Value.Value); + host.load(rec); + returns.push(host); + }); + + sub.next(returns); + sub.complete(); + }); + }); + } + + save_nodes(page: PageRecord, nodes: Array): Observable> { + return new Observable>(sub => { + nodes = nodes.map(x => { + x.PageId = page.UUID; + return x.toSave(); + }); + + this.api.post(`/page/${page.UUID}/nodes/save`, nodes).subscribe(res => { + const returns = []; + res.data.forEach(rec => { + const host = new HostRecord(rec.Value.Value); + host.load(rec); + returns.push(host); + }); + + sub.next(returns); + sub.complete(); + }); + }); + } +} diff --git a/src/app/structures/HostRecord.ts b/src/app/structures/HostRecord.ts index 82d591a..c158603 100644 --- a/src/app/structures/HostRecord.ts +++ b/src/app/structures/HostRecord.ts @@ -1,9 +1,54 @@ export default class HostRecord { public value = ''; + public type: 'paragraph'|'header1'|'header2'|'header3'|'header4'|'block_code'|'click_link' = 'paragraph'; - public type: 'paragraph'|'header1'|'header2'|'header3'|'header4'|'block_code' = 'paragraph'; + public CreatedAt: string; + public PageId: string; + public UUID: string; + public UpdatedAt: string; + public Value: any; constructor(value = '') { this.value = value; } + + load(data: any) { + this.type = data.Type; + + [ + 'CreatedAt', + 'PageId', + 'UUID', + 'UpdatedAt', + 'Value', + ].forEach(field => { + if ( field in data ) { + this[field] = data[field]; + } + }); + } + + toSave() { + const data: any = { + Type: this.type + }; + + [ + 'CreatedAt', + 'PageId', + 'UUID', + 'UpdatedAt', + 'Value', + ].forEach(field => { + if ( field in this ) { + data[field] = this[field]; + } + }); + + if ( !data.Value ) { + data.Value = {}; + } + data.Value.Value = this.value; + return data; + } } diff --git a/src/app/structures/PageRecord.ts b/src/app/structures/PageRecord.ts new file mode 100644 index 0000000..8350912 --- /dev/null +++ b/src/app/structures/PageRecord.ts @@ -0,0 +1,58 @@ +export default class PageRecord { + public UUID: string; + public Name: string; + public OrgUserId: string; + public IsPublic = true; + public IsVisibleInMenu = true; + public ParentId: string; + public NodeIds: Array; + public CreatedAt: Date; + public UpdatedAt: Date; + public CreatedUserId: string; + public UpdateUserId: string; + public ChildPageIds: Array; + + constructor(data: any = {Name: 'Click to edit title...'}) { + [ + 'UUID', + 'Name', + 'OrgUserId', + 'IsPublic', + 'IsVisibleInMenu', + 'ParentId', + 'NodeIds', + 'CreatedAt', + 'UpdatedAt', + 'CreatedUserId', + 'UpdateUserId', + 'ChildPageIds' + ].forEach(field => { + if ( field in data ) { + this[field] = data[field]; + } + }); + } + + toSave() { + const data = {}; + [ + 'UUID', + 'Name', + 'OrgUserId', + 'IsPublic', + 'IsVisibleInMenu', + 'ParentId', + 'NodeIds', + 'CreatedAt', + 'UpdatedAt', + 'CreatedUserId', + 'UpdateUserId', + 'ChildPageIds' + ].forEach(field => { + if ( field in this ) { + data[field] = this[field]; + } + }); + return data; + } +} diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 1c7097a..be8c9c6 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -1,4 +1,4 @@ export const environment = { production: true, - backendBase: '/', + backendBase: '/api/v1', };