Show read-only editor in versions (not actually versioning yet)
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
26e8d6ecd6
commit
ab811bb54c
@ -17,7 +17,7 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'editor',
|
path: 'editor',
|
||||||
loadChildren: () => import('./pages/editor/editor.module').then( m => m.EditorPageModule)
|
loadChildren: () => import('./components/components.module').then( m => m.ComponentsModule)
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -31,6 +31,8 @@ import {MarkdownComponent as MarkdownEditorComponent} from './nodes/markdown/mar
|
|||||||
import {DirectivesModule} from '../directives/directives.module';
|
import {DirectivesModule} from '../directives/directives.module';
|
||||||
import {MarkdownModule} from 'ngx-markdown';
|
import {MarkdownModule} from 'ngx-markdown';
|
||||||
import {VersionModalComponent} from './version-modal/version-modal.component';
|
import {VersionModalComponent} from './version-modal/version-modal.component';
|
||||||
|
import {EditorPageRoutingModule} from '../pages/editor/editor-routing.module';
|
||||||
|
import {EditorPage} from '../pages/editor/editor.page';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -58,6 +60,7 @@ import {VersionModalComponent} from './version-modal/version-modal.component';
|
|||||||
NormComponent,
|
NormComponent,
|
||||||
MarkdownEditorComponent,
|
MarkdownEditorComponent,
|
||||||
VersionModalComponent,
|
VersionModalComponent,
|
||||||
|
EditorPage,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@ -69,6 +72,7 @@ import {VersionModalComponent} from './version-modal/version-modal.component';
|
|||||||
MonacoEditorModule,
|
MonacoEditorModule,
|
||||||
DirectivesModule,
|
DirectivesModule,
|
||||||
MarkdownModule,
|
MarkdownModule,
|
||||||
|
EditorPageRoutingModule,
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
NodePickerComponent,
|
NodePickerComponent,
|
||||||
@ -95,6 +99,7 @@ import {VersionModalComponent} from './version-modal/version-modal.component';
|
|||||||
NormComponent,
|
NormComponent,
|
||||||
MarkdownEditorComponent,
|
MarkdownEditorComponent,
|
||||||
VersionModalComponent,
|
VersionModalComponent,
|
||||||
|
EditorPage,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
NodePickerComponent,
|
NodePickerComponent,
|
||||||
@ -121,6 +126,7 @@ import {VersionModalComponent} from './version-modal/version-modal.component';
|
|||||||
NormComponent,
|
NormComponent,
|
||||||
MarkdownEditorComponent,
|
MarkdownEditorComponent,
|
||||||
VersionModalComponent,
|
VersionModalComponent,
|
||||||
|
EditorPage,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class ComponentsModule {}
|
export class ComponentsModule {}
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
</ion-item>
|
</ion-item>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
<div class="preview">
|
<div class="preview">
|
||||||
Page preview will be here.
|
<app-editor [pageId]="pageId" *ngIf="selectedVersion" [hosted]="true" [readonly]="true" #editor></app-editor>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
.contents {
|
.contents {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow-y: scroll;
|
overflow-y: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
||||||
@ -35,6 +35,8 @@
|
|||||||
|
|
||||||
.preview {
|
.preview {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
.version-date {
|
.version-date {
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import {Component, Input, OnInit} from '@angular/core';
|
import {Component, Input, OnInit, ViewChild} from '@angular/core';
|
||||||
import {AlertController, ModalController} from '@ionic/angular';
|
import {AlertController, ModalController} from '@ionic/angular';
|
||||||
import {EditorService} from '../../service/editor.service';
|
import {EditorService} from '../../service/editor.service';
|
||||||
import {PageVersionRecord} from '../../structures/PageRecord';
|
import {PageVersionRecord} from '../../structures/PageRecord';
|
||||||
|
import {EditorPage} from '../../pages/editor/editor.page';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-version-modal',
|
selector: 'app-version-modal',
|
||||||
@ -10,6 +11,8 @@ import {PageVersionRecord} from '../../structures/PageRecord';
|
|||||||
})
|
})
|
||||||
export class VersionModalComponent implements OnInit {
|
export class VersionModalComponent implements OnInit {
|
||||||
@Input() pageId: string;
|
@Input() pageId: string;
|
||||||
|
@ViewChild('editor') editor: EditorPage;
|
||||||
|
|
||||||
public pageVersions: PageVersionRecord[] = [];
|
public pageVersions: PageVersionRecord[] = [];
|
||||||
public selectedVersion?: PageVersionRecord;
|
public selectedVersion?: PageVersionRecord;
|
||||||
|
|
||||||
@ -23,7 +26,10 @@ export class VersionModalComponent implements OnInit {
|
|||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.pageVersions = await this.editorService.loadPageVersions(this.pageId);
|
this.pageVersions = await this.editorService.loadPageVersions(this.pageId);
|
||||||
await this.onVersionClick(this.pageVersions[0]);
|
this.selectedVersion = this.pageVersions[0];
|
||||||
|
setTimeout(() => {
|
||||||
|
this.onVersionClick(this.pageVersions[0]);
|
||||||
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
dismiss() {
|
dismiss() {
|
||||||
@ -32,5 +38,8 @@ export class VersionModalComponent implements OnInit {
|
|||||||
|
|
||||||
async onVersionClick(version: PageVersionRecord) {
|
async onVersionClick(version: PageVersionRecord) {
|
||||||
this.selectedVersion = version;
|
this.selectedVersion = version;
|
||||||
|
if ( this.editor ) {
|
||||||
|
this.editor.ionViewDidEnter();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { FormsModule } from '@angular/forms';
|
|
||||||
|
|
||||||
import { IonicModule } from '@ionic/angular';
|
|
||||||
|
|
||||||
import { EditorPageRoutingModule } from './editor-routing.module';
|
|
||||||
|
|
||||||
import { EditorPage } from './editor.page';
|
|
||||||
import {ComponentsModule} from '../../components/components.module';
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [
|
|
||||||
CommonModule,
|
|
||||||
FormsModule,
|
|
||||||
IonicModule,
|
|
||||||
EditorPageRoutingModule,
|
|
||||||
ComponentsModule
|
|
||||||
],
|
|
||||||
declarations: [EditorPage]
|
|
||||||
})
|
|
||||||
export class EditorPageModule {}
|
|
@ -7,6 +7,7 @@
|
|||||||
<ion-title #titleBar>
|
<ion-title #titleBar>
|
||||||
<ion-input
|
<ion-input
|
||||||
[(ngModel)]="editorService.mutablePageName"
|
[(ngModel)]="editorService.mutablePageName"
|
||||||
|
[readonly]="!editorService.canEdit()"
|
||||||
placeholder="Click to edit page name..."
|
placeholder="Click to edit page name..."
|
||||||
class="title-input"
|
class="title-input"
|
||||||
></ion-input>
|
></ion-input>
|
||||||
@ -43,7 +44,7 @@
|
|||||||
>
|
>
|
||||||
<div class="host-icons">
|
<div class="host-icons">
|
||||||
<i class="type-icon fa" [ngClass]="typeIcons[(node.isNorm() ? 'node' : node.type)] + ' ' + (node.isNorm() ? 'node' : node.type)"></i>
|
<i class="type-icon fa" [ngClass]="typeIcons[(node.isNorm() ? 'node' : node.type)] + ' ' + (node.isNorm() ? 'node' : node.type)"></i>
|
||||||
<button (click)="onOptionsClick($event, node)">
|
<button (click)="onOptionsClick($event, node)" *ngIf="editorService.canEdit()">
|
||||||
<i class="fa fa-ellipsis-v" title="Node options"></i>
|
<i class="fa fa-ellipsis-v" title="Node options"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -63,7 +64,7 @@
|
|||||||
<editor-files style="flex: 1;" [nodeId]="node.UUID" [editorUUID]="editorService.instanceUUID"></editor-files>
|
<editor-files style="flex: 1;" [nodeId]="node.UUID" [editorUUID]="editorService.instanceUUID"></editor-files>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
<button class="host-add-button" (click)="onAddClick($event)">
|
<button *ngIf="editorService.canEdit()" class="host-add-button" (click)="onAddClick($event)">
|
||||||
<i class="fa fa-plus"></i> Add Node
|
<i class="fa fa-plus"></i> Add Node
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,6 +17,17 @@ import {VersionModalComponent} from '../../components/version-modal/version-moda
|
|||||||
export class EditorPage implements OnInit {
|
export class EditorPage implements OnInit {
|
||||||
public typeIcons = NodeTypeIcons;
|
public typeIcons = NodeTypeIcons;
|
||||||
@Input() pageId: string;
|
@Input() pageId: string;
|
||||||
|
@Input() hosted = false;
|
||||||
|
@Input() version?: number;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set readonly(val: boolean) {
|
||||||
|
this.editorService.forceReadonly = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
get readonly() {
|
||||||
|
return this.editorService.forceReadonly;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected route: ActivatedRoute,
|
protected route: ActivatedRoute,
|
||||||
@ -39,7 +50,7 @@ export class EditorPage implements OnInit {
|
|||||||
ionViewDidEnter() {
|
ionViewDidEnter() {
|
||||||
if ( this.pageId ) {
|
if ( this.pageId ) {
|
||||||
this.editorService.startEditing(this.pageId);
|
this.editorService.startEditing(this.pageId);
|
||||||
} else {
|
} else if ( !this.hosted ) {
|
||||||
this.router.navigate(['/home']);
|
this.router.navigate(['/home']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
export interface RedirectAction {
|
|
||||||
type: 'redirect';
|
|
||||||
args: {
|
|
||||||
destination: string
|
|
||||||
blank?: boolean
|
|
||||||
appUrl?: boolean
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Action = RedirectAction;
|
|
@ -1,27 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Action } from './action.types';
|
|
||||||
import {SessionService} from './session.service';
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class ActionsService {
|
|
||||||
constructor(
|
|
||||||
protected readonly session: SessionService,
|
|
||||||
) { }
|
|
||||||
|
|
||||||
async perform(action: Action) {
|
|
||||||
if ( action.type === 'redirect' ) {
|
|
||||||
let destination = action.args.destination;
|
|
||||||
if ( action.args.appUrl ) {
|
|
||||||
destination = this.session.buildAppUrl(destination);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( action.args.blank ) {
|
|
||||||
window.open(destination, '_blank');
|
|
||||||
} else {
|
|
||||||
window.location.href = destination;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -31,6 +31,7 @@ export class EditorService {
|
|||||||
protected ready$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
protected ready$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
protected subs: Subscription[] = [];
|
protected subs: Subscription[] = [];
|
||||||
protected saving = false;
|
protected saving = false;
|
||||||
|
public forceReadonly = false;
|
||||||
protected saveTriggered = false;
|
protected saveTriggered = false;
|
||||||
public notAvailable = false;
|
public notAvailable = false;
|
||||||
public readonly instanceUUID: string;
|
public readonly instanceUUID: string;
|
||||||
@ -139,7 +140,7 @@ export class EditorService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
if ( !(await this.needsSave()) || this.saving ) {
|
if ( !(await this.needsSave()) || this.saving || this.forceReadonly ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,6 +171,10 @@ export class EditorService {
|
|||||||
throw new NoPageLoadedError();
|
throw new NoPageLoadedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( this.forceReadonly ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const nodeIndex = this.currentNodes.findIndex(maybeNode => maybeNode.UUID === node.UUID);
|
const nodeIndex = this.currentNodes.findIndex(maybeNode => maybeNode.UUID === node.UUID);
|
||||||
if ( nodeIndex < 0 ) {
|
if ( nodeIndex < 0 ) {
|
||||||
return;
|
return;
|
||||||
@ -417,6 +422,10 @@ export class EditorService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async needsSave() {
|
async needsSave() {
|
||||||
|
if ( this.forceReadonly ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if ( this.dirtyOverride ) {
|
if ( this.dirtyOverride ) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -432,6 +441,10 @@ export class EditorService {
|
|||||||
throw new NoPageLoadedError();
|
throw new NoPageLoadedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( this.forceReadonly ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const node = this.currentNodes.find(maybeNode => maybeNode.UUID === nodeId);
|
const node = this.currentNodes.find(maybeNode => maybeNode.UUID === nodeId);
|
||||||
if ( !node ) {
|
if ( !node ) {
|
||||||
throw new Error('Invalid node ID.');
|
throw new Error('Invalid node ID.');
|
||||||
@ -463,6 +476,10 @@ export class EditorService {
|
|||||||
throw new NoPageLoadedError();
|
throw new NoPageLoadedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( this.forceReadonly ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const baseHost = new HostRecord();
|
const baseHost = new HostRecord();
|
||||||
baseHost.type = type;
|
baseHost.type = type;
|
||||||
baseHost.PageId = this.currentPage.UUID;
|
baseHost.PageId = this.currentPage.UUID;
|
||||||
@ -495,11 +512,7 @@ export class EditorService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
canEdit() {
|
canEdit() {
|
||||||
if ( !this.currentPage ) {
|
return this.currentPage && !this.currentPage.isViewOnly() && !this.forceReadonly;
|
||||||
throw new NoPageLoadedError();
|
|
||||||
}
|
|
||||||
|
|
||||||
return !this.currentPage.isViewOnly();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async registerNodeEditor(nodeId: string, editor: EditorNodeContract) {
|
async registerNodeEditor(nodeId: string, editor: EditorNodeContract) {
|
||||||
|
@ -1,74 +0,0 @@
|
|||||||
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<PageRecord> {
|
|
||||||
return new Observable<PageRecord>(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<PageRecord> {
|
|
||||||
return new Observable<PageRecord>(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<Array<HostRecord>> {
|
|
||||||
return new Observable<Array<HostRecord>>(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<HostRecord>): Observable<Array<HostRecord>> {
|
|
||||||
return new Observable<Array<HostRecord>>(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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user