diff --git a/src/api/apis/BoardGameGeekAPI.ts b/src/api/apis/BoardGameGeekAPI.ts index 57c5a5b..4198720 100644 --- a/src/api/apis/BoardGameGeekAPI.ts +++ b/src/api/apis/BoardGameGeekAPI.ts @@ -4,7 +4,6 @@ import MediaDbPlugin from '../../main'; import {BoardGameModel} from 'src/models/BoardGameModel'; import {debugLog} from '../../utils/Utils'; import {requestUrl} from 'obsidian'; -import {MediaType} from '../../utils/MediaType'; export class BoardGameGeekAPI extends APIModel { plugin: MediaDbPlugin; diff --git a/src/main.ts b/src/main.ts index 6d93b9c..6d9d2f2 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,9 +2,8 @@ import {MarkdownView, Notice, parseYaml, Plugin, stringifyYaml, TFile, TFolder} import {getDefaultSettings, MediaDbPluginSettings, MediaDbSettingTab} from './settings/Settings'; import {APIManager} from './api/APIManager'; import {MediaTypeModel} from './models/MediaTypeModel'; -import {dateTimeToString, markdownTable, replaceIllegalFileNameCharactersInString, UserCancelError, UserSkipError} from './utils/Utils'; +import {dateTimeToString, markdownTable, replaceIllegalFileNameCharactersInString} from './utils/Utils'; import {OMDbAPI} from './api/apis/OMDbAPI'; -import {MediaDbSearchResultModal} from './modals/MediaDbSearchResultModal'; import {MALAPI} from './api/apis/MALAPI'; import {WikipediaAPI} from './api/apis/WikipediaAPI'; import {MusicBrainzAPI} from './api/apis/MusicBrainzAPI'; @@ -15,7 +14,7 @@ import {PropertyMapper} from './settings/PropertyMapper'; import {YAMLConverter} from './utils/YAMLConverter'; import {MediaDbFolderImportModal} from './modals/MediaDbFolderImportModal'; import {PropertyMapping, PropertyMappingModel} from './settings/PropertyMapping'; -import {ModalHelper} from './utils/ModalHelper'; +import {ModalHelper, ModalResultCode} from './utils/ModalHelper'; export default class MediaDbPlugin extends Plugin { settings: MediaDbPluginSettings; @@ -126,23 +125,23 @@ export default class MediaDbPlugin extends Plugin { */ async createLinkWithSearchModal() { - let apiSearchResults: MediaTypeModel[] = await this.modalHelper.openAdvancedSearchModal(async (advancedSearchOptions) => { - return await this.apiManager.query(advancedSearchOptions.query, advancedSearchOptions.apis); + let apiSearchResults: MediaTypeModel[] = await this.modalHelper.openAdvancedSearchModal({}, async (advancedSearchModalData) => { + return await this.apiManager.query(advancedSearchModalData.query, advancedSearchModalData.apis); }); if (!apiSearchResults) { return; } - const selectResults: MediaTypeModel[] = await this.modalHelper.openSelectModal(apiSearchResults, async (selectedMediaTypeModels) => { - return await this.queryDetails(selectedMediaTypeModels); + const selectResults: MediaTypeModel[] = await this.modalHelper.openSelectModal({elements: apiSearchResults, multiSelect: false}, async (selectModalData) => { + return await this.queryDetails(selectModalData.selected); }); if (!selectResults || selectResults.length < 1) { return; } - const link = `[${selectResults[0].title}](${selectResults[0].url})` + const link = `[${selectResults[0].title}](${selectResults[0].url})`; const view = this.app.workspace.getActiveViewOfType(MarkdownView); @@ -160,8 +159,8 @@ export default class MediaDbPlugin extends Plugin { * TODO: further refactor: extract it into own method, pass the action (api query) as lambda as well as an options object */ async createEntryWithAdvancedSearchModal() { - const apiSearchResults: MediaTypeModel[] = await this.modalHelper.openAdvancedSearchModal(async (advancedSearchOptions) => { - return await this.apiManager.query(advancedSearchOptions.query, advancedSearchOptions.apis); + let apiSearchResults: MediaTypeModel[] = await this.modalHelper.openAdvancedSearchModal({}, async (advancedSearchModalData) => { + return await this.apiManager.query(advancedSearchModalData.query, advancedSearchModalData.apis); }); if (!apiSearchResults) { @@ -227,7 +226,7 @@ export default class MediaDbPlugin extends Plugin { return detailModels; } - async createMediaDbNoteFromModel(mediaTypeModel: MediaTypeModel, options: {attachTemplate?: boolean, attachFile?: TFile, openNote?: boolean}): Promise { + async createMediaDbNoteFromModel(mediaTypeModel: MediaTypeModel, options: { attachTemplate?: boolean, attachFile?: TFile, openNote?: boolean }): Promise { try { console.debug('MDB | creating new note'); @@ -245,7 +244,10 @@ export default class MediaDbPlugin extends Plugin { let fileContent = ''; ({fileMetadata, fileContent} = await this.attachFile(fileMetadata, fileContent, options.attachFile)); - ({fileMetadata, fileContent} = await this.attachTemplate(fileMetadata, fileContent, options.attachTemplate ? await this.mediaTypeManager.getTemplate(mediaTypeModel, this.app) : '')); + ({ + fileMetadata, + fileContent, + } = await this.attachTemplate(fileMetadata, fileContent, options.attachTemplate ? await this.mediaTypeManager.getTemplate(mediaTypeModel, this.app) : '')); fileContent = `---\n${this.settings.useCustomYamlStringifier ? YAMLConverter.toYaml(fileMetadata) : stringifyYaml(fileMetadata)}---\n` + fileContent; return fileContent; @@ -434,46 +436,36 @@ export default class MediaDbPlugin extends Plugin { continue; } - let selectedResults: MediaTypeModel[] = []; - const modal = new MediaDbSearchResultModal(this, results, true); - try { - selectedResults = await new Promise((resolve, reject) => { - modal.title = `Results for \'${title}\'`; - modal.setSubmitCallback(res => resolve(res)); - modal.setSkipCallback(() => reject(new UserCancelError('user skipped'))); - modal.setCloseCallback(err => { - if (err) { - reject(err); - } - reject(new UserCancelError('user canceled')); - }); + let {selectModalResult, selectModal} = await this.modalHelper.createSelectModal({elements: results, skipButton: true, modalTitle: `Results for \'${title}\'`}); - modal.open(); - }); - } catch (e) { - modal.close(); - 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 (selectModalResult.code === ModalResultCode.ERROR) { + erroredFiles.push({filePath: file.path, error: selectModalResult.error.message}); + selectModal.close(); + continue; } - if (selectedResults.length === 0) { + if (selectModalResult.code === ModalResultCode.CLOSE) { + erroredFiles.push({filePath: file.path, error: 'user canceled'}); + selectModal.close(); + canceled = true; + continue; + } + + if (selectModalResult.code === ModalResultCode.SKIP) { + erroredFiles.push({filePath: file.path, error: 'user skipped'}); + selectModal.close(); + continue; + } + + if (selectModalResult.data.selected.length === 0) { erroredFiles.push({filePath: file.path, error: `no search results selected`}); continue; } - const detailedResults = await this.queryDetails(selectedResults); + const detailedResults = await this.queryDetails(selectModalResult.data.selected); await this.createMediaDbNotes(detailedResults, appendContent ? file : null); - modal.close(); + selectModal.close(); } } diff --git a/src/modals/MediaDbAdvancedSearchModal.ts b/src/modals/MediaDbAdvancedSearchModal.ts index 3db3235..fa7ac6b 100644 --- a/src/modals/MediaDbAdvancedSearchModal.ts +++ b/src/modals/MediaDbAdvancedSearchModal.ts @@ -1,26 +1,37 @@ import {ButtonComponent, Modal, Notice, Setting, TextComponent, ToggleComponent} from 'obsidian'; import {MediaTypeModel} from '../models/MediaTypeModel'; import MediaDbPlugin from '../main'; +import {ADVANCED_SEARCH_MODAL_DEFAULT_OPTIONS, AdvancedSearchModalData, AdvancedSearchModalOptions} from '../utils/ModalHelper'; export class MediaDbAdvancedSearchModal extends Modal { + plugin: MediaDbPlugin; + query: string; isBusy: boolean; - plugin: MediaDbPlugin; - searchBtn: ButtonComponent; + title: string; selectedApis: { name: string, selected: boolean }[]; - submitCallback?: (res: { query: string, apis: string[] }) => void; + + searchBtn: ButtonComponent; + + submitCallback?: (res: AdvancedSearchModalData) => void; closeCallback?: (err?: Error) => void; - constructor(plugin: MediaDbPlugin) { + + constructor(plugin: MediaDbPlugin, advancedSearchModalOptions: AdvancedSearchModalOptions) { + advancedSearchModalOptions = Object.assign({}, ADVANCED_SEARCH_MODAL_DEFAULT_OPTIONS, advancedSearchModalOptions); super(plugin.app); + this.plugin = plugin; this.selectedApis = []; + this.title = advancedSearchModalOptions.modalTitle; + this.query = advancedSearchModalOptions.prefilledSearchString; + for (const api of this.plugin.apiManager.apis) { - this.selectedApis.push({name: api.apiName, selected: false}); + this.selectedApis.push({name: api.apiName, selected: advancedSearchModalOptions.preselectedAPIs.contains(api.apiName)}); } } - setSubmitCallback(submitCallback: (res: { query: string, apis: string[] }) => void): void { + setSubmitCallback(submitCallback: (res: AdvancedSearchModalData) => void): void { this.submitCallback = submitCallback; } @@ -59,12 +70,13 @@ export class MediaDbAdvancedSearchModal extends Modal { onOpen() { const {contentEl} = this; - contentEl.createEl('h2', {text: 'Search media db'}); + contentEl.createEl('h2', {text: this.title}); const placeholder = 'Search by title'; const searchComponent = new TextComponent(contentEl); searchComponent.inputEl.style.width = '100%'; searchComponent.setPlaceholder(placeholder); + searchComponent.setValue(this.query); searchComponent.onChange(value => (this.query = value)); searchComponent.inputEl.addEventListener('keydown', this.keyPressCallback.bind(this)); diff --git a/src/modals/MediaDbIdSearchModal.ts b/src/modals/MediaDbIdSearchModal.ts index f53197a..ec29cac 100644 --- a/src/modals/MediaDbIdSearchModal.ts +++ b/src/modals/MediaDbIdSearchModal.ts @@ -1,23 +1,32 @@ import {ButtonComponent, DropdownComponent, Modal, Notice, Setting, TextComponent} from 'obsidian'; import {MediaTypeModel} from '../models/MediaTypeModel'; import MediaDbPlugin from '../main'; +import {ID_SEARCH_MODAL_DEFAULT_OPTIONS, IdSearchModalData, IdSearchModalOptions} from '../utils/ModalHelper'; export class MediaDbIdSearchModal extends Modal { + plugin: MediaDbPlugin; + query: string; isBusy: boolean; - plugin: MediaDbPlugin; - searchBtn: ButtonComponent; + title: string; selectedApi: string; - submitCallback?: (res: { query: string, api: string }, err?: Error) => void; + + searchBtn: ButtonComponent; + + submitCallback?: (res: IdSearchModalData, err?: Error) => void; closeCallback?: (err?: Error) => void; - constructor(plugin: MediaDbPlugin) { + + constructor(plugin: MediaDbPlugin, idSearchModalOptions: IdSearchModalOptions) { + idSearchModalOptions = Object.assign({}, ID_SEARCH_MODAL_DEFAULT_OPTIONS, idSearchModalOptions); super(plugin.app); + this.plugin = plugin; - this.selectedApi = plugin.apiManager.apis[0].apiName; + this.title = idSearchModalOptions.modalTitle; + this.selectedApi = idSearchModalOptions.preselectedAPI || plugin.apiManager.apis[0].apiName; } - setSubmitCallback(submitCallback: (res: { query: string, api: string }, err?: Error) => void): void { + setSubmitCallback(submitCallback: (res: IdSearchModalData, err?: Error) => void): void { this.submitCallback = submitCallback; } @@ -54,7 +63,7 @@ export class MediaDbIdSearchModal extends Modal { onOpen() { const {contentEl} = this; - contentEl.createEl('h2', {text: 'Search media db by id'}); + contentEl.createEl('h2', {text: this.title}); const placeholder = 'Search by id'; const searchComponent = new TextComponent(contentEl); diff --git a/src/modals/MediaDbSearchResultModal.ts b/src/modals/MediaDbSearchResultModal.ts index 546c394..c44a91f 100644 --- a/src/modals/MediaDbSearchResultModal.ts +++ b/src/modals/MediaDbSearchResultModal.ts @@ -1,31 +1,34 @@ import {MediaTypeModel} from '../models/MediaTypeModel'; import MediaDbPlugin from '../main'; import {SelectModal} from './SelectModal'; +import {SELECT_MODAL_OPTIONS_DEFAULT, SelectModalData, SelectModalOptions} from '../utils/ModalHelper'; export class MediaDbSearchResultModal extends SelectModal { plugin: MediaDbPlugin; - heading: string; + busy: boolean; - submitCallback: (res: MediaTypeModel[]) => void; + sendCallback: boolean; + + submitCallback: (res: SelectModalData) => void; closeCallback: (err?: Error) => void; skipCallback: () => void; - sendCallback: boolean; - constructor(plugin: MediaDbPlugin, elements: MediaTypeModel[], skipButton: boolean, allowMultiSelect: boolean = true) { - super(plugin.app, elements, allowMultiSelect); + constructor(plugin: MediaDbPlugin, selectModalOptions: SelectModalOptions) { + selectModalOptions = Object.assign({}, SELECT_MODAL_OPTIONS_DEFAULT, selectModalOptions); + super(plugin.app, selectModalOptions.elements, selectModalOptions.multiSelect); this.plugin = plugin; - this.title = 'Search Results'; + this.title = selectModalOptions.modalTitle; this.description = 'Select one or multiple search results.'; - this.addSkipButton = skipButton; + this.addSkipButton = selectModalOptions.skipButton; this.busy = false; this.sendCallback = false; } - setSubmitCallback(submitCallback: (res: MediaTypeModel[]) => void): void { + setSubmitCallback(submitCallback: (res: SelectModalData) => void): void { this.submitCallback = submitCallback; } @@ -49,7 +52,7 @@ export class MediaDbSearchResultModal extends SelectModal { if (!this.busy) { this.busy = true; this.submitButton.setButtonText('Creating entry...'); - this.submitCallback(this.selectModalElements.filter(x => x.isActive()).map(x => x.value)); + this.submitCallback({selected: this.selectModalElements.filter(x => x.isActive()).map(x => x.value)}); } } diff --git a/src/settings/PropertyMappingModelComponent.svelte b/src/settings/PropertyMappingModelComponent.svelte index da8797b..a0a3b68 100644 --- a/src/settings/PropertyMappingModelComponent.svelte +++ b/src/settings/PropertyMappingModelComponent.svelte @@ -46,16 +46,16 @@
- { /if } - { /if } + { /if } + { /if } - { /each } + { /each } { #if !validationResult?.res }
{validationResult?.err?.message}
- { /if } + { /if }