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 {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,6 +57,7 @@ import {MarkdownModule} from "ngx-markdown";
|
||||
|
||||
NormComponent,
|
||||
MarkdownEditorComponent,
|
||||
VersionModalComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
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-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…
Reference in New Issue
Block a user