You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
447 lines
13 KiB
447 lines
13 KiB
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();
|
|
}
|
|
}
|