Initial pass of the new file box node
This commit is contained in:
parent
fc247b3570
commit
bb3eda2577
@ -46,6 +46,7 @@ import {LinkRendererComponent} from './editor/database/renderers/link-renderer.c
|
|||||||
import {FormInputComponent} from './nodes/form-input/form-input.component';
|
import {FormInputComponent} from './nodes/form-input/form-input.component';
|
||||||
import {FormInputOptionsComponent} from './nodes/form-input/options/form-input-options.component';
|
import {FormInputOptionsComponent} from './nodes/form-input/options/form-input-options.component';
|
||||||
import {DatabaseLinkComponent} from './editor/forms/database-link.component';
|
import {DatabaseLinkComponent} from './editor/forms/database-link.component';
|
||||||
|
import {FileBoxComponent} from './nodes/file-box/file-box.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -85,6 +86,7 @@ import {DatabaseLinkComponent} from './editor/forms/database-link.component';
|
|||||||
FormInputComponent,
|
FormInputComponent,
|
||||||
FormInputOptionsComponent,
|
FormInputOptionsComponent,
|
||||||
DatabaseLinkComponent,
|
DatabaseLinkComponent,
|
||||||
|
FileBoxComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@ -137,6 +139,7 @@ import {DatabaseLinkComponent} from './editor/forms/database-link.component';
|
|||||||
FormInputComponent,
|
FormInputComponent,
|
||||||
FormInputOptionsComponent,
|
FormInputOptionsComponent,
|
||||||
DatabaseLinkComponent,
|
DatabaseLinkComponent,
|
||||||
|
FileBoxComponent,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
NodePickerComponent,
|
NodePickerComponent,
|
||||||
@ -175,6 +178,7 @@ import {DatabaseLinkComponent} from './editor/forms/database-link.component';
|
|||||||
FormInputComponent,
|
FormInputComponent,
|
||||||
FormInputOptionsComponent,
|
FormInputOptionsComponent,
|
||||||
DatabaseLinkComponent,
|
DatabaseLinkComponent,
|
||||||
|
FileBoxComponent,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class ComponentsModule {}
|
export class ComponentsModule {}
|
||||||
|
@ -17,7 +17,11 @@
|
|||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item button (click)="onSelect('file_ref')" class="files" *ngIf="!formMode">
|
<ion-item button (click)="onSelect('file_ref')" class="files" *ngIf="!formMode">
|
||||||
<i class="fa" slot="start" [ngClass]="typeIcons.files"></i>
|
<i class="fa" slot="start" [ngClass]="typeIcons.files"></i>
|
||||||
<ion-label>Upload Files</ion-label>
|
<ion-label>Simple Files</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item button (click)="onSelect('file_box')" class="files" *ngIf="!formMode">
|
||||||
|
<i class="fa" slot="start" [ngClass]="typeIcons.file_box"></i>
|
||||||
|
<ion-label>File Box</ion-label>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|
||||||
<!-- Form inputs -->
|
<!-- Form inputs -->
|
||||||
|
48
src/app/components/nodes/file-box/file-box.component.html
Normal file
48
src/app/components/nodes/file-box/file-box.component.html
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<div class="file-box-wrapper" *ngIf="!notAvailableOffline">
|
||||||
|
<ion-toolbar>
|
||||||
|
<div style="display: flex; flex-direction: row">
|
||||||
|
<button
|
||||||
|
style="padding: 0 15px;" *ngIf="history.length > 0"
|
||||||
|
(click)="navigateUp()"
|
||||||
|
><i class="fa fa-arrow-up"></i></button>
|
||||||
|
<button
|
||||||
|
*ngFor="let rec of history"
|
||||||
|
(click)="navigateBack(rec)"
|
||||||
|
>{{ rec.name }} /</button>
|
||||||
|
<ion-input
|
||||||
|
[readonly]="readonly"
|
||||||
|
[(ngModel)]="fileBoxName"
|
||||||
|
(change)="onRecordNameChange($event)"
|
||||||
|
style="flex: 1; font-size: 15pt;"
|
||||||
|
></ion-input>
|
||||||
|
</div>
|
||||||
|
<ion-buttons *ngIf="!readonly" style="flex-wrap: wrap;">
|
||||||
|
<ion-button (click)="onActionClick('folder-add')"><i class="fa fa-folder-plus"></i> Folder</ion-button>
|
||||||
|
<ion-button (click)="onUploadFilesClick($event)"><i class="fa fa-upload"></i> Upload Files</ion-button>
|
||||||
|
|
||||||
|
<input type="file" name="fileInput" #fileInput multiple style="display: none;" (change)="onUploadFilesChange($event)">
|
||||||
|
</ion-buttons>
|
||||||
|
</ion-toolbar>
|
||||||
|
<div class="content-wrapper" (contextmenu)="onSurfaceContextMenu($event)">
|
||||||
|
<div *ngIf="!items || !items.length" class="empty-text" (contextmenu)="onSurfaceContextMenu($event)">No files or folders.</div>
|
||||||
|
<div *ngIf="items && items.length" class="folders">
|
||||||
|
<ng-container *ngFor="let item of items">
|
||||||
|
<div class="folder item" *ngIf="item.type === 'folder'" (contextmenu)="onItemContextMenu(item, $event)" (dblclick)="onItemActivate(item)">
|
||||||
|
<div class="icon" style="color: #ecd3a9"><i class="fa fa-folder"></i></div>
|
||||||
|
<div class="title">{{ item.title }}</div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="items && items.length" class="files">
|
||||||
|
<ng-container *ngFor="let item of items">
|
||||||
|
<div class="folder item" *ngIf="item.type === 'file'" (contextmenu)="onItemContextMenu(item, $event)" (dblclick)="onItemActivate(item)">
|
||||||
|
<div class="icon"><i [ngClass]="categoryIcons[item.category]" [ngStyle]="{ color: categoryColors[item.category] }"></i></div>
|
||||||
|
<div class="title">{{ item.title }}</div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="file-box-wrapper not-available" *ngIf="notAvailableOffline">
|
||||||
|
Sorry, this file box is not available offline yet.
|
||||||
|
</div>
|
53
src/app/components/nodes/file-box/file-box.component.scss
Normal file
53
src/app/components/nodes/file-box/file-box.component.scss
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
div.file-box-wrapper {
|
||||||
|
border: 2px solid #8c8c8c;
|
||||||
|
border-radius: 3px;
|
||||||
|
margin-top: 15px;
|
||||||
|
|
||||||
|
&.not-available {
|
||||||
|
height: 150px;
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 40px;
|
||||||
|
color: #494949;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-wrapper {
|
||||||
|
min-height: 200px;
|
||||||
|
background: #222;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
justify-content: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.folders, .files {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
.item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
background: #393939;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 10px;
|
||||||
|
border-radius: 3px;
|
||||||
|
transition: all 0.2s linear;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background: #424242;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
446
src/app/components/nodes/file-box/file-box.component.ts
Normal file
446
src/app/components/nodes/file-box/file-box.component.ts
Normal file
@ -0,0 +1,446 @@
|
|||||||
|
import {Component, ElementRef, Inject, Input, OnInit, ViewChild} from '@angular/core';
|
||||||
|
import {ApiService, ResourceNotAvailableOfflineError} from '../../../service/api.service';
|
||||||
|
import { APP_BASE_HREF } from '@angular/common';
|
||||||
|
import {EditorService} from '../../../service/editor.service';
|
||||||
|
import {EditorNodeContract} from '../EditorNode.contract';
|
||||||
|
import {HttpClient} from '@angular/common/http';
|
||||||
|
import {AlertController, LoadingController, PopoverController} from '@ionic/angular';
|
||||||
|
import {OptionMenuComponent} from '../../option-menu/option-menu.component';
|
||||||
|
|
||||||
|
export interface FileBoxFile {
|
||||||
|
type: 'file';
|
||||||
|
title: string;
|
||||||
|
mime: string;
|
||||||
|
uploaded: string;
|
||||||
|
id: string;
|
||||||
|
category: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FileBoxRecord {
|
||||||
|
type: 'folder';
|
||||||
|
title: string;
|
||||||
|
UUID: string;
|
||||||
|
name: string;
|
||||||
|
pageId: string;
|
||||||
|
fileIds: string[];
|
||||||
|
rootUUID: string;
|
||||||
|
parentUUID?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FileBoxItem = FileBoxFile | FileBoxRecord;
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'editor-file-box',
|
||||||
|
templateUrl: './file-box.component.html',
|
||||||
|
styleUrls: ['./file-box.component.scss'],
|
||||||
|
})
|
||||||
|
export class FileBoxComponent extends EditorNodeContract implements OnInit {
|
||||||
|
@Input() nodeId: string;
|
||||||
|
@Input() editorUUID: string;
|
||||||
|
@ViewChild('fileInput') fileInput: ElementRef;
|
||||||
|
|
||||||
|
categoryIcons = {
|
||||||
|
document: 'fa fa-file-word',
|
||||||
|
spreadsheet: 'fa fa-file-excel',
|
||||||
|
presentation: 'fa fa-file-powerpoint',
|
||||||
|
image: 'fa fa-file-image',
|
||||||
|
pdf: 'fa fa-file-pdf',
|
||||||
|
video: 'fa fa-file-video',
|
||||||
|
code: 'fa fa-file-code',
|
||||||
|
text: 'fa fa-file-alt',
|
||||||
|
other: 'fa fa-file',
|
||||||
|
};
|
||||||
|
|
||||||
|
categoryColors = {
|
||||||
|
document: '#4269a5',
|
||||||
|
spreadsheet: '#39825a',
|
||||||
|
presentation: '#dc6141',
|
||||||
|
image: '#ffbf50',
|
||||||
|
pdf: '#d32f2f',
|
||||||
|
video: '#8049c0',
|
||||||
|
code: '#ff4500',
|
||||||
|
text: '#cccccc',
|
||||||
|
other: '#ffffff',
|
||||||
|
};
|
||||||
|
|
||||||
|
protected dirty = false;
|
||||||
|
protected pendingSetup = true;
|
||||||
|
public notAvailableOffline = false;
|
||||||
|
public fileBoxName = 'New File Box';
|
||||||
|
public record?: FileBoxRecord;
|
||||||
|
|
||||||
|
public history: FileBoxRecord[] = [];
|
||||||
|
|
||||||
|
public items: FileBoxItem[] = [];
|
||||||
|
|
||||||
|
public get readonly() {
|
||||||
|
return !this.node || !this.editorService.canEdit();
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected api: ApiService,
|
||||||
|
protected http: HttpClient,
|
||||||
|
public editorService: EditorService,
|
||||||
|
protected loading: LoadingController,
|
||||||
|
protected popover: PopoverController,
|
||||||
|
protected alerts: AlertController,
|
||||||
|
@Inject(APP_BASE_HREF) private baseHref: string
|
||||||
|
) { super(); }
|
||||||
|
|
||||||
|
public isDirty(): boolean | Promise<boolean> {
|
||||||
|
return this.dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeChangesToNode(): void | Promise<void> {
|
||||||
|
this.node.Value.Mode = 'files';
|
||||||
|
}
|
||||||
|
|
||||||
|
public needsLoad(): boolean | Promise<boolean> {
|
||||||
|
return this.node && this.pendingSetup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async performLoad(): Promise<void> {
|
||||||
|
if ( !this.node.Value ) {
|
||||||
|
this.node.Value = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( this.api.isOffline ) {
|
||||||
|
this.notAvailableOffline = true;
|
||||||
|
this.pendingSetup = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('file box compt', this);
|
||||||
|
if ( !this.node.Value.Value && !this.readonly ) {
|
||||||
|
this.record = await this.api.createFileBox(this.page.UUID, this.fileBoxName);
|
||||||
|
console.log(this.record);
|
||||||
|
|
||||||
|
this.node.Value.Value = this.record.UUID;
|
||||||
|
this.node.value = this.record.UUID;
|
||||||
|
this.dirty = true;
|
||||||
|
} else if ( this.node.Value.Value ) {
|
||||||
|
this.record = await this.api.getFileBox(this.page.UUID, this.node.Value.Value);
|
||||||
|
console.log(this.record);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !this.record ) {
|
||||||
|
this.notAvailableOffline = true;
|
||||||
|
this.pendingSetup = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fileBoxName = this.record.name;
|
||||||
|
|
||||||
|
await this.loadBox();
|
||||||
|
|
||||||
|
if ( this.dirty ) {
|
||||||
|
this.editorService.triggerSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( this.fileInput ) {
|
||||||
|
this.fileInput.nativeElement.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pendingSetup = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async loadBox(): Promise<void> {
|
||||||
|
this.fileBoxName = this.record.name;
|
||||||
|
const files = await this.api.getFileBoxFiles(this.page.UUID, this.record.UUID);
|
||||||
|
const children = await this.api.getFileBoxChildren(this.page.UUID, this.record.UUID);
|
||||||
|
|
||||||
|
this.items = [...children, ...files];
|
||||||
|
}
|
||||||
|
|
||||||
|
public async navigateUp() {
|
||||||
|
if ( this.history.length < 1 ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const last = this.history[this.history.length - 1];
|
||||||
|
if ( last ) {
|
||||||
|
await this.navigateBack(last);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async navigateBack(record: FileBoxRecord) {
|
||||||
|
const newHistory: FileBoxRecord[] = [];
|
||||||
|
|
||||||
|
const found = this.history.some(row => {
|
||||||
|
if ( row.UUID === record.UUID ) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
newHistory.push(row);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if ( found ) {
|
||||||
|
this.history = newHistory;
|
||||||
|
} else {
|
||||||
|
this.history = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.record = record;
|
||||||
|
await this.loadBox();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async performDelete(): Promise<void> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.editorService = this.editorService.getEditor(this.editorUUID);
|
||||||
|
this.editorService.registerNodeEditor(this.nodeId, this).then(() => {
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
surfaceContextItems() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: 'New Folder',
|
||||||
|
icon: 'fa fa-folder-plus',
|
||||||
|
value: 'folder-add',
|
||||||
|
title: 'Create a new folder in the current file box',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
async onSurfaceContextMenu(event: MouseEvent) {
|
||||||
|
if ( !event.ctrlKey ) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
const popover = await this.popover.create({
|
||||||
|
event,
|
||||||
|
component: OptionMenuComponent,
|
||||||
|
componentProps: {
|
||||||
|
menuItems: [
|
||||||
|
...this.surfaceContextItems(),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
popover.onDidDismiss().then(result => {
|
||||||
|
if ( result.data ) {
|
||||||
|
this.onActionClick(result.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await popover.present();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
itemContextItems(item: FileBoxItem) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: 'Rename',
|
||||||
|
value: 'rename',
|
||||||
|
icon: 'fa fa-edit',
|
||||||
|
title: 'Rename this ' + item.type,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Delete',
|
||||||
|
value: 'delete',
|
||||||
|
icon: 'fa fa-trash',
|
||||||
|
title: 'Delete this ' + item.type,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
async onRecordNameChange(event) {
|
||||||
|
const name = event.target.value;
|
||||||
|
this.fileBoxName = name;
|
||||||
|
this.record.name = name;
|
||||||
|
this.record.title = name;
|
||||||
|
await this.api.updateFileBox(this.page.UUID, this.record.UUID, { name });
|
||||||
|
}
|
||||||
|
|
||||||
|
async onItemContextMenu(item: FileBoxItem, event: MouseEvent) {
|
||||||
|
if ( !event.ctrlKey ) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
const popover = await this.popover.create({
|
||||||
|
event,
|
||||||
|
component: OptionMenuComponent,
|
||||||
|
componentProps: {
|
||||||
|
menuItems: [
|
||||||
|
...this.itemContextItems(item),
|
||||||
|
...this.surfaceContextItems(),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
popover.onDidDismiss().then(result => {
|
||||||
|
if ( result.data ) {
|
||||||
|
this.onActionClick(result.data, item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await popover.present();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async onUploadFilesClick(event) {
|
||||||
|
if ( this.fileInput ) {
|
||||||
|
this.fileInput.nativeElement.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async onUploadFilesChange(event) {
|
||||||
|
if ( this.readonly ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const loader = await this.loading.create({
|
||||||
|
message: 'Uploading files...',
|
||||||
|
});
|
||||||
|
|
||||||
|
await loader.present();
|
||||||
|
|
||||||
|
const fileList: FileList = this.fileInput?.nativeElement?.files;
|
||||||
|
if ( !fileList ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( fileList.length > 0 ) {
|
||||||
|
const formData: FormData = new FormData();
|
||||||
|
|
||||||
|
// tslint:disable-next-line:prefer-for-of
|
||||||
|
for ( let i = 0; i < fileList.length; i += 1 ) {
|
||||||
|
const file = fileList[i];
|
||||||
|
formData.append(`uploaded_file_${i}`, file, file.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.api.uploadFileBoxFiles(this.page.UUID, this.record.UUID, formData);
|
||||||
|
await this.loadBox();
|
||||||
|
}
|
||||||
|
|
||||||
|
await loader.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
async onItemActivate(item: FileBoxItem) {
|
||||||
|
if ( item.type === 'folder' ) {
|
||||||
|
this.history.push(this.record);
|
||||||
|
this.record = item;
|
||||||
|
await this.loadBox();
|
||||||
|
} else if ( item.type === 'file' ) {
|
||||||
|
const url = this.api.getFileBoxFileDownloadUrl(this.page.UUID, this.record.UUID, item.id);
|
||||||
|
window.open(url, '_blank');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async onActionClick(action: string, item?: FileBoxItem) {
|
||||||
|
if ( action === 'folder-add' ) {
|
||||||
|
await this.actionNewFolder();
|
||||||
|
} else if ( action === 'rename' && item ) {
|
||||||
|
await this.actionRename(item);
|
||||||
|
} else if ( action === 'delete' && item ) {
|
||||||
|
await this.actionDelete(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async actionNewFolder() {
|
||||||
|
const alert = await this.alerts.create({
|
||||||
|
header: 'New Folder',
|
||||||
|
message: 'Enter a name for the new folder:',
|
||||||
|
inputs: [
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
placeholder: 'New Folder',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Create',
|
||||||
|
role: 'ok',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Cancel',
|
||||||
|
role: 'cancel',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
alert.onDidDismiss().then(async result => {
|
||||||
|
if ( result.role === 'ok' ) {
|
||||||
|
const name = result.data?.values?.name?.trim() || 'New Folder';
|
||||||
|
await this.api.createFileBox(this.page.UUID, name, this.record.rootUUID, this.record.UUID);
|
||||||
|
await this.loadBox();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await alert.present();
|
||||||
|
}
|
||||||
|
|
||||||
|
async actionRename(item: FileBoxItem) {
|
||||||
|
const alert = await this.alerts.create({
|
||||||
|
header: 'Rename ' + item.type,
|
||||||
|
message: `Enter a new name for the ${item.type}:`,
|
||||||
|
inputs: [
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
value: item.title,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Rename',
|
||||||
|
role: 'ok',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Cancel',
|
||||||
|
role: 'cancel',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
alert.onDidDismiss().then(async result => {
|
||||||
|
const name = result.data?.values?.name?.trim();
|
||||||
|
if ( result.role === 'ok' && name ) {
|
||||||
|
if ( item.type === 'folder' ) {
|
||||||
|
item.name = name;
|
||||||
|
item.title = name;
|
||||||
|
await this.api.updateFileBox(this.page.UUID, item.UUID, { name });
|
||||||
|
} else if ( item.type === 'file' ) {
|
||||||
|
item.title = name;
|
||||||
|
await this.api.updateFileBoxFile(this.page.UUID, this.record.UUID, item.id, { name });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await alert.present();
|
||||||
|
}
|
||||||
|
|
||||||
|
async actionDelete(item: FileBoxItem) {
|
||||||
|
const alert = await this.alerts.create({
|
||||||
|
header: `Delete ${item.type}?`,
|
||||||
|
message: `Are you sure you want to delete the ${item.type} "${item.title}"? This action cannot be undone.`,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Delete It',
|
||||||
|
role: 'ok',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Keep It',
|
||||||
|
role: 'cancel',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
alert.onDidDismiss().then(async result => {
|
||||||
|
if ( result.role === 'ok' ) {
|
||||||
|
if ( item.type === 'file' ) {
|
||||||
|
await this.api.deleteFileBoxFile(this.page.UUID, this.record.UUID, item.id);
|
||||||
|
await this.loadBox();
|
||||||
|
} else if ( item.type === 'folder' ) {
|
||||||
|
await this.api.deleteFileBox(this.page.UUID, item.UUID);
|
||||||
|
await this.loadBox();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await alert.present();
|
||||||
|
}
|
||||||
|
}
|
@ -73,6 +73,9 @@
|
|||||||
<ng-container *ngIf="node.type === 'file_ref'">
|
<ng-container *ngIf="node.type === 'file_ref'">
|
||||||
<editor-files style="flex: 1;" [nodeId]="node.UUID" [editorUUID]="editorService.instanceUUID" #nodeContainer></editor-files>
|
<editor-files style="flex: 1;" [nodeId]="node.UUID" [editorUUID]="editorService.instanceUUID" #nodeContainer></editor-files>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
<ng-container *ngIf="node.type === 'file_box'">
|
||||||
|
<editor-file-box style="flex: 1;" [nodeId]="node.UUID" [editorUUID]="editorService.instanceUUID" #nodeContainer></editor-file-box>
|
||||||
|
</ng-container>
|
||||||
<ng-container *ngIf="node.isForm()">
|
<ng-container *ngIf="node.isForm()">
|
||||||
<editor-node-form-input style="flex: 1;" [isFilloutMode]="isFilloutMode" [nodeId]="node.UUID" [editorUUID]="editorService.instanceUUID" #nodeContainer></editor-node-form-input>
|
<editor-node-form-input style="flex: 1;" [isFilloutMode]="isFilloutMode" [nodeId]="node.UUID" [editorUUID]="editorService.instanceUUID" #nodeContainer></editor-node-form-input>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@ -29,7 +29,7 @@ ion-icon.invisible {
|
|||||||
color: var(--noded-background-db);
|
color: var(--noded-background-db);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.file_ref {
|
&.file_ref, &.file_box {
|
||||||
color: var(--noded-background-files);
|
color: var(--noded-background-files);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,7 +199,11 @@ export class ApiService {
|
|||||||
return this.request(endpoint, body, 'post');
|
return this.request(endpoint, body, 'post');
|
||||||
}
|
}
|
||||||
|
|
||||||
public request(endpoint, params = {}, method: 'get'|'post' = 'get'): Observable<ApiResponse> {
|
public delete(endpoint, body = {}): Observable<ApiResponse> {
|
||||||
|
return this.request(endpoint, body, 'delete');
|
||||||
|
}
|
||||||
|
|
||||||
|
public request(endpoint, params = {}, method: 'get'|'post'|'delete' = 'get'): Observable<ApiResponse> {
|
||||||
return this._request(this._build_url(endpoint), params, method);
|
return this._request(this._build_url(endpoint), params, method);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,7 +263,7 @@ export class ApiService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _request(endpoint, params = {}, method: 'get'|'post' = 'get'): Observable<ApiResponse> {
|
protected _request(endpoint, params = {}, method: 'get'|'post'|'delete' = 'get'): Observable<ApiResponse> {
|
||||||
return new Observable<ApiResponse>(sub => {
|
return new Observable<ApiResponse>(sub => {
|
||||||
let data: any = {};
|
let data: any = {};
|
||||||
if ( method === 'get' ) {
|
if ( method === 'get' ) {
|
||||||
@ -1223,6 +1227,145 @@ export class ApiService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public createFileBox(PageId: string, name: string, rootUUID?: string, parentUUID?: string): Promise<any> {
|
||||||
|
return new Promise(async (res, rej) => {
|
||||||
|
if ( this.isOffline ) {
|
||||||
|
return rej(new ResourceNotAvailableOfflineError());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.post(`/file-box/${PageId}/create`, { name, rootUUID, parentUUID }).subscribe({
|
||||||
|
next: async result => {
|
||||||
|
res(result.data);
|
||||||
|
},
|
||||||
|
error: rej,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getFileBox(PageId: string, FileBoxId: string): Promise<any> {
|
||||||
|
return new Promise(async (res, rej) => {
|
||||||
|
if ( this.isOffline ) {
|
||||||
|
return rej(new ResourceNotAvailableOfflineError());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.get(`/file-box/${PageId}/${FileBoxId}`).subscribe({
|
||||||
|
next: async result => {
|
||||||
|
res(result.data);
|
||||||
|
},
|
||||||
|
error: rej,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getFileBoxFiles(PageId: string, FileBoxId: string): Promise<any> {
|
||||||
|
return new Promise(async (res, rej) => {
|
||||||
|
if ( this.isOffline ) {
|
||||||
|
return rej(new ResourceNotAvailableOfflineError());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.get(`/file-box/${PageId}/${FileBoxId}/files`).subscribe({
|
||||||
|
next: async result => {
|
||||||
|
res(result.data);
|
||||||
|
},
|
||||||
|
error: rej,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getFileBoxChildren(PageId: string, FileBoxId: string): Promise<any> {
|
||||||
|
return new Promise(async (res, rej) => {
|
||||||
|
if ( this.isOffline ) {
|
||||||
|
return rej(new ResourceNotAvailableOfflineError());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.get(`/file-box/${PageId}/${FileBoxId}/children`).subscribe({
|
||||||
|
next: async result => {
|
||||||
|
res(result.data);
|
||||||
|
},
|
||||||
|
error: rej,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateFileBox(PageId: string, FileBoxId: string, data: any): Promise<any> {
|
||||||
|
return new Promise(async (res, rej) => {
|
||||||
|
if ( this.isOffline ) {
|
||||||
|
return rej(new ResourceNotAvailableOfflineError());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.post(`/file-box/${PageId}/${FileBoxId}`, data).subscribe({
|
||||||
|
next: result => {
|
||||||
|
res(result.data);
|
||||||
|
},
|
||||||
|
error: rej,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateFileBoxFile(PageId: string, FileBoxId: string, FileBoxFileId: string, data: any): Promise<any> {
|
||||||
|
return new Promise(async (res, rej) => {
|
||||||
|
if ( this.isOffline ) {
|
||||||
|
return rej(new ResourceNotAvailableOfflineError());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.post(`/file-box/${PageId}/${FileBoxId}/files/${FileBoxFileId}`, data).subscribe({
|
||||||
|
next: result => {
|
||||||
|
res(result.data);
|
||||||
|
},
|
||||||
|
error: rej,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteFileBoxFile(PageId: string, FileBoxId: string, FileBoxFileId: string): Promise<any> {
|
||||||
|
return new Promise(async (res, rej) => {
|
||||||
|
if ( this.isOffline ) {
|
||||||
|
return rej(new ResourceNotAvailableOfflineError());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.delete(`/file-box/${PageId}/${FileBoxId}/files/${FileBoxFileId}`).subscribe({
|
||||||
|
next: result => {
|
||||||
|
res(result.data);
|
||||||
|
},
|
||||||
|
error: rej,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteFileBox(PageId: string, FileBoxId: string): Promise<any> {
|
||||||
|
return new Promise(async (res, rej) => {
|
||||||
|
if ( this.isOffline ) {
|
||||||
|
return rej(new ResourceNotAvailableOfflineError());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.delete(`/file-box/${PageId}/${FileBoxId}`).subscribe({
|
||||||
|
next: result => {
|
||||||
|
res(result.data);
|
||||||
|
},
|
||||||
|
error: rej,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public uploadFileBoxFiles(PageId: string, FileBoxId: string, formData: FormData): Promise<any> {
|
||||||
|
return new Promise(async (res, rej) => {
|
||||||
|
if ( this.isOffline ) {
|
||||||
|
return rej(new ResourceNotAvailableOfflineError());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.post(`/file-box/${PageId}/${FileBoxId}/files`, formData).subscribe({
|
||||||
|
next: async result => {
|
||||||
|
return res(result.data);
|
||||||
|
},
|
||||||
|
error: rej,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getFileBoxFileDownloadUrl(PageId: string, FileBoxId: string, FileBoxFileId: string): string {
|
||||||
|
return this._build_url(`/file-box/${PageId}/${FileBoxId}/files/${FileBoxFileId}`);
|
||||||
|
}
|
||||||
|
|
||||||
public moveMenuNode(MovedPageId: string, ParentPageId: string): Promise<any> {
|
public moveMenuNode(MovedPageId: string, ParentPageId: string): Promise<any> {
|
||||||
return new Promise(async (res, rej) => {
|
return new Promise(async (res, rej) => {
|
||||||
if ( this.isOffline ) {
|
if ( this.isOffline ) {
|
||||||
|
@ -10,6 +10,7 @@ export const NodeTypeIcons = {
|
|||||||
code_ref: 'fa fa-code noded-code',
|
code_ref: 'fa fa-code noded-code',
|
||||||
file_ref: 'fa fa-archive noded-files',
|
file_ref: 'fa fa-archive noded-files',
|
||||||
files: 'fa fa-archive noded-files',
|
files: 'fa fa-archive noded-files',
|
||||||
|
file_box: 'fa fa-archive noded-file-box',
|
||||||
markdown: 'fab fa-markdown noded-markdown',
|
markdown: 'fab fa-markdown noded-markdown',
|
||||||
form_input_text: 'fa fa-font noded-form',
|
form_input_text: 'fa fa-font noded-form',
|
||||||
form_input_number: 'fa fa-hashtag noded-form',
|
form_input_number: 'fa fa-hashtag noded-form',
|
||||||
|
Loading…
Reference in New Issue
Block a user