Start page versions modal
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
36d97c9eca
commit
0e0d237d4f
@ -29,7 +29,8 @@ import {SearchComponent} from './search/Search.component';
|
|||||||
import {NormComponent} from './nodes/norm/norm.component';
|
import {NormComponent} from './nodes/norm/norm.component';
|
||||||
import {MarkdownComponent as MarkdownEditorComponent} from './nodes/markdown/markdown.component';
|
import {MarkdownComponent as MarkdownEditorComponent} from './nodes/markdown/markdown.component';
|
||||||
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';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -56,6 +57,7 @@ import {MarkdownModule} from "ngx-markdown";
|
|||||||
|
|
||||||
NormComponent,
|
NormComponent,
|
||||||
MarkdownEditorComponent,
|
MarkdownEditorComponent,
|
||||||
|
VersionModalComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@ -92,6 +94,7 @@ import {MarkdownModule} from "ngx-markdown";
|
|||||||
|
|
||||||
NormComponent,
|
NormComponent,
|
||||||
MarkdownEditorComponent,
|
MarkdownEditorComponent,
|
||||||
|
VersionModalComponent,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
NodePickerComponent,
|
NodePickerComponent,
|
||||||
@ -117,6 +120,7 @@ import {MarkdownModule} from "ngx-markdown";
|
|||||||
|
|
||||||
NormComponent,
|
NormComponent,
|
||||||
MarkdownEditorComponent,
|
MarkdownEditorComponent,
|
||||||
|
VersionModalComponent,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class ComponentsModule {}
|
export class ComponentsModule {}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<ion-list>
|
<ion-list>
|
||||||
<ion-item *ngFor="let menuItem of menuItems; let i = index" button (click)="onSelect(menuItems[i].value)">
|
<ion-item *ngFor="let menuItem of menuItems; let i = index" button (click)="onSelect(menuItems[i].value)" [title]="menuItems[i].title">
|
||||||
<i slot="start" [ngClass]="menuItems[i].icon"></i>
|
<i slot="start" [ngClass]="menuItems[i].icon"></i>
|
||||||
<ion-label>{{ menuItems[i].name }}</ion-label>
|
<ion-label>{{ menuItems[i].name }}</ion-label>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
@ -7,7 +7,7 @@ import {PopoverController} from '@ionic/angular';
|
|||||||
styleUrls: ['./option-menu.component.scss'],
|
styleUrls: ['./option-menu.component.scss'],
|
||||||
})
|
})
|
||||||
export class OptionMenuComponent implements OnInit {
|
export class OptionMenuComponent implements OnInit {
|
||||||
@Input() menuItems: Array<{name: string, icon: string, value: string, type?: string}> = [];
|
@Input() menuItems: Array<{name: string, icon: string, value: string, type?: string, title?: string}> = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected popover: PopoverController,
|
protected popover: PopoverController,
|
||||||
@ -18,5 +18,4 @@ export class OptionMenuComponent implements OnInit {
|
|||||||
async onSelect(value) {
|
async onSelect(value) {
|
||||||
await this.popover.dismiss(value);
|
await this.popover.dismiss(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<div class="title">Page Versions</div>
|
||||||
|
<button title="Close" class="close" (click)="dismiss()">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="contents">
|
||||||
|
<ion-list>
|
||||||
|
<ion-item button *ngFor="let version of pageVersions" (click)="onVersionClick(version)" [color]="selectedVersion === version ? 'primary' : ''">
|
||||||
|
<ion-label>
|
||||||
|
<h4 style="font-weight: bold">
|
||||||
|
#{{ version.versionNum }}
|
||||||
|
<span class="version-date">({{ version.versionCreateDate.toLocaleString() }})</span>
|
||||||
|
<span class="current-version"><i *ngIf="version.currentVersion" class="fa fa-asterisk" title="Current version"></i></span>
|
||||||
|
</h4>
|
||||||
|
<h5>{{ version.versionMessage }} by {{ version.userDisplay }}</h5>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</ion-list>
|
||||||
|
<div class="preview">
|
||||||
|
Page preview will be here.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,49 @@
|
|||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.header {
|
||||||
|
background: lightgrey;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
flex: 1;
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
padding: 10px 15px;
|
||||||
|
background: #ff6666;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.contents {
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: scroll;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
ion-list {
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: scroll;
|
||||||
|
max-width: 325px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-date {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-version {
|
||||||
|
color: darkgreen;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
src/app/components/version-modal/version-modal.component.ts
Normal file
36
src/app/components/version-modal/version-modal.component.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import {Component, Input, OnInit} from '@angular/core';
|
||||||
|
import {AlertController, ModalController} from '@ionic/angular';
|
||||||
|
import {EditorService} from '../../service/editor.service';
|
||||||
|
import {PageVersionRecord} from '../../structures/PageRecord';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-version-modal',
|
||||||
|
templateUrl: './version-modal.component.html',
|
||||||
|
styleUrls: ['./version-modal.component.scss'],
|
||||||
|
})
|
||||||
|
export class VersionModalComponent implements OnInit {
|
||||||
|
@Input() pageId: string;
|
||||||
|
public pageVersions: PageVersionRecord[] = [];
|
||||||
|
public selectedVersion?: PageVersionRecord;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected alerts: AlertController,
|
||||||
|
protected modals: ModalController,
|
||||||
|
protected editorService: EditorService,
|
||||||
|
) {
|
||||||
|
console.log('version modal', this);
|
||||||
|
}
|
||||||
|
|
||||||
|
async ngOnInit() {
|
||||||
|
this.pageVersions = await this.editorService.loadPageVersions(this.pageId);
|
||||||
|
await this.onVersionClick(this.pageVersions[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
dismiss() {
|
||||||
|
this.modals.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
async onVersionClick(version: PageVersionRecord) {
|
||||||
|
this.selectedVersion = version;
|
||||||
|
}
|
||||||
|
}
|
@ -12,10 +12,18 @@
|
|||||||
></ion-input>
|
></ion-input>
|
||||||
</ion-title>
|
</ion-title>
|
||||||
<ion-buttons slot="end">
|
<ion-buttons slot="end">
|
||||||
<button class="save-button" (click)="editorService.triggerSave()" title="Manually save this note">
|
<button
|
||||||
|
class="save-button"
|
||||||
|
(click)="editorService.triggerSave()"
|
||||||
|
title="Manually save this note"
|
||||||
|
*ngIf="editorService.isEditing && editorService.canEdit()"
|
||||||
|
>
|
||||||
<i *ngIf="!(editorService.isSaving || editorService.willSave)" class="fa fa-check-circle"></i>
|
<i *ngIf="!(editorService.isSaving || editorService.willSave)" class="fa fa-check-circle"></i>
|
||||||
{{ (editorService.isSaving || editorService.willSave) ? 'Saving...' : 'Saved!' }}
|
{{ (editorService.isSaving || editorService.willSave) ? 'Saving...' : 'Saved!' }}
|
||||||
</button>
|
</button>
|
||||||
|
<ion-button *ngIf="editorService.isEditing && editorService.canEdit()" title="More page options" (click)="onPageMenuClick($event)">
|
||||||
|
<i class="fa fa-ellipsis-v"></i>
|
||||||
|
</ion-button>
|
||||||
</ion-buttons>
|
</ion-buttons>
|
||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
</ion-header>
|
</ion-header>
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import {Component, HostListener, Input, OnInit} from '@angular/core';
|
import {Component, HostListener, Input, OnInit} from '@angular/core';
|
||||||
import HostRecord from '../../structures/HostRecord';
|
import HostRecord from '../../structures/HostRecord';
|
||||||
import {ActivatedRoute, Router} from '@angular/router';
|
import {ActivatedRoute, Router} from '@angular/router';
|
||||||
import {AlertController, LoadingController, PopoverController} from '@ionic/angular';
|
import {AlertController, LoadingController, ModalController, PopoverController} from '@ionic/angular';
|
||||||
import {NodePickerComponent} from '../../components/editor/node-picker/node-picker.component';
|
import {NodePickerComponent} from '../../components/editor/node-picker/node-picker.component';
|
||||||
import {HostOptionsComponent} from '../../components/editor/host-options/host-options.component';
|
import {HostOptionsComponent} from '../../components/editor/host-options/host-options.component';
|
||||||
import {EditorService} from '../../service/editor.service';
|
import {EditorService} from '../../service/editor.service';
|
||||||
import {NodeTypeIcons} from '../../structures/node-types';
|
import {NodeTypeIcons} from '../../structures/node-types';
|
||||||
|
import {OptionMenuComponent} from '../../components/option-menu/option-menu.component';
|
||||||
|
import {VersionModalComponent} from "../../components/version-modal/version-modal.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-editor',
|
selector: 'app-editor',
|
||||||
@ -22,6 +24,7 @@ export class EditorPage implements OnInit {
|
|||||||
protected loader: LoadingController,
|
protected loader: LoadingController,
|
||||||
protected popover: PopoverController,
|
protected popover: PopoverController,
|
||||||
protected alerts: AlertController,
|
protected alerts: AlertController,
|
||||||
|
protected modals: ModalController,
|
||||||
public readonly editorService: EditorService,
|
public readonly editorService: EditorService,
|
||||||
) {
|
) {
|
||||||
this.route.params.subscribe(params => {
|
this.route.params.subscribe(params => {
|
||||||
@ -86,9 +89,9 @@ export class EditorPage implements OnInit {
|
|||||||
} else if ( value === 'move_down' ) {
|
} else if ( value === 'move_down' ) {
|
||||||
this.editorService.moveNode(node, 'down');
|
this.editorService.moveNode(node, 'down');
|
||||||
} else if ( value === 'add_before' ) {
|
} else if ( value === 'add_before' ) {
|
||||||
this.onAddClick(dismissEvent, 'before', node.UUID);
|
this.onAddClick(event, 'before', node.UUID);
|
||||||
} else if ( value === 'add_after' ) {
|
} else if ( value === 'add_after' ) {
|
||||||
this.onAddClick(dismissEvent, 'after', node.UUID);
|
this.onAddClick(event, 'after', node.UUID);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -115,4 +118,43 @@ export class EditorPage implements OnInit {
|
|||||||
|
|
||||||
await popover.present();
|
await popover.present();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async onPageMenuClick(event: MouseEvent) {
|
||||||
|
const popover = await this.popover.create({
|
||||||
|
event,
|
||||||
|
component: OptionMenuComponent,
|
||||||
|
componentProps: {
|
||||||
|
menuItems: [
|
||||||
|
{
|
||||||
|
name: 'Versions',
|
||||||
|
icon: 'fa fa-history',
|
||||||
|
value: 'view-versions',
|
||||||
|
title: 'View other versions of this page',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
popover.onDidDismiss().then(async value => {
|
||||||
|
const { data } = value;
|
||||||
|
|
||||||
|
if ( data === 'view-versions' ) {
|
||||||
|
await this.showVersionModal();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await popover.present();
|
||||||
|
}
|
||||||
|
|
||||||
|
async showVersionModal() {
|
||||||
|
const modal = await this.modals.create({
|
||||||
|
component: VersionModalComponent,
|
||||||
|
componentProps: {
|
||||||
|
pageId: this.pageId,
|
||||||
|
},
|
||||||
|
cssClass: 'modal-big',
|
||||||
|
});
|
||||||
|
|
||||||
|
await modal.present();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
import {ApiService, ResourceNotAvailableOfflineError} from './api.service';
|
import {ApiService, ResourceNotAvailableOfflineError} from './api.service';
|
||||||
import PageRecord from '../structures/PageRecord';
|
import PageRecord, {PageVersionRecord} from '../structures/PageRecord';
|
||||||
import HostRecord from '../structures/HostRecord';
|
import HostRecord from '../structures/HostRecord';
|
||||||
import {EditorNodeContract} from '../components/nodes/EditorNode.contract';
|
import {EditorNodeContract} from '../components/nodes/EditorNode.contract';
|
||||||
import {BehaviorSubject, Subscription} from 'rxjs';
|
import {BehaviorSubject, Subscription} from 'rxjs';
|
||||||
@ -55,6 +55,10 @@ export class EditorService {
|
|||||||
return this.saveTriggered;
|
return this.saveTriggered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get isEditing() {
|
||||||
|
return !!this.currentPage;
|
||||||
|
}
|
||||||
|
|
||||||
public get immutableNodes(): HostRecord[] {
|
public get immutableNodes(): HostRecord[] {
|
||||||
return [...this.currentNodes];
|
return [...this.currentNodes];
|
||||||
}
|
}
|
||||||
@ -570,6 +574,31 @@ export class EditorService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async loadPageVersions(pageId: string): Promise<PageVersionRecord[]> {
|
||||||
|
return new Promise(async (res, rej) => {
|
||||||
|
if ( this.api.isOffline ) {
|
||||||
|
return rej(new ResourceNotAvailableOfflineError());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.api.get(`/page/${pageId}/versions`).subscribe({
|
||||||
|
next: results => {
|
||||||
|
return res(results.data.map(data => {
|
||||||
|
return {
|
||||||
|
currentVersion: Boolean(data.current_version),
|
||||||
|
versionNum: Number(data.version_num),
|
||||||
|
versionUserId: data.version_user_id,
|
||||||
|
versionMessage: data.version_message,
|
||||||
|
versionUUID: data.version_UUID,
|
||||||
|
versionCreateDate: new Date(data.version_create_date),
|
||||||
|
userDisplay: data.user_display,
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
error: rej,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async loadNodes(pageId: string): Promise<HostRecord[]> {
|
async loadNodes(pageId: string): Promise<HostRecord[]> {
|
||||||
return new Promise(async (res, rej) => {
|
return new Promise(async (res, rej) => {
|
||||||
const existingNodes = await this.db.pageNodes.where({ PageId: pageId }).toArray() as PageNode[];
|
const existingNodes = await this.db.pageNodes.where({ PageId: pageId }).toArray() as PageNode[];
|
||||||
|
@ -1,3 +1,13 @@
|
|||||||
|
export class PageVersionRecord {
|
||||||
|
currentVersion: boolean;
|
||||||
|
versionNum: number;
|
||||||
|
versionUserId: string;
|
||||||
|
versionMessage: string;
|
||||||
|
versionUUID: string;
|
||||||
|
versionCreateDate: Date;
|
||||||
|
userDisplay: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default class PageRecord {
|
export default class PageRecord {
|
||||||
public UUID: string;
|
public UUID: string;
|
||||||
public Name: string;
|
public Name: string;
|
||||||
|
@ -123,3 +123,10 @@ hr {
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal-big {
|
||||||
|
.modal-wrapper {
|
||||||
|
height: calc(100vh - 30px);
|
||||||
|
width: calc(100vw - 30px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user