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.
frontend/src/app/components/search/Search.component.ts

157 lines
4.8 KiB

import {Component, Input, OnInit, ViewChild} from '@angular/core';
import {IonInput, ModalController} from '@ionic/angular';
import {ApiService} from '../../service/api.service';
import {BehaviorSubject} from 'rxjs';
import {Router} from '@angular/router';
import {NodeTypeIcons} from '../../structures/node-types';
import {debounce, debug} from '../../utility';
import {OpenerService} from '../../service/opener.service';
export interface SearchResult {
title: string;
short_title: string;
type: string;
id: string;
boxId?: string;
associated?: {
title: string,
type: 'page',
id: string
};
}
@Component({
selector: 'noded-search-modal',
templateUrl: './Search.component.html',
styleUrls: ['./Search.component.scss'],
})
export class SearchComponent implements OnInit {
@ViewChild('ionInput') ionInput: IonInput;
@Input() query = '';
public results: BehaviorSubject<SearchResult[]> = new BehaviorSubject<SearchResult[]>([]);
public allResults: BehaviorSubject<SearchResult[]> = new BehaviorSubject<SearchResult[]>([]);
public loading = false;
public typeIcons = NodeTypeIcons;
public allResultTypes = ['all', 'page', 'node', 'db', 'code', 'files'];
public resultTypes = ['all'];
public typeTitles = {
all: 'All',
page: 'Pages',
node: 'Nodes',
db: 'Databases',
code: 'Code',
files: 'Files',
};
public selectedType = 'all';
protected searchChangeDebounce = debounce(async ($event) => {
const query = $event.detail.value;
const results = await this.search(query);
this.allResults.next(results);
if ( !this.allResults.getValue() ) {
this.resultTypes = ['all'];
} else {
const existentTypes = this.allResults.getValue().map(x => this.classifyResult(x));
this.resultTypes = this.allResultTypes.filter((x: any) => existentTypes.includes(x) || x === 'all');
}
this.filterView(this.selectedType);
this.loading = false;
}, 1000);
constructor(
protected ionModalController: ModalController,
protected api: ApiService,
protected router: Router,
protected opener: OpenerService,
) { }
async dismiss() {
await this.ionModalController.dismiss();
}
public isDark() {
return document.body.classList.contains('dark');
}
filterView(name: string) {
this.selectedType = name;
this.results.next((this.allResults.getValue() || []).filter(x => {
if ( this.selectedType === 'all' ) {
return true;
}
return this.classifyResult(x) === this.selectedType;
}));
}
classifyResult(result: SearchResult) {
if ( ['page', 'form'].includes(result.type) ) {
return 'page';
} else if ( ['node', 'markdown'].includes(result.type) ) {
return 'node';
} else if ( result.type === 'code' ) {
return 'code';
} else if ( result.type === 'db' ) {
return 'db';
} else if ( ['file_box', 'files'].includes(result.type) || result.type.startsWith('file_box') ) {
return 'files';
}
}
ngOnInit() {
setTimeout(() => {
this.ionInput.setFocus();
}, 400);
}
async onSearchChange($event) {
this.loading = true;
this.searchChangeDebounce($event);
}
async search(query): Promise<SearchResult[]> {
return new Promise(resolve => {
this.api.get(`/search?query=${query}`).subscribe(res => {
resolve(res.data.results as SearchResult[]);
});
});
}
async openResult(result: SearchResult) {
debug('Search result:', result);
const nodeTypes = [
'node', 'code', 'files', 'markdown',
];
if ( result.type === 'page' ) {
await this.opener.openTarget(result.id);
await this.dismiss();
} else if ( nodeTypes.includes(result.type) ) {
await this.opener.openTarget(result.associated.id, result.id);
await this.dismiss();
} else if ( result.type === 'db' ) {
await this.opener.openDatabase(result.associated.id, result.id);
} else if ( result.type.startsWith('file_box') ) {
await this.opener.openFileBox(result.associated.id, result.id, result.boxId);
} else {
debug('No open handler for search result!');
}
}
async openRelated(event, result: SearchResult) {
event.preventDefault();
event.stopPropagation();
if ( result.associated ) {
if ( result.associated.type === 'page' ) {
await this.opener.openTarget(result.associated.id);
await this.dismiss();
}
}
}
}