Start page versions modal
continuous-integration/drone/push Build is passing Details

master
Garrett Mills 4 years ago
parent 36d97c9eca
commit 0e0d237d4f
Signed by: garrettmills
GPG Key ID: D2BF5FBA8298F246

@ -29,7 +29,8 @@ import {SearchComponent} from './search/Search.component';
import {NormComponent} from './nodes/norm/norm.component';
import {MarkdownComponent as MarkdownEditorComponent} from './nodes/markdown/markdown.component';
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({
declarations: [
@ -56,18 +57,19 @@ import {MarkdownModule} from "ngx-markdown";
NormComponent,
MarkdownEditorComponent,
VersionModalComponent,
],
imports: [
CommonModule,
IonicModule,
AgGridModule,
FormsModule,
ReactiveFormsModule,
ContenteditableModule,
MonacoEditorModule,
DirectivesModule,
MarkdownModule,
],
imports: [
CommonModule,
IonicModule,
AgGridModule,
FormsModule,
ReactiveFormsModule,
ContenteditableModule,
MonacoEditorModule,
DirectivesModule,
MarkdownModule,
],
entryComponents: [
NodePickerComponent,
DatabaseComponent,
@ -92,6 +94,7 @@ import {MarkdownModule} from "ngx-markdown";
NormComponent,
MarkdownEditorComponent,
VersionModalComponent,
],
exports: [
NodePickerComponent,
@ -117,6 +120,7 @@ import {MarkdownModule} from "ngx-markdown";
NormComponent,
MarkdownEditorComponent,
VersionModalComponent,
]
})
export class ComponentsModule {}

@ -1,5 +1,5 @@
<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>
<ion-label>{{ menuItems[i].name }}</ion-label>
</ion-item>

@ -7,7 +7,7 @@ import {PopoverController} from '@ionic/angular';
styleUrls: ['./option-menu.component.scss'],
})
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(
protected popover: PopoverController,
@ -18,5 +18,4 @@ export class OptionMenuComponent implements OnInit {
async onSelect(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;
}
}
}

@ -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-title>
<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>
{{ (editorService.isSaving || editorService.willSave) ? 'Saving...' : 'Saved!' }}
</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-toolbar>
</ion-header>

@ -1,11 +1,13 @@
import {Component, HostListener, Input, OnInit} from '@angular/core';
import HostRecord from '../../structures/HostRecord';
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 {HostOptionsComponent} from '../../components/editor/host-options/host-options.component';
import {EditorService} from '../../service/editor.service';
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({
selector: 'app-editor',
@ -22,6 +24,7 @@ export class EditorPage implements OnInit {
protected loader: LoadingController,
protected popover: PopoverController,
protected alerts: AlertController,
protected modals: ModalController,
public readonly editorService: EditorService,
) {
this.route.params.subscribe(params => {
@ -86,9 +89,9 @@ export class EditorPage implements OnInit {
} else if ( value === 'move_down' ) {
this.editorService.moveNode(node, 'down');
} else if ( value === 'add_before' ) {
this.onAddClick(dismissEvent, 'before', node.UUID);
this.onAddClick(event, 'before', node.UUID);
} 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();
}
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 {ApiService, ResourceNotAvailableOfflineError} from './api.service';
import PageRecord from '../structures/PageRecord';
import PageRecord, {PageVersionRecord} from '../structures/PageRecord';
import HostRecord from '../structures/HostRecord';
import {EditorNodeContract} from '../components/nodes/EditorNode.contract';
import {BehaviorSubject, Subscription} from 'rxjs';
@ -55,6 +55,10 @@ export class EditorService {
return this.saveTriggered;
}
public get isEditing() {
return !!this.currentPage;
}
public get immutableNodes(): HostRecord[] {
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[]> {
return new Promise(async (res, rej) => {
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 {
public UUID: string;
public Name: string;

@ -123,3 +123,10 @@ hr {
padding: 10px;
}
}
.modal-big {
.modal-wrapper {
height: calc(100vh - 30px);
width: calc(100vw - 30px);
}
}

Loading…
Cancel
Save