From aa7659ee1f83692ffde5f6dc1058ff369dbbfa76 Mon Sep 17 00:00:00 2001 From: mProjectsCode Date: Sat, 11 Jun 2022 15:12:24 +0200 Subject: [PATCH] cancel and skip buttons #26 --- src/main.ts | 58 +++++++++++++++++------- src/modals/MediaDbAdvancedSearchModal.ts | 2 + src/modals/MediaDbFolderImportModal.ts | 40 +++++++++++++--- src/modals/MediaDbIdSearchModal.ts | 4 ++ src/modals/MediaDbSearchResultModal.ts | 28 +++++++++--- src/modals/SelectModal.ts | 45 +++++++++--------- src/modals/SelectModalElement.ts | 2 +- src/utils/Utils.ts | 12 +++++ styles.css | 4 ++ 9 files changed, 144 insertions(+), 51 deletions(-) diff --git a/src/main.ts b/src/main.ts index e08b3b1..3bbf6aa 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,7 +2,7 @@ import {Notice, Plugin, TFile, TFolder} from 'obsidian'; import {DEFAULT_SETTINGS, MediaDbPluginSettings, MediaDbSettingTab} from './settings/Settings'; import {APIManager} from './api/APIManager'; import {MediaTypeModel} from './models/MediaTypeModel'; -import {dateTimeToString, markdownTable, replaceIllegalFileNameCharactersInString} from './utils/Utils'; +import {dateTimeToString, markdownTable, replaceIllegalFileNameCharactersInString, UserCancelError, UserSkipError} from './utils/Utils'; import {OMDbAPI} from './api/apis/OMDbAPI'; import {MediaDbAdvancedSearchModal} from './modals/MediaDbAdvancedSearchModal'; import {MediaDbSearchResultModal} from './modals/MediaDbSearchResultModal'; @@ -177,10 +177,11 @@ export default class MediaDbPlugin extends Plugin { async createEntriesFromFolder(folder: TFolder) { const erroredFiles: { filePath: string, error: string }[] = []; + let canceled: boolean = false; - const {selectedAPI, titleFieldName} = await new Promise((resolve, reject) => { - new MediaDbFolderImportModal(this.app, this, ((selectedAPI, titleFieldName) => { - resolve({selectedAPI, titleFieldName}); + const {selectedAPI, titleFieldName, appendContent} = await new Promise((resolve, reject) => { + new MediaDbFolderImportModal(this.app, this, ((selectedAPI, titleFieldName, appendContent) => { + resolve({selectedAPI, titleFieldName, appendContent}); })).open(); }); @@ -193,6 +194,11 @@ export default class MediaDbPlugin extends Plugin { for (const child of folder.children) { if (child instanceof TFile) { const file = child as TFile; + if (canceled) { + erroredFiles.push({filePath: file.path, error: 'user canceled'}); + continue; + } + let metadata: any = this.app.metadataCache.getFileCache(file).frontmatter; let title = metadata[titleFieldName]; @@ -213,22 +219,40 @@ export default class MediaDbPlugin extends Plugin { continue; } - let selectedResults: MediaTypeModel[] = await new Promise((resolve, reject) => { - const searchResultModal = new MediaDbSearchResultModal(this.app, this, results, (err, res) => { - if (err) { - return reject(err); - } - resolve(res); - }, () => { - resolve([]); - }); + let selectedResults: MediaTypeModel[] = []; + try { + selectedResults = await new Promise((resolve, reject) => { + const searchResultModal = new MediaDbSearchResultModal(this.app, this, results, true, (err, res) => { + if (err) { + return reject(err); + } + resolve(res); + }, () => { + reject(new UserCancelError('user canceled')); + }, () => { + reject(new UserSkipError('user skipped')); + }); - searchResultModal.title = `Results for \'${title}\'`; - searchResultModal.open(); - }); + searchResultModal.title = `Results for \'${title}\'`; + searchResultModal.open(); + }); + } catch (e) { + if (e instanceof UserCancelError) { + erroredFiles.push({filePath: file.path, error: e.message}); + canceled = true; + continue; + } else if (e instanceof UserSkipError) { + erroredFiles.push({filePath: file.path, error: e.message}); + continue; + } else { + erroredFiles.push({filePath: file.path, error: e.message}); + continue; + } + } if (selectedResults.length === 0) { erroredFiles.push({filePath: file.path, error: `no search results selected`}); + continue; } await this.createMediaDbNote(async () => selectedResults); @@ -253,7 +277,7 @@ export default class MediaDbPlugin extends Plugin { if (err) { return reject(err); } - new MediaDbSearchResultModal(this.app, this, results, (err2, res) => { + new MediaDbSearchResultModal(this.app, this, results, false, (err2, res) => { if (err2) { return reject(err2); } diff --git a/src/modals/MediaDbAdvancedSearchModal.ts b/src/modals/MediaDbAdvancedSearchModal.ts index c878e61..5b28370 100644 --- a/src/modals/MediaDbAdvancedSearchModal.ts +++ b/src/modals/MediaDbAdvancedSearchModal.ts @@ -81,6 +81,7 @@ export class MediaDbAdvancedSearchModal extends Modal { contentEl.appendChild(searchComponent.inputEl); searchComponent.inputEl.focus(); + contentEl.createDiv({cls: 'media-db-plugin-spacer'}); contentEl.createEl('h3', {text: 'APIs to search'}); const apiToggleComponents: Component[] = []; @@ -102,6 +103,7 @@ export class MediaDbAdvancedSearchModal extends Modal { apiToggleComponentWrapper.appendChild(apiToggleComponent.toggleEl); } + contentEl.createDiv({cls: 'media-db-plugin-spacer'}); new Setting(contentEl) .addButton(btn => btn.setButtonText('Cancel').onClick(() => this.close())) diff --git a/src/modals/MediaDbFolderImportModal.ts b/src/modals/MediaDbFolderImportModal.ts index 69b579f..ed8f675 100644 --- a/src/modals/MediaDbFolderImportModal.ts +++ b/src/modals/MediaDbFolderImportModal.ts @@ -1,14 +1,15 @@ -import {App, ButtonComponent, DropdownComponent, Modal, Setting, TextComponent} from 'obsidian'; +import {App, ButtonComponent, DropdownComponent, Modal, Setting, TextComponent, ToggleComponent} from 'obsidian'; import MediaDbPlugin from '../main'; export class MediaDbFolderImportModal extends Modal { plugin: MediaDbPlugin; - onSubmit: (selectedAPI: string, titleFieldName: string) => void; + onSubmit: (selectedAPI: string, titleFieldName: string, appendContent: boolean) => void; selectedApi: string; searchBtn: ButtonComponent; titleFieldName: string; + appendContent: boolean; - constructor(app: App, plugin: MediaDbPlugin, onSubmit: (selectedAPI: string, titleFieldName: string) => void) { + constructor(app: App, plugin: MediaDbPlugin, onSubmit: (selectedAPI: string, titleFieldName: string, appendContent: boolean) => void) { super(app); this.plugin = plugin; this.onSubmit = onSubmit; @@ -16,7 +17,7 @@ export class MediaDbFolderImportModal extends Modal { } submit() { - this.onSubmit(this.selectedApi, this.titleFieldName); + this.onSubmit(this.selectedApi, this.titleFieldName, this.appendContent); this.close(); } @@ -39,14 +40,41 @@ export class MediaDbFolderImportModal extends Modal { apiSelectorWrapper.appendChild(apiSelectorComponent.selectEl); - const placeholder = 'Title metadata field name'; + contentEl.createDiv({cls: 'media-db-plugin-spacer'}); + contentEl.createEl('h3', {text: 'Append note content to Media DB entry.'}); + + const appendContentToggleElementWrapper = contentEl.createEl('div', {cls: 'media-db-plugin-list-wrapper'}); + const appendContentToggleTextWrapper = appendContentToggleElementWrapper.createEl('div', {cls: 'media-db-plugin-list-text-wrapper'}); + appendContentToggleTextWrapper.createEl('span', { + text: 'If this is enabled, the plugin will override meta data fields with the same name.', + cls: 'media-db-plugin-list-text', + }); + + const appendContentToggleComponentWrapper = appendContentToggleElementWrapper.createEl('div', {cls: 'media-db-plugin-list-toggle'}); + + const appendContentToggle = new ToggleComponent(appendContentToggleElementWrapper); + appendContentToggle.setValue(false); + appendContentToggle.onChange(value => this.appendContent = value); + appendContentToggleComponentWrapper.appendChild(appendContentToggle.toggleEl); + + + contentEl.createDiv({cls: 'media-db-plugin-spacer'}); + contentEl.createEl('h3', {text: 'The name of the mata data field that should be used as the title to query'}); + + const placeholder = 'title'; const titleFieldNameComponent = new TextComponent(contentEl); titleFieldNameComponent.inputEl.style.width = '100%'; titleFieldNameComponent.setPlaceholder(placeholder); titleFieldNameComponent.onChange(value => this.titleFieldName = value); - + titleFieldNameComponent.inputEl.addEventListener('keydown', (ke) => { + if (ke.key === 'Enter') { + this.submit(); + } + }); contentEl.appendChild(titleFieldNameComponent.inputEl); + contentEl.createDiv({cls: 'media-db-plugin-spacer'}); + new Setting(contentEl) .addButton(btn => btn.setButtonText('Cancel').onClick(() => this.close())) .addButton(btn => btn.setButtonText('Ok').setCta().onClick(() => this.submit())); diff --git a/src/modals/MediaDbIdSearchModal.ts b/src/modals/MediaDbIdSearchModal.ts index 035e8fb..31c74d6 100644 --- a/src/modals/MediaDbIdSearchModal.ts +++ b/src/modals/MediaDbIdSearchModal.ts @@ -75,6 +75,8 @@ export class MediaDbIdSearchModal extends Modal { contentEl.appendChild(searchComponent.inputEl); searchComponent.inputEl.focus(); + contentEl.createDiv({cls: 'media-db-plugin-spacer'}); + const apiSelectorWrapper = contentEl.createEl('div', {cls: 'media-db-plugin-list-wrapper'}); const apiSelectorTExtWrapper = apiSelectorWrapper.createEl('div', {cls: 'media-db-plugin-list-text-wrapper'}); apiSelectorTExtWrapper.createEl('span', {text: 'API to search', cls: 'media-db-plugin-list-text'}); @@ -88,6 +90,8 @@ export class MediaDbIdSearchModal extends Modal { } apiSelectorWrapper.appendChild(apiSelectorComponent.selectEl); + contentEl.createDiv({cls: 'media-db-plugin-spacer'}); + new Setting(contentEl) .addButton(btn => btn.setButtonText('Cancel').onClick(() => this.close())) .addButton(btn => { diff --git a/src/modals/MediaDbSearchResultModal.ts b/src/modals/MediaDbSearchResultModal.ts index 5bddc99..ec7015a 100644 --- a/src/modals/MediaDbSearchResultModal.ts +++ b/src/modals/MediaDbSearchResultModal.ts @@ -6,17 +6,24 @@ import {SelectModal} from './SelectModal'; export class MediaDbSearchResultModal extends SelectModal { plugin: MediaDbPlugin; heading: string; - onChoose: (error: Error, result: MediaTypeModel[]) => void; + onSubmit: (error: Error, result: MediaTypeModel[]) => void; onCancel: () => void; + onSkip: () => void; - constructor(app: App, plugin: MediaDbPlugin, elements: MediaTypeModel[], onChoose: (error: Error, result: MediaTypeModel[]) => void, onCancel: () => void) { + sendCallback: boolean; + + constructor(app: App, plugin: MediaDbPlugin, elements: MediaTypeModel[], skipButton: boolean, onSubmit: (error: Error, result: MediaTypeModel[]) => void, onCancel: () => void, onSkip?: () => void) { super(app, elements); this.plugin = plugin; - this.onChoose = onChoose; + this.onSubmit = onSubmit; this.onCancel = onCancel; + this.onSkip = onSkip; this.title = 'Search Results'; this.description = 'Select one or multiple search results.'; + this.skipButton = skipButton; + + this.sendCallback = false; } // Renders each suggestion item. @@ -27,12 +34,21 @@ export class MediaDbSearchResultModal extends SelectModal { } // Perform action on the selected suggestion. - onSubmit() { - this.onChoose(null, this.selectModalElements.filter(x => x.isActive()).map(x => x.value)); + submit() { + this.onSubmit(null, this.selectModalElements.filter(x => x.isActive()).map(x => x.value)); + this.sendCallback = true; + this.close(); + } + + skip() { + this.onSkip(); + this.sendCallback = true; this.close(); } onClose() { - this.onCancel(); + if (!this.sendCallback) { + this.onCancel(); + } } } diff --git a/src/modals/SelectModal.ts b/src/modals/SelectModal.ts index c6423b8..35b278b 100644 --- a/src/modals/SelectModal.ts +++ b/src/modals/SelectModal.ts @@ -2,27 +2,33 @@ import {App, Modal, Setting} from 'obsidian'; import {SelectModalElement} from './SelectModalElement'; export abstract class SelectModal extends Modal { - multiSelect: boolean; allowMultiSelect: boolean; title: string; description: string; + skipButton: boolean; elements: T[]; selectModalElements: SelectModalElement[]; - constructor(app: App, elements: T[]) { + protected constructor(app: App, elements: T[]) { super(app); - this.elements = elements; this.allowMultiSelect = true; + this.title = ''; + this.description = ''; + this.skipButton = false; + + this.elements = elements; this.selectModalElements = []; } abstract renderElement(value: T, el: HTMLElement): any; - abstract onSubmit(): void; + abstract submit(): void; + + abstract skip(): void; disableAllOtherElements(elementId: number) { for (const selectModalElement of this.selectModalElements) { @@ -35,23 +41,17 @@ export abstract class SelectModal extends Modal { async onOpen() { const {contentEl} = this; + /* + contentEl.id = 'media-db-plugin-modal' + + contentEl.on('keydown', '#' + contentEl.id, (ev, delegateTarget) => { + console.log(ev.key); + }); + */ + contentEl.createEl('h2', {text: this.title}); contentEl.createEl('p', {text: this.description}); - if (this.allowMultiSelect) { - new Setting(contentEl) - .setName('Select Multiple') - .addToggle(cb => { - cb.setValue(this.multiSelect); - cb.onChange(value => { - this.multiSelect = value; - for (const selectModalElement of this.selectModalElements) { - selectModalElement.setActive(false); - } - }); - }); - } - const elementWrapper = contentEl.createDiv({cls: 'media-db-plugin-select-wrapper'}); let i = 0; @@ -65,8 +65,11 @@ export abstract class SelectModal extends Modal { i += 1; } - new Setting(contentEl) - .addButton(btn => btn.setButtonText('Cancel').onClick(() => this.close())) - .addButton(btn => btn.setButtonText('Ok').setCta().onClick(() => this.onSubmit())); + const bottomSetting = new Setting(contentEl); + bottomSetting.addButton(btn => btn.setButtonText('Cancel').onClick(() => this.close())); + if (this.skipButton) { + bottomSetting.addButton(btn => btn.setButtonText('Skip').onClick(() => this.skip())); + } + bottomSetting.addButton(btn => btn.setButtonText('Ok').setCta().onClick(() => this.submit())); } } diff --git a/src/modals/SelectModalElement.ts b/src/modals/SelectModalElement.ts index 40c983f..8ef9a74 100644 --- a/src/modals/SelectModalElement.ts +++ b/src/modals/SelectModalElement.ts @@ -24,7 +24,7 @@ export class SelectModalElement { this.element.id = this.getHTMLId(); this.element.on('click', '#' + this.getHTMLId(), () => { this.setActive(!this.active); - if (!this.selectModal.allowMultiSelect || !this.selectModal.multiSelect) { + if (!this.selectModal.allowMultiSelect) { this.selectModal.disableAllOtherElements(this.id); } }); diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index 847e3cb..621d414 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -152,3 +152,15 @@ export function dateTimeToString(dateTime: Date) { return `${dateToString(dateTime)} ${timeToString(dateTime)}`; } +export class UserCancelError extends Error { + constructor(message: string) { + super(message); + } +} + +export class UserSkipError extends Error { + constructor(message: string) { + super(message); + } +} + diff --git a/styles.css b/styles.css index 221a228..7befc32 100644 --- a/styles.css +++ b/styles.css @@ -44,3 +44,7 @@ small.media-db-plugin-list-text{ .media-db-plugin-select-element-hover { background: var(--background-secondary-alt); } + +.media-db-plugin-spacer { + margin-bottom: 10px; +}