import {Component, ElementRef, HostListener, Input, OnInit, ViewChildren} from '@angular/core'; import HostRecord from '../../structures/HostRecord'; import {ActivatedRoute, Router} from '@angular/router'; 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'; import {debug} from '../../utility'; import {DatabaseLinkComponent} from '../../components/editor/forms/database-link.component'; @Component({ selector: 'app-editor', templateUrl: './editor.page.html', styleUrls: ['./editor.page.scss'], }) export class EditorPage implements OnInit { public typeIcons = NodeTypeIcons; @Input() pageId: string; @Input() baseId: string; @Input() isFilloutMode = false; @Input() hosted = false; @Input() version?: number; @ViewChildren('nodeContainer') nodeContainers; @ViewChildren('nodeContainer', { read: ElementRef }) nodeElements; public pageType?: string; protected scrollToNodeId?: string; @Input() set readonly(val: boolean) { this.editorService.forceReadonly = val; } get readonly() { return this.editorService.forceReadonly; } constructor( public readonly route: ActivatedRoute, public readonly router: Router, public readonly loader: LoadingController, public readonly popover: PopoverController, public readonly alerts: AlertController, public readonly modals: ModalController, public editorService: EditorService, ) { debug('Constructed editor page.'); this.editorService = editorService.getEditor(); this.route.params.subscribe(params => { this.baseId = params.id; this.pageId = params.page || params.id; debug('Got page ID from route:', this.pageId, params); if ( params.node_id ) { debug('Scroll to node ID:', params.node_id); this.scrollToNodeId = params.node_id; } }); } ngOnInit() {} async ionViewDidEnter() { debug('Ion view did enter editor page.'); if ( this.pageId ) { await this.editorService.startEditing(this.pageId, this.version); } else if ( !this.hosted ) { await this.router.navigate(['/home']); } setTimeout(() => { if ( this.scrollToNodeId ) { debug('Scrolling to node.'); const nodes = this.nodeContainers.toArray(); const elements = this.nodeElements.toArray(); debug('Nodes', {nodes, elements}); nodes.forEach((node, i) => { if ( node.nodeId === this.scrollToNodeId ) { elements[i].nativeElement.scrollIntoView(); node.performUIActivation(); } }); } }, 150); } @HostListener('document:keydown.control.s', ['$event']) onManualSave(event) { event.preventDefault(); this.editorService.save(); } async onOptionsClick(event: MouseEvent, node: HostRecord) { if ( !this.editorService.canEdit() ) { return; } const popover = await this.popover.create({ component: HostOptionsComponent, event, componentProps: { editor: this, index: this.editorService.immutableNodes.indexOf(node), event, hostRecord: node, } }); popover.onDidDismiss().then(result => { const { event: dismissEvent , value } = result.data; if ( value === 'delete_node' ) { this.alerts.create({ header: 'Delete node?', message: 'Are you sure you want to delete this node? Its contents will be unrecoverable.', buttons: [ { text: 'Keep It', role: 'cancel', }, { text: 'Delete It', role: 'ok', handler: () => { this.editorService.deleteNode(node.UUID); } } ], }).then(alert => alert.present()); } else if ( value === 'move_up' ) { this.editorService.moveNode(node, 'up'); } else if ( value === 'move_down' ) { this.editorService.moveNode(node, 'down'); } else if ( value === 'add_before' ) { this.onAddClick(event, 'before', node.UUID); } else if ( value === 'add_after' ) { this.onAddClick(event, 'after', node.UUID); } }); await popover.present(); } async onAddClick(event: MouseEvent, position?: 'before' | 'after', positionNodeId?: string) { if ( !this.editorService.canEdit() ) { return; } const popover = await this.popover.create({ component: NodePickerComponent, event, componentProps: { formMode: this.editorService.currentPageType === 'form', }, }); popover.onDidDismiss().then(result => { if ( !result.data ) { return; } this.editorService.addNode(result.data, position, positionNodeId); }); await popover.present(); } async onPageMenuClick(event: MouseEvent) { debug('Page type: ', this.pageType) 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', }, ...(this.editorService.currentPageType === 'form' ? [ { name: 'Link to Database', icon: NodeTypeIcons.db, value: 'link-to-db', title: 'Configure the database where submissions of this form will be stored', }, ] : []), ], }, }); popover.onDidDismiss().then(async value => { const { data } = value; if ( data === 'view-versions' ) { await this.showVersionModal(); } else if ( data === 'link-to-db' ) { await this.showFormDatabaseLinkModal(); } }); await popover.present(); } async showFormDatabaseLinkModal() { const modal = await this.modals.create({ component: DatabaseLinkComponent, componentProps: { pageId: this.pageId, editorUUID: this.editorService.instanceUUID, }, cssClass: 'modal-med', }); modal.onDidDismiss().then(data => { debug('Database link closed', data); }); const modalState = { modal : true, desc : 'Link form to database' }; history.pushState(modalState, null); await modal.present(); } async showVersionModal() { const modal = await this.modals.create({ component: VersionModalComponent, componentProps: { pageId: this.pageId, }, cssClass: 'modal-big', }); modal.onDidDismiss().then(data => { if ( data.data ) { this.editorService.startEditing(this.pageId); } }); const modalState = { modal : true, desc : 'Page versions' }; history.pushState(modalState, null); await modal.present(); } }