import server from "../../services/server.js"; import NoteContextAwareWidget from "../note_context_aware_widget.js"; import froca from "../../services/froca.js"; import ws from "../../services/ws.js"; import toastService from "../../services/toast.js"; import treeService from "../../services/tree.js"; import DeleteNoteSearchAction from "../search_actions/delete_note.js"; import DeleteLabelSearchAction from "../search_actions/delete_label.js"; import DeleteRelationSearchAction from "../search_actions/delete_relation.js"; import RenameLabelSearchAction from "../search_actions/rename_label.js"; import SetLabelValueSearchAction from "../search_actions/set_label_value.js"; import SetRelationTargetSearchAction from "../search_actions/set_relation_target.js"; import RenameRelationSearchAction from "../search_actions/rename_relation.js"; import ExecuteScriptSearchAction from "../search_actions/execute_script.js" import SearchString from "../search_options/search_string.js"; import FastSearch from "../search_options/fast_search.js"; import Ancestor from "../search_options/ancestor.js"; import IncludeArchivedNotes from "../search_options/include_archived_notes.js"; import OrderBy from "../search_options/order_by.js"; import SearchScript from "../search_options/search_script.js"; import Limit from "../search_options/limit.js"; import DeleteNoteRevisionsSearchAction from "../search_actions/delete_note_revisions.js"; import Debug from "../search_options/debug.js"; import appContext from "../../services/app_context.js"; const TPL = `
`; const OPTION_CLASSES = [ SearchString, SearchScript, Ancestor, FastSearch, IncludeArchivedNotes, OrderBy, Limit, Debug ]; const ACTION_CLASSES = {}; for (const clazz of [ DeleteNoteSearchAction, DeleteNoteRevisionsSearchAction, DeleteLabelSearchAction, DeleteRelationSearchAction, RenameLabelSearchAction, RenameRelationSearchAction, SetLabelValueSearchAction, SetRelationTargetSearchAction, ExecuteScriptSearchAction ]) { ACTION_CLASSES[clazz.actionName] = clazz; } export default class SearchDefinitionWidget extends NoteContextAwareWidget { isEnabled() { return this.note && this.note.type === 'search'; } getTitle() { return { show: this.isEnabled(), activate: true, title: 'Search parameters', icon: 'bx bx-search' }; } doRender() { this.$widget = $(TPL); this.contentSized(); this.$component = this.$widget.find('.search-definition-widget'); this.$widget.on('click', '[data-search-option-add]', async event => { const searchOptionName = $(event.target).attr('data-search-option-add'); const clazz = OPTION_CLASSES.find(SearchOptionClass => SearchOptionClass.optionName === searchOptionName); if (clazz) { await clazz.create(this.noteId); } else { logError(`Unknown search option ${searchOptionName}`); } this.refresh(); }); this.$widget.on('click', '[data-action-add]', async event => { const actionName = $(event.target).attr('data-action-add'); await server.post(`notes/${this.noteId}/attributes`, { type: 'label', name: 'action', value: JSON.stringify({ name: actionName }) }); this.$widget.find('.action-add-toggle').dropdown('toggle'); await ws.waitForMaxKnownEntityChangeId(); this.refresh(); }); this.$searchOptions = this.$widget.find('.search-options'); this.$actionOptions = this.$widget.find('.action-options'); this.$searchButton = this.$widget.find('.search-button'); this.$searchButton.on('click', () => this.triggerCommand('refreshResults')); this.$searchAndExecuteButton = this.$widget.find('.search-and-execute-button'); this.$searchAndExecuteButton.on('click', () => this.searchAndExecute()); this.$saveToNoteButton = this.$widget.find('.save-to-note-button'); this.$saveToNoteButton.on('click', async () => { const {notePath} = await server.post("special-notes/save-search-note", {searchNoteId: this.noteId}); await ws.waitForMaxKnownEntityChangeId(); await appContext.tabManager.getActiveContext().setNote(notePath); toastService.showMessage("Search note has been saved into " + await treeService.getNotePathTitle(notePath)); }); } async refreshResultsCommand() { try { await froca.loadSearchNote(this.noteId); } catch (e) { toastService.showError(e.message); } this.triggerEvent('searchRefreshed', {ntxId: this.noteContext.ntxId}); } async refreshSearchDefinitionCommand() { await this.refresh(); } async refreshWithNote(note) { this.$component.show(); this.$saveToNoteButton.toggle(!note.getAllNotePaths().find(notePathArr => !notePathArr.includes("hidden"))); this.$searchOptions.empty(); for (const OptionClass of OPTION_CLASSES) { const {attributeType, optionName} = OptionClass; const attr = this.note.getAttribute(attributeType, optionName); this.$widget.find(`[data-search-option-add='${optionName}'`).toggle(!attr); if (attr) { const searchOption = new OptionClass(attr, this.note).setParent(this); this.child(searchOption); this.$searchOptions.append(searchOption.render()); } } this.$actionOptions.empty(); const actionLabels = this.note.getLabels('action'); for (const actionAttr of actionLabels) { let actionDef; try { actionDef = JSON.parse(actionAttr.value); } catch (e) { logError(`Parsing of attribute: '${actionAttr.value}' failed with error: ${e.message}`); continue; } const ActionClass = ACTION_CLASSES[actionDef.name]; if (!ActionClass) { logError(`No action class for '${actionDef.name}' found.`); continue; } const action = new ActionClass(actionAttr, actionDef).setParent(this); this.child(action); this.$actionOptions.append(action.render()); } this.$searchAndExecuteButton.css('visibility', actionLabels.length > 0 ? 'visible' : 'hidden'); } getContent() { return ''; } async searchAndExecute() { await server.post(`search-and-execute-note/${this.noteId}`); this.triggerCommand('refreshResults'); toastService.showMessage('Actions have been executed.', 3000); } }