commit
217e4f7ae1
14 changed files with 819 additions and 231 deletions
|
|
@ -4,7 +4,6 @@ import MediaDbPlugin from '../../main';
|
||||||
import {BoardGameModel} from 'src/models/BoardGameModel';
|
import {BoardGameModel} from 'src/models/BoardGameModel';
|
||||||
import {debugLog} from '../../utils/Utils';
|
import {debugLog} from '../../utils/Utils';
|
||||||
import {requestUrl} from 'obsidian';
|
import {requestUrl} from 'obsidian';
|
||||||
import {MediaType} from '../../utils/MediaType';
|
|
||||||
|
|
||||||
export class BoardGameGeekAPI extends APIModel {
|
export class BoardGameGeekAPI extends APIModel {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
|
|
@ -79,17 +78,16 @@ export class BoardGameGeekAPI extends APIModel {
|
||||||
const genres = Array.from(boardgame.querySelectorAll('boardgamecategory')).map(n => n!.textContent!);
|
const genres = Array.from(boardgame.querySelectorAll('boardgamecategory')).map(n => n!.textContent!);
|
||||||
|
|
||||||
const model = new BoardGameModel({
|
const model = new BoardGameModel({
|
||||||
type: MediaType.BoardGame,
|
|
||||||
title,
|
title,
|
||||||
englishTitle: title,
|
englishTitle: title,
|
||||||
year: year === '0' ? '' : year,
|
year: year === '0' ? '' : year,
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
url: `https://boardgamegeek.com/boardgame/${id}`,
|
url: `https://boardgamegeek.com/boardgame/${id}`,
|
||||||
id,
|
id: id,
|
||||||
|
|
||||||
genres,
|
genres: genres,
|
||||||
onlineRating,
|
onlineRating: onlineRating,
|
||||||
image,
|
image: image,
|
||||||
released: true,
|
released: true,
|
||||||
|
|
||||||
userData: {
|
userData: {
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ export class WikipediaAPI extends APIModel {
|
||||||
englishTitle: result.title,
|
englishTitle: result.title,
|
||||||
year: '',
|
year: '',
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
|
url: result.fullurl,
|
||||||
id: result.pageid,
|
id: result.pageid,
|
||||||
|
|
||||||
wikiUrl: result.fullurl,
|
wikiUrl: result.fullurl,
|
||||||
|
|
|
||||||
291
src/main.ts
291
src/main.ts
|
|
@ -1,13 +1,10 @@
|
||||||
import {Notice, parseYaml, Plugin, stringifyYaml, TFile, TFolder} from 'obsidian';
|
import {MarkdownView, Notice, parseYaml, Plugin, stringifyYaml, TFile, TFolder} from 'obsidian';
|
||||||
import {getDefaultSettings, MediaDbPluginSettings, MediaDbSettingTab} from './settings/Settings';
|
import {getDefaultSettings, MediaDbPluginSettings, MediaDbSettingTab} from './settings/Settings';
|
||||||
import {APIManager} from './api/APIManager';
|
import {APIManager} from './api/APIManager';
|
||||||
import {MediaTypeModel} from './models/MediaTypeModel';
|
import {MediaTypeModel} from './models/MediaTypeModel';
|
||||||
import {dateTimeToString, markdownTable, replaceIllegalFileNameCharactersInString, UserCancelError, UserSkipError} from './utils/Utils';
|
import {CreateNoteOptions, dateTimeToString, markdownTable, replaceIllegalFileNameCharactersInString} from './utils/Utils';
|
||||||
import {OMDbAPI} from './api/apis/OMDbAPI';
|
import {OMDbAPI} from './api/apis/OMDbAPI';
|
||||||
import {MediaDbAdvancedSearchModal} from './modals/MediaDbAdvancedSearchModal';
|
|
||||||
import {MediaDbSearchResultModal} from './modals/MediaDbSearchResultModal';
|
|
||||||
import {MALAPI} from './api/apis/MALAPI';
|
import {MALAPI} from './api/apis/MALAPI';
|
||||||
import {MediaDbIdSearchModal} from './modals/MediaDbIdSearchModal';
|
|
||||||
import {WikipediaAPI} from './api/apis/WikipediaAPI';
|
import {WikipediaAPI} from './api/apis/WikipediaAPI';
|
||||||
import {MusicBrainzAPI} from './api/apis/MusicBrainzAPI';
|
import {MusicBrainzAPI} from './api/apis/MusicBrainzAPI';
|
||||||
import {MediaTypeManager} from './utils/MediaTypeManager';
|
import {MediaTypeManager} from './utils/MediaTypeManager';
|
||||||
|
|
@ -17,12 +14,14 @@ import {PropertyMapper} from './settings/PropertyMapper';
|
||||||
import {YAMLConverter} from './utils/YAMLConverter';
|
import {YAMLConverter} from './utils/YAMLConverter';
|
||||||
import {MediaDbFolderImportModal} from './modals/MediaDbFolderImportModal';
|
import {MediaDbFolderImportModal} from './modals/MediaDbFolderImportModal';
|
||||||
import {PropertyMapping, PropertyMappingModel} from './settings/PropertyMapping';
|
import {PropertyMapping, PropertyMappingModel} from './settings/PropertyMapping';
|
||||||
|
import {ModalHelper, ModalResultCode} from './utils/ModalHelper';
|
||||||
|
|
||||||
export default class MediaDbPlugin extends Plugin {
|
export default class MediaDbPlugin extends Plugin {
|
||||||
settings: MediaDbPluginSettings;
|
settings: MediaDbPluginSettings;
|
||||||
apiManager: APIManager;
|
apiManager: APIManager;
|
||||||
mediaTypeManager: MediaTypeManager;
|
mediaTypeManager: MediaTypeManager;
|
||||||
modelPropertyMapper: PropertyMapper;
|
modelPropertyMapper: PropertyMapper;
|
||||||
|
modalHelper: ModalHelper;
|
||||||
|
|
||||||
frontMatterRexExpPattern: string = '^(---)\\n[\\s\\S]*?\\n---';
|
frontMatterRexExpPattern: string = '^(---)\\n[\\s\\S]*?\\n---';
|
||||||
|
|
||||||
|
|
@ -39,6 +38,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
|
|
||||||
this.mediaTypeManager = new MediaTypeManager();
|
this.mediaTypeManager = new MediaTypeManager();
|
||||||
this.modelPropertyMapper = new PropertyMapper(this);
|
this.modelPropertyMapper = new PropertyMapper(this);
|
||||||
|
this.modalHelper = new ModalHelper(this);
|
||||||
|
|
||||||
await this.loadSettings();
|
await this.loadSettings();
|
||||||
// register the settings tab
|
// register the settings tab
|
||||||
|
|
@ -57,7 +57,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
menu.addItem(item => {
|
menu.addItem(item => {
|
||||||
item.setTitle('Import folder as Media DB entries')
|
item.setTitle('Import folder as Media DB entries')
|
||||||
.setIcon('database')
|
.setIcon('database')
|
||||||
.onClick(() => this.createEntriesFromFolder(file as TFolder));
|
.onClick(() => this.createEntriesFromFolder(file));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
@ -77,94 +77,139 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
// register command to update the open note
|
// register command to update the open note
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: 'update-media-db-note',
|
id: 'update-media-db-note',
|
||||||
name: 'Update the open note, if it is a Media DB entry.',
|
name: 'Update open note (this will recreate the note)',
|
||||||
checkCallback: (checking: boolean) => {
|
checkCallback: (checking: boolean) => {
|
||||||
if (!this.app.workspace.getActiveFile()) {
|
if (!this.app.workspace.getActiveFile()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!checking) {
|
if (!checking) {
|
||||||
this.updateActiveNote();
|
this.updateActiveNote(false);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.addCommand({
|
||||||
|
id: 'update-media-db-note-metadata',
|
||||||
|
name: 'Update metadata',
|
||||||
|
checkCallback: (checking: boolean) => {
|
||||||
|
if (!this.app.workspace.getActiveFile()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!checking) {
|
||||||
|
this.updateActiveNote(true);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// register link insert command
|
||||||
|
this.addCommand({
|
||||||
|
id: 'add-media-db-link',
|
||||||
|
name: 'Insert link',
|
||||||
|
checkCallback: (checking: boolean) => {
|
||||||
|
if (!this.app.workspace.getActiveFile()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!checking) {
|
||||||
|
this.createLinkWithSearchModal();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* first very simple approach
|
||||||
|
* TODO:
|
||||||
|
* - replace the detail query
|
||||||
|
* - maybe custom link syntax
|
||||||
|
*/
|
||||||
|
async createLinkWithSearchModal() {
|
||||||
|
|
||||||
|
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({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 view = this.app.workspace.getActiveViewOfType(MarkdownView);
|
||||||
|
|
||||||
|
// Make sure the user is editing a Markdown file.
|
||||||
|
if (view) {
|
||||||
|
view.editor.replaceRange(link, view.editor.getCursor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async createEntryWithSearchModal() {
|
async createEntryWithSearchModal() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: further refactor: extract it into own method, pass the action (api query) as lambda as well as an options object
|
||||||
|
*/
|
||||||
async createEntryWithAdvancedSearchModal() {
|
async createEntryWithAdvancedSearchModal() {
|
||||||
let results: MediaTypeModel[] = [];
|
let apiSearchResults: MediaTypeModel[] = await this.modalHelper.openAdvancedSearchModal({}, async (advancedSearchModalData) => {
|
||||||
|
return await this.apiManager.query(advancedSearchModalData.query, advancedSearchModalData.apis);
|
||||||
|
});
|
||||||
|
|
||||||
const {advancedSearchOptions, advancedSearchModal} = await this.openMediaDbAdvancedSearchModal();
|
if (!apiSearchResults) {
|
||||||
if (!advancedSearchOptions) {
|
// TODO: add new notice saying no results found?
|
||||||
advancedSearchModal.close();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let apiSearchResults: MediaTypeModel[] = undefined;
|
let selectResults: MediaTypeModel[];
|
||||||
try {
|
let proceed: boolean;
|
||||||
apiSearchResults = await this.apiManager.query(advancedSearchOptions.query, advancedSearchOptions.apis);
|
|
||||||
} catch (e) {
|
while (!proceed) {
|
||||||
console.warn(e);
|
selectResults = await this.modalHelper.openSelectModal({elements: apiSearchResults}, async (selectModalData) => {
|
||||||
new Notice(e.toString());
|
return await this.queryDetails(selectModalData.selected);
|
||||||
advancedSearchModal.close();
|
});
|
||||||
|
if (!selectResults) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
advancedSearchModal.close();
|
proceed = await this.modalHelper.openPreviewModal({elements: selectResults}, async (previewModalData) => {
|
||||||
|
return previewModalData.confirmed;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const {selectRes, selectModal} = await this.openMediaDbSelectModal(apiSearchResults, false);
|
await this.createMediaDbNotes(selectResults);
|
||||||
if (!selectRes) {
|
}
|
||||||
selectModal.close();
|
|
||||||
|
async createEntryWithIdSearchModal(): Promise<void> {
|
||||||
|
let idSearchResult: MediaTypeModel;
|
||||||
|
let proceed: boolean;
|
||||||
|
|
||||||
|
while (!proceed) {
|
||||||
|
idSearchResult = await this.modalHelper.openIdSearchModal({}, async (idSearchModalData) => {
|
||||||
|
return await this.apiManager.queryDetailedInfoById(idSearchModalData.query, idSearchModalData.api);
|
||||||
|
});
|
||||||
|
if (!idSearchResult) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
proceed = await this.modalHelper.openPreviewModal({elements: [idSearchResult]}, async (previewModalData) => {
|
||||||
results = await this.queryDetails(selectRes);
|
return previewModalData.confirmed;
|
||||||
} catch (e) {
|
});
|
||||||
console.warn(e);
|
|
||||||
new Notice(e.toString());
|
|
||||||
selectModal.close();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
selectModal.close();
|
await this.createMediaDbNoteFromModel(idSearchResult, {attachTemplate: true, openNote: true});
|
||||||
|
|
||||||
if (results) {
|
|
||||||
await this.createMediaDbNotes(results);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async createEntryWithIdSearchModal() {
|
|
||||||
let result: MediaTypeModel = undefined;
|
|
||||||
|
|
||||||
const {idSearchOptions, idSearchModal} = await this.openMediaDbIdSearchModal();
|
|
||||||
if (!idSearchOptions) {
|
|
||||||
idSearchModal.close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
result = await this.apiManager.queryDetailedInfoById(idSearchOptions.query, idSearchOptions.api);
|
|
||||||
} catch (e) {
|
|
||||||
console.warn(e);
|
|
||||||
new Notice(e.toString());
|
|
||||||
idSearchModal.close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
idSearchModal.close();
|
|
||||||
|
|
||||||
if (result) {
|
|
||||||
await this.createMediaDbNoteFromModel(result);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async createMediaDbNotes(models: MediaTypeModel[], attachFile?: TFile): Promise<void> {
|
async createMediaDbNotes(models: MediaTypeModel[], attachFile?: TFile): Promise<void> {
|
||||||
for (const model of models) {
|
for (const model of models) {
|
||||||
await this.createMediaDbNoteFromModel(model, attachFile);
|
await this.createMediaDbNoteFromModel(model, {attachTemplate: true, attachFile: attachFile});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -181,27 +226,28 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
return detailModels;
|
return detailModels;
|
||||||
}
|
}
|
||||||
|
|
||||||
async createMediaDbNoteFromModel(mediaTypeModel: MediaTypeModel, attachFile?: TFile): Promise<void> {
|
async createMediaDbNoteFromModel(mediaTypeModel: MediaTypeModel, options: CreateNoteOptions): Promise<void> {
|
||||||
try {
|
try {
|
||||||
console.debug('MDB | creating new note');
|
console.debug('MDB | creating new note');
|
||||||
|
|
||||||
let fileContent = await this.generateMediaDbNoteContents(mediaTypeModel, attachFile);
|
let fileContent = await this.generateMediaDbNoteContents(mediaTypeModel, options);
|
||||||
|
|
||||||
await this.createNote(this.mediaTypeManager.getFileName(mediaTypeModel), fileContent);
|
await this.createNote(this.mediaTypeManager.getFileName(mediaTypeModel), fileContent, options.openNote);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(e);
|
console.warn(e);
|
||||||
new Notice(e.toString());
|
new Notice(e.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async generateMediaDbNoteContents(mediaTypeModel: MediaTypeModel, attachFile: TFile) {
|
async generateMediaDbNoteContents(mediaTypeModel: MediaTypeModel, options: CreateNoteOptions) {
|
||||||
let fileMetadata = this.modelPropertyMapper.convertObject(mediaTypeModel.toMetaDataObject());
|
let fileMetadata = this.modelPropertyMapper.convertObject(mediaTypeModel.toMetaDataObject());
|
||||||
let fileContent = '';
|
let fileContent = '';
|
||||||
|
const template = options.attachTemplate ? await this.mediaTypeManager.getTemplate(mediaTypeModel, this.app) : '';
|
||||||
|
|
||||||
({fileMetadata, fileContent} = await this.attachFile(fileMetadata, fileContent, attachFile));
|
({fileMetadata, fileContent} = await this.attachFile(fileMetadata, fileContent, options.attachFile));
|
||||||
({fileMetadata, fileContent} = await this.attachTemplate(fileMetadata, fileContent, await this.mediaTypeManager.getTemplate(mediaTypeModel, this.app)));
|
({fileMetadata, fileContent} = await this.attachTemplate(fileMetadata, fileContent, template));
|
||||||
|
|
||||||
fileContent = `---\n${this.settings.useCustomYamlStringifier ? YAMLConverter.toYaml(fileMetadata) : stringifyYaml(fileMetadata)}---` + fileContent;
|
fileContent = `---\n${this.settings.useCustomYamlStringifier ? YAMLConverter.toYaml(fileMetadata) : stringifyYaml(fileMetadata)}---\n` + fileContent;
|
||||||
return fileContent;
|
return fileContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -214,8 +260,9 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
fileMetadata = Object.assign(attachFileMetadata, fileMetadata);
|
fileMetadata = Object.assign(attachFileMetadata, fileMetadata);
|
||||||
|
|
||||||
let attachFileContent: string = await this.app.vault.read(fileToAttach);
|
let attachFileContent: string = await this.app.vault.read(fileToAttach);
|
||||||
const regExp = new RegExp('^(---)\\n[\\s\\S]*\\n---');
|
const regExp = new RegExp(this.frontMatterRexExpPattern);
|
||||||
attachFileContent = attachFileContent.replace(regExp, '');
|
attachFileContent = attachFileContent.replace(regExp, '');
|
||||||
|
attachFileContent = attachFileContent.startsWith('\n') ? attachFileContent.substring(1) : attachFileContent;
|
||||||
fileContent += attachFileContent;
|
fileContent += attachFileContent;
|
||||||
|
|
||||||
return {fileMetadata: fileMetadata, fileContent: fileContent};
|
return {fileMetadata: fileMetadata, fileContent: fileContent};
|
||||||
|
|
@ -315,7 +362,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
* Update the active note by querying the API again.
|
* Update the active note by querying the API again.
|
||||||
* Tries to read the type, id and dataSource of the active note. If successful it will query the api, delete the old note and create a new one.
|
* Tries to read the type, id and dataSource of the active note. If successful it will query the api, delete the old note and create a new one.
|
||||||
*/
|
*/
|
||||||
async updateActiveNote() {
|
async updateActiveNote(onlyMetadata: boolean = false) {
|
||||||
const activeFile: TFile = this.app.workspace.getActiveFile();
|
const activeFile: TFile = this.app.workspace.getActiveFile();
|
||||||
if (!activeFile) {
|
if (!activeFile) {
|
||||||
throw new Error('MDB | there is no active note');
|
throw new Error('MDB | there is no active note');
|
||||||
|
|
@ -341,7 +388,12 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
|
|
||||||
// deletion not happening anymore why is this log statement still here
|
// deletion not happening anymore why is this log statement still here
|
||||||
console.debug('MDB | deleting old entry');
|
console.debug('MDB | deleting old entry');
|
||||||
await this.createMediaDbNoteFromModel(newMediaTypeModel, activeFile);
|
if (onlyMetadata) {
|
||||||
|
await this.createMediaDbNoteFromModel(newMediaTypeModel, {attachFile: activeFile, openNote: true});
|
||||||
|
} else {
|
||||||
|
await this.createMediaDbNoteFromModel(newMediaTypeModel, {attachTemplate: true, openNote: true});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async createEntriesFromFolder(folder: TFolder) {
|
async createEntriesFromFolder(folder: TFolder) {
|
||||||
|
|
@ -382,46 +434,36 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let selectedResults: MediaTypeModel[] = [];
|
let {selectModalResult, selectModal} = await this.modalHelper.createSelectModal({elements: results, skipButton: true, modalTitle: `Results for \'${title}\'`});
|
||||||
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'));
|
|
||||||
});
|
|
||||||
|
|
||||||
modal.open();
|
if (selectModalResult.code === ModalResultCode.ERROR) {
|
||||||
});
|
erroredFiles.push({filePath: file.path, error: selectModalResult.error.message});
|
||||||
} catch (e) {
|
selectModal.close();
|
||||||
modal.close();
|
continue;
|
||||||
if (e instanceof UserCancelError) {
|
}
|
||||||
erroredFiles.push({filePath: file.path, error: e.message});
|
|
||||||
|
if (selectModalResult.code === ModalResultCode.CLOSE) {
|
||||||
|
erroredFiles.push({filePath: file.path, error: 'user canceled'});
|
||||||
|
selectModal.close();
|
||||||
canceled = true;
|
canceled = true;
|
||||||
continue;
|
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) {
|
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`});
|
erroredFiles.push({filePath: file.path, error: `no search results selected`});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const detailedResults = await this.queryDetails(selectedResults);
|
const detailedResults = await this.queryDetails(selectModalResult.data.selected);
|
||||||
await this.createMediaDbNotes(detailedResults, appendContent ? file : null);
|
await this.createMediaDbNotes(detailedResults, appendContent ? file : null);
|
||||||
|
|
||||||
modal.close();
|
selectModal.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -441,55 +483,6 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
const targetFile = await this.app.vault.create(filePath, fileContent);
|
const targetFile = await this.app.vault.create(filePath, fileContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
async openMediaDbAdvancedSearchModal(): Promise<{ advancedSearchOptions: { query: string, apis: string[] }, advancedSearchModal: MediaDbAdvancedSearchModal }> {
|
|
||||||
const modal = new MediaDbAdvancedSearchModal(this);
|
|
||||||
const res: { query: string, apis: string[] } = await new Promise((resolve, reject) => {
|
|
||||||
modal.setSubmitCallback(res => resolve(res));
|
|
||||||
modal.setCloseCallback(err => {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
resolve(undefined);
|
|
||||||
});
|
|
||||||
|
|
||||||
modal.open();
|
|
||||||
});
|
|
||||||
return {advancedSearchOptions: res, advancedSearchModal: modal};
|
|
||||||
}
|
|
||||||
|
|
||||||
async openMediaDbIdSearchModal(): Promise<{ idSearchOptions: { query: string, api: string }, idSearchModal: MediaDbIdSearchModal }> {
|
|
||||||
const modal = new MediaDbIdSearchModal(this);
|
|
||||||
const res: { query: string, api: string } = await new Promise((resolve, reject) => {
|
|
||||||
modal.setSubmitCallback(res => resolve(res));
|
|
||||||
modal.setCloseCallback(err => {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
resolve(undefined);
|
|
||||||
});
|
|
||||||
|
|
||||||
modal.open();
|
|
||||||
});
|
|
||||||
return {idSearchOptions: res, idSearchModal: modal};
|
|
||||||
}
|
|
||||||
|
|
||||||
async openMediaDbSelectModal(resultsToDisplay: MediaTypeModel[], skipButton: boolean = false): Promise<{ selectRes: MediaTypeModel[], selectModal: MediaDbSearchResultModal }> {
|
|
||||||
const modal = new MediaDbSearchResultModal(this, resultsToDisplay, skipButton);
|
|
||||||
const res: MediaTypeModel[] = await new Promise((resolve, reject) => {
|
|
||||||
modal.setSubmitCallback(res => resolve(res));
|
|
||||||
modal.setSkipCallback(() => resolve([]));
|
|
||||||
modal.setCloseCallback(err => {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
resolve(undefined);
|
|
||||||
});
|
|
||||||
|
|
||||||
modal.open();
|
|
||||||
});
|
|
||||||
return {selectRes: res, selectModal: modal};
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadSettings() {
|
async loadSettings() {
|
||||||
// console.log(DEFAULT_SETTINGS);
|
// console.log(DEFAULT_SETTINGS);
|
||||||
const diskSettings: MediaDbPluginSettings = await this.loadData();
|
const diskSettings: MediaDbPluginSettings = await this.loadData();
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,37 @@
|
||||||
import {ButtonComponent, Modal, Notice, Setting, TextComponent, ToggleComponent} from 'obsidian';
|
import {ButtonComponent, Modal, Notice, Setting, TextComponent, ToggleComponent} from 'obsidian';
|
||||||
import {MediaTypeModel} from '../models/MediaTypeModel';
|
import {MediaTypeModel} from '../models/MediaTypeModel';
|
||||||
import MediaDbPlugin from '../main';
|
import MediaDbPlugin from '../main';
|
||||||
|
import {ADVANCED_SEARCH_MODAL_DEFAULT_OPTIONS, AdvancedSearchModalData, AdvancedSearchModalOptions} from '../utils/ModalHelper';
|
||||||
|
|
||||||
export class MediaDbAdvancedSearchModal extends Modal {
|
export class MediaDbAdvancedSearchModal extends Modal {
|
||||||
|
plugin: MediaDbPlugin;
|
||||||
|
|
||||||
query: string;
|
query: string;
|
||||||
isBusy: boolean;
|
isBusy: boolean;
|
||||||
plugin: MediaDbPlugin;
|
title: string;
|
||||||
searchBtn: ButtonComponent;
|
|
||||||
selectedApis: { name: string, selected: boolean }[];
|
selectedApis: { name: string, selected: boolean }[];
|
||||||
submitCallback?: (res: { query: string, apis: string[] }) => void;
|
|
||||||
|
searchBtn: ButtonComponent;
|
||||||
|
|
||||||
|
submitCallback?: (res: AdvancedSearchModalData) => void;
|
||||||
closeCallback?: (err?: Error) => 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);
|
super(plugin.app);
|
||||||
|
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.selectedApis = [];
|
this.selectedApis = [];
|
||||||
|
this.title = advancedSearchModalOptions.modalTitle;
|
||||||
|
this.query = advancedSearchModalOptions.prefilledSearchString;
|
||||||
|
|
||||||
for (const api of this.plugin.apiManager.apis) {
|
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;
|
this.submitCallback = submitCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,7 +47,7 @@ export class MediaDbAdvancedSearchModal extends Modal {
|
||||||
|
|
||||||
async search(): Promise<MediaTypeModel[]> {
|
async search(): Promise<MediaTypeModel[]> {
|
||||||
if (!this.query || this.query.length < 3) {
|
if (!this.query || this.query.length < 3) {
|
||||||
new Notice('MDB | Query to short');
|
new Notice('MDB | Query too short');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,12 +70,13 @@ export class MediaDbAdvancedSearchModal extends Modal {
|
||||||
onOpen() {
|
onOpen() {
|
||||||
const {contentEl} = this;
|
const {contentEl} = this;
|
||||||
|
|
||||||
contentEl.createEl('h2', {text: 'Search media db'});
|
contentEl.createEl('h2', {text: this.title});
|
||||||
|
|
||||||
const placeholder = 'Search by title';
|
const placeholder = 'Search by title';
|
||||||
const searchComponent = new TextComponent(contentEl);
|
const searchComponent = new TextComponent(contentEl);
|
||||||
searchComponent.inputEl.style.width = '100%';
|
searchComponent.inputEl.style.width = '100%';
|
||||||
searchComponent.setPlaceholder(placeholder);
|
searchComponent.setPlaceholder(placeholder);
|
||||||
|
searchComponent.setValue(this.query);
|
||||||
searchComponent.onChange(value => (this.query = value));
|
searchComponent.onChange(value => (this.query = value));
|
||||||
searchComponent.inputEl.addEventListener('keydown', this.keyPressCallback.bind(this));
|
searchComponent.inputEl.addEventListener('keydown', this.keyPressCallback.bind(this));
|
||||||
|
|
||||||
|
|
@ -96,14 +108,19 @@ export class MediaDbAdvancedSearchModal extends Modal {
|
||||||
contentEl.createDiv({cls: 'media-db-plugin-spacer'});
|
contentEl.createDiv({cls: 'media-db-plugin-spacer'});
|
||||||
|
|
||||||
new Setting(contentEl)
|
new Setting(contentEl)
|
||||||
.addButton(btn => btn.setButtonText('Cancel').onClick(() => this.close()))
|
|
||||||
.addButton(btn => {
|
.addButton(btn => {
|
||||||
return (this.searchBtn = btn
|
btn.setButtonText('Cancel');
|
||||||
.setButtonText('Ok')
|
btn.onClick(() => this.close());
|
||||||
.setCta()
|
btn.buttonEl.addClass('media-db-plugin-button');
|
||||||
.onClick(() => {
|
})
|
||||||
|
.addButton(btn => {
|
||||||
|
btn.setButtonText('Ok');
|
||||||
|
btn.setCta();
|
||||||
|
btn.onClick(() => {
|
||||||
this.search();
|
this.search();
|
||||||
}));
|
});
|
||||||
|
btn.buttonEl.addClass('media-db-plugin-button');
|
||||||
|
this.searchBtn = btn;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,8 +76,20 @@ export class MediaDbFolderImportModal extends Modal {
|
||||||
contentEl.createDiv({cls: 'media-db-plugin-spacer'});
|
contentEl.createDiv({cls: 'media-db-plugin-spacer'});
|
||||||
|
|
||||||
new Setting(contentEl)
|
new Setting(contentEl)
|
||||||
.addButton(btn => btn.setButtonText('Cancel').onClick(() => this.close()))
|
.addButton(btn => {
|
||||||
.addButton(btn => btn.setButtonText('Ok').setCta().onClick(() => this.submit()));
|
btn.setButtonText('Cancel');
|
||||||
|
btn.onClick(() => this.close());
|
||||||
|
btn.buttonEl.addClass('media-db-plugin-button');
|
||||||
|
})
|
||||||
|
.addButton(btn => {
|
||||||
|
btn.setButtonText('Ok');
|
||||||
|
btn.setCta();
|
||||||
|
btn.onClick(() => {
|
||||||
|
this.submit();
|
||||||
|
});
|
||||||
|
btn.buttonEl.addClass('media-db-plugin-button');
|
||||||
|
this.searchBtn = btn;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose() {
|
onClose() {
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,32 @@
|
||||||
import {ButtonComponent, DropdownComponent, Modal, Notice, Setting, TextComponent} from 'obsidian';
|
import {ButtonComponent, DropdownComponent, Modal, Notice, Setting, TextComponent} from 'obsidian';
|
||||||
import {MediaTypeModel} from '../models/MediaTypeModel';
|
import {MediaTypeModel} from '../models/MediaTypeModel';
|
||||||
import MediaDbPlugin from '../main';
|
import MediaDbPlugin from '../main';
|
||||||
|
import {ID_SEARCH_MODAL_DEFAULT_OPTIONS, IdSearchModalData, IdSearchModalOptions} from '../utils/ModalHelper';
|
||||||
|
|
||||||
export class MediaDbIdSearchModal extends Modal {
|
export class MediaDbIdSearchModal extends Modal {
|
||||||
|
plugin: MediaDbPlugin;
|
||||||
|
|
||||||
query: string;
|
query: string;
|
||||||
isBusy: boolean;
|
isBusy: boolean;
|
||||||
plugin: MediaDbPlugin;
|
title: string;
|
||||||
searchBtn: ButtonComponent;
|
|
||||||
selectedApi: string;
|
selectedApi: string;
|
||||||
submitCallback?: (res: { query: string, api: string }, err?: Error) => void;
|
|
||||||
|
searchBtn: ButtonComponent;
|
||||||
|
|
||||||
|
submitCallback?: (res: IdSearchModalData, err?: Error) => void;
|
||||||
closeCallback?: (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);
|
super(plugin.app);
|
||||||
|
|
||||||
this.plugin = plugin;
|
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;
|
this.submitCallback = submitCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,7 +63,7 @@ export class MediaDbIdSearchModal extends Modal {
|
||||||
onOpen() {
|
onOpen() {
|
||||||
const {contentEl} = this;
|
const {contentEl} = this;
|
||||||
|
|
||||||
contentEl.createEl('h2', {text: 'Search media db by id'});
|
contentEl.createEl('h2', {text: this.title});
|
||||||
|
|
||||||
const placeholder = 'Search by id';
|
const placeholder = 'Search by id';
|
||||||
const searchComponent = new TextComponent(contentEl);
|
const searchComponent = new TextComponent(contentEl);
|
||||||
|
|
@ -84,14 +93,19 @@ export class MediaDbIdSearchModal extends Modal {
|
||||||
contentEl.createDiv({cls: 'media-db-plugin-spacer'});
|
contentEl.createDiv({cls: 'media-db-plugin-spacer'});
|
||||||
|
|
||||||
new Setting(contentEl)
|
new Setting(contentEl)
|
||||||
.addButton(btn => btn.setButtonText('Cancel').onClick(() => this.close()))
|
|
||||||
.addButton(btn => {
|
.addButton(btn => {
|
||||||
return (this.searchBtn = btn
|
btn.setButtonText('Cancel');
|
||||||
.setButtonText('Ok')
|
btn.onClick(() => this.close());
|
||||||
.setCta()
|
btn.buttonEl.addClass('media-db-plugin-button');
|
||||||
.onClick(() => {
|
})
|
||||||
|
.addButton(btn => {
|
||||||
|
btn.setButtonText('Ok');
|
||||||
|
btn.setCta();
|
||||||
|
btn.onClick(() => {
|
||||||
this.search();
|
this.search();
|
||||||
}));
|
});
|
||||||
|
btn.buttonEl.addClass('media-db-plugin-button');
|
||||||
|
this.searchBtn = btn;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
82
src/modals/MediaDbPreviewModal.ts
Normal file
82
src/modals/MediaDbPreviewModal.ts
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
import {ButtonComponent, MarkdownRenderer, Modal, Setting} from 'obsidian';
|
||||||
|
import MediaDbPlugin from 'src/main';
|
||||||
|
import {MediaTypeModel} from 'src/models/MediaTypeModel';
|
||||||
|
import {PREVIEW_MODAL_DEFAULT_OPTIONS, PreviewModalData, PreviewModalOptions} from '../utils/ModalHelper';
|
||||||
|
import {CreateNoteOptions} from '../utils/Utils';
|
||||||
|
|
||||||
|
export class MediaDbPreviewModal extends Modal {
|
||||||
|
plugin: MediaDbPlugin;
|
||||||
|
|
||||||
|
createNoteOptions: CreateNoteOptions;
|
||||||
|
elements: MediaTypeModel[];
|
||||||
|
isBusy: boolean;
|
||||||
|
title: string;
|
||||||
|
cancelButton: ButtonComponent;
|
||||||
|
submitButton: ButtonComponent;
|
||||||
|
|
||||||
|
submitCallback: (previewModalData: PreviewModalData) => void;
|
||||||
|
closeCallback: (err?: Error) => void;
|
||||||
|
|
||||||
|
constructor(plugin: MediaDbPlugin, previewModalOptions: PreviewModalOptions) {
|
||||||
|
previewModalOptions = Object.assign({}, PREVIEW_MODAL_DEFAULT_OPTIONS, previewModalOptions);
|
||||||
|
|
||||||
|
super(plugin.app);
|
||||||
|
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.title = previewModalOptions.modalTitle;
|
||||||
|
this.elements = previewModalOptions.elements;
|
||||||
|
this.createNoteOptions = previewModalOptions.createNoteOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSubmitCallback(submitCallback: (previewModalData: PreviewModalData) => void): void {
|
||||||
|
this.submitCallback = submitCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCloseCallback(closeCallback: (err?: Error) => void): void {
|
||||||
|
this.closeCallback = closeCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
async preview(): Promise<void> {
|
||||||
|
let {contentEl} = this;
|
||||||
|
contentEl.addClass('media-db-plugin-preview-modal');
|
||||||
|
|
||||||
|
contentEl.createEl('h2', {text: this.title});
|
||||||
|
|
||||||
|
const previewWrapper = contentEl.createDiv({cls: 'media-db-plugin-preview-wrapper'});
|
||||||
|
|
||||||
|
for (let result of this.elements) {
|
||||||
|
previewWrapper.createEl('h3', {text: result.englishTitle});
|
||||||
|
const fileDiv = previewWrapper.createDiv();
|
||||||
|
|
||||||
|
let fileContent = await this.plugin.generateMediaDbNoteContents(result, this.createNoteOptions);
|
||||||
|
fileContent = `\n${fileContent}\n`;
|
||||||
|
|
||||||
|
MarkdownRenderer.renderMarkdown(fileContent, fileDiv, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
contentEl.createDiv({cls: 'media-db-plugin-spacer'});
|
||||||
|
|
||||||
|
const bottomSettingRow = new Setting(contentEl);
|
||||||
|
bottomSettingRow.addButton(btn => {
|
||||||
|
btn.setButtonText('Cancel');
|
||||||
|
btn.onClick(() => this.closeCallback());
|
||||||
|
btn.buttonEl.addClass('media-db-plugin-button');
|
||||||
|
this.cancelButton = btn;
|
||||||
|
});
|
||||||
|
bottomSettingRow.addButton(btn => {
|
||||||
|
btn.setButtonText('Ok');
|
||||||
|
btn.setCta();
|
||||||
|
btn.onClick(() => this.submitCallback({confirmed: true}));
|
||||||
|
btn.buttonEl.addClass('media-db-plugin-button');
|
||||||
|
this.submitButton = btn;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onOpen(): void {
|
||||||
|
this.preview();
|
||||||
|
}
|
||||||
|
|
||||||
|
onClose(): void {
|
||||||
|
this.closeCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,31 +1,34 @@
|
||||||
import {MediaTypeModel} from '../models/MediaTypeModel';
|
import {MediaTypeModel} from '../models/MediaTypeModel';
|
||||||
import MediaDbPlugin from '../main';
|
import MediaDbPlugin from '../main';
|
||||||
import {SelectModal} from './SelectModal';
|
import {SelectModal} from './SelectModal';
|
||||||
|
import {SELECT_MODAL_OPTIONS_DEFAULT, SelectModalData, SelectModalOptions} from '../utils/ModalHelper';
|
||||||
|
|
||||||
export class MediaDbSearchResultModal extends SelectModal<MediaTypeModel> {
|
export class MediaDbSearchResultModal extends SelectModal<MediaTypeModel> {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
heading: string;
|
|
||||||
busy: boolean;
|
busy: boolean;
|
||||||
submitCallback: (res: MediaTypeModel[]) => void;
|
sendCallback: boolean;
|
||||||
|
|
||||||
|
submitCallback: (res: SelectModalData) => void;
|
||||||
closeCallback: (err?: Error) => void;
|
closeCallback: (err?: Error) => void;
|
||||||
skipCallback: () => void;
|
skipCallback: () => void;
|
||||||
|
|
||||||
sendCallback: boolean;
|
|
||||||
|
|
||||||
constructor(plugin: MediaDbPlugin, elements: MediaTypeModel[], skipButton: boolean) {
|
constructor(plugin: MediaDbPlugin, selectModalOptions: SelectModalOptions) {
|
||||||
super(plugin.app, elements);
|
selectModalOptions = Object.assign({}, SELECT_MODAL_OPTIONS_DEFAULT, selectModalOptions);
|
||||||
|
super(plugin.app, selectModalOptions.elements, selectModalOptions.multiSelect);
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
|
|
||||||
this.title = 'Search Results';
|
this.title = selectModalOptions.modalTitle;
|
||||||
this.description = 'Select one or multiple search results.';
|
this.description = 'Select one or multiple search results.';
|
||||||
this.addSkipButton = skipButton;
|
this.addSkipButton = selectModalOptions.skipButton;
|
||||||
|
|
||||||
this.busy = false;
|
this.busy = false;
|
||||||
|
|
||||||
this.sendCallback = false;
|
this.sendCallback = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
setSubmitCallback(submitCallback: (res: MediaTypeModel[]) => void): void {
|
setSubmitCallback(submitCallback: (res: SelectModalData) => void): void {
|
||||||
this.submitCallback = submitCallback;
|
this.submitCallback = submitCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,7 +52,7 @@ export class MediaDbSearchResultModal extends SelectModal<MediaTypeModel> {
|
||||||
if (!this.busy) {
|
if (!this.busy) {
|
||||||
this.busy = true;
|
this.busy = true;
|
||||||
this.submitButton.setButtonText('Creating entry...');
|
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)});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,6 +62,7 @@ export class MediaDbSearchResultModal extends SelectModal<MediaTypeModel> {
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose() {
|
onClose() {
|
||||||
|
console.log('close');
|
||||||
this.closeCallback();
|
this.closeCallback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,15 @@ export abstract class SelectModal<T> extends Modal {
|
||||||
skipButton?: ButtonComponent;
|
skipButton?: ButtonComponent;
|
||||||
submitButton?: ButtonComponent;
|
submitButton?: ButtonComponent;
|
||||||
|
|
||||||
|
elementWrapper?: HTMLDivElement;
|
||||||
|
|
||||||
elements: T[];
|
elements: T[];
|
||||||
selectModalElements: SelectModalElement<T>[];
|
selectModalElements: SelectModalElement<T>[];
|
||||||
|
|
||||||
|
|
||||||
protected constructor(app: App, elements: T[]) {
|
protected constructor(app: App, elements: T[], allowMultiSelect: boolean = true) {
|
||||||
super(app);
|
super(app);
|
||||||
this.allowMultiSelect = true;
|
this.allowMultiSelect = allowMultiSelect;
|
||||||
|
|
||||||
this.title = '';
|
this.title = '';
|
||||||
this.description = '';
|
this.description = '';
|
||||||
|
|
@ -27,18 +29,28 @@ export abstract class SelectModal<T> extends Modal {
|
||||||
this.skipButton = undefined;
|
this.skipButton = undefined;
|
||||||
this.submitButton = undefined;
|
this.submitButton = undefined;
|
||||||
|
|
||||||
|
this.elementWrapper = undefined;
|
||||||
|
|
||||||
this.elements = elements;
|
this.elements = elements;
|
||||||
this.selectModalElements = [];
|
this.selectModalElements = [];
|
||||||
|
|
||||||
this.scope.register([], 'ArrowUp', () => {
|
this.scope.register([], 'ArrowUp', (evt) => {
|
||||||
this.highlightUp();
|
this.highlightUp();
|
||||||
|
evt.preventDefault();
|
||||||
});
|
});
|
||||||
this.scope.register([], 'ArrowDown', () => {
|
this.scope.register([], 'ArrowDown', (evt) => {
|
||||||
this.highlightDown();
|
this.highlightDown();
|
||||||
|
evt.preventDefault();
|
||||||
});
|
});
|
||||||
this.scope.register([], 'ArrowRight', () => {
|
this.scope.register([], 'ArrowRight', () => {
|
||||||
this.activateHighlighted();
|
this.activateHighlighted();
|
||||||
});
|
});
|
||||||
|
this.scope.register([], ' ', (evt) => {
|
||||||
|
if (this.elementWrapper && this.elementWrapper === document.activeElement) {
|
||||||
|
this.activateHighlighted();
|
||||||
|
evt.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
this.scope.register([], 'Enter', () => this.submit());
|
this.scope.register([], 'Enter', () => this.submit());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,26 +77,18 @@ export abstract class SelectModal<T> extends Modal {
|
||||||
}
|
}
|
||||||
|
|
||||||
async onOpen() {
|
async onOpen() {
|
||||||
const {contentEl} = this;
|
const {contentEl, titleEl} = this;
|
||||||
|
|
||||||
/*
|
titleEl.createEl('h2', {text: this.title});
|
||||||
contentEl.id = 'media-db-plugin-modal'
|
contentEl.addClass('media-db-plugin-select-modal');
|
||||||
|
|
||||||
contentEl.on('keydown', '#' + contentEl.id, (ev, delegateTarget) => {
|
|
||||||
console.log(ev.key);
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
|
|
||||||
contentEl.createEl('h2', {text: this.title});
|
|
||||||
contentEl.createEl('p', {text: this.description});
|
contentEl.createEl('p', {text: this.description});
|
||||||
|
|
||||||
contentEl.addClass('media-db-plugin-select-modal');
|
this.elementWrapper = contentEl.createDiv({cls: 'media-db-plugin-select-wrapper'});
|
||||||
|
this.elementWrapper.tabIndex = 0;
|
||||||
const elementWrapper = contentEl.createDiv({cls: 'media-db-plugin-select-wrapper'});
|
|
||||||
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (const element of this.elements) {
|
for (const element of this.elements) {
|
||||||
const selectModalElement = new SelectModalElement(element, elementWrapper, i, this, false);
|
const selectModalElement = new SelectModalElement(element, this.elementWrapper, i, this, false);
|
||||||
|
|
||||||
this.selectModalElements.push(selectModalElement);
|
this.selectModalElements.push(selectModalElement);
|
||||||
|
|
||||||
|
|
@ -96,11 +100,27 @@ export abstract class SelectModal<T> extends Modal {
|
||||||
this.selectModalElements.first()?.element.scrollIntoView();
|
this.selectModalElements.first()?.element.scrollIntoView();
|
||||||
|
|
||||||
const bottomSettingRow = new Setting(contentEl);
|
const bottomSettingRow = new Setting(contentEl);
|
||||||
bottomSettingRow.addButton(btn => this.cancelButton = btn.setButtonText('Cancel').onClick(() => this.close()));
|
bottomSettingRow.addButton(btn => {
|
||||||
|
btn.setButtonText('Cancel');
|
||||||
|
btn.onClick(() => this.close());
|
||||||
|
btn.buttonEl.addClass('media-db-plugin-button');
|
||||||
|
this.cancelButton = btn;
|
||||||
|
});
|
||||||
if (this.addSkipButton) {
|
if (this.addSkipButton) {
|
||||||
bottomSettingRow.addButton(btn => this.skipButton = btn.setButtonText('Skip').onClick(() => this.skip()));
|
bottomSettingRow.addButton(btn => {
|
||||||
|
btn.setButtonText('Skip');
|
||||||
|
btn.onClick(() => this.skip());
|
||||||
|
btn.buttonEl.addClass('media-db-plugin-button');
|
||||||
|
this.skipButton = btn;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
bottomSettingRow.addButton(btn => this.submitButton = btn.setButtonText('Ok').setCta().onClick(() => this.submit()));
|
bottomSettingRow.addButton(btn => {
|
||||||
|
btn.setButtonText('Ok');
|
||||||
|
btn.setCta();
|
||||||
|
btn.onClick(() => this.submit());
|
||||||
|
btn.buttonEl.addClass('media-db-plugin-button');
|
||||||
|
this.submitButton = btn;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
activateHighlighted() {
|
activateHighlighted() {
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,7 @@
|
||||||
import PropertyMappingModelComponent from './PropertyMappingModelComponent.svelte';
|
import PropertyMappingModelComponent from './PropertyMappingModelComponent.svelte';
|
||||||
|
|
||||||
export let models: PropertyMappingModel[] = [];
|
export let models: PropertyMappingModel[] = [];
|
||||||
|
|
||||||
export let save: (model: PropertyMappingModel) => void;
|
export let save: (model: PropertyMappingModel) => void;
|
||||||
|
|
||||||
// TODO: validate all the mappings before saving.
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
||||||
418
src/utils/ModalHelper.ts
Normal file
418
src/utils/ModalHelper.ts
Normal file
|
|
@ -0,0 +1,418 @@
|
||||||
|
import {MediaDbAdvancedSearchModal} from '../modals/MediaDbAdvancedSearchModal';
|
||||||
|
import {MediaDbIdSearchModal} from '../modals/MediaDbIdSearchModal';
|
||||||
|
import {MediaTypeModel} from '../models/MediaTypeModel';
|
||||||
|
import {MediaDbSearchResultModal} from '../modals/MediaDbSearchResultModal';
|
||||||
|
import {Notice} from 'obsidian';
|
||||||
|
import MediaDbPlugin from '../main';
|
||||||
|
import {MediaDbPreviewModal} from 'src/modals/MediaDbPreviewModal';
|
||||||
|
import {CreateNoteOptions} from './Utils';
|
||||||
|
|
||||||
|
|
||||||
|
export enum ModalResultCode {
|
||||||
|
SUCCESS,
|
||||||
|
SKIP,
|
||||||
|
CLOSE,
|
||||||
|
ERROR,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object containing the data {@link ModalHelper.createAdvancedSearchModal} returns.
|
||||||
|
* On {@link ModalResultCode.SUCCESS} this contains {@link AdvancedSearchModalData}.
|
||||||
|
* On {@link ModalResultCode.ERROR} this contains a reference to that error.
|
||||||
|
*/
|
||||||
|
export interface AdvancedSearchModalResult {
|
||||||
|
code: ModalResultCode.SUCCESS | ModalResultCode.CLOSE | ModalResultCode.ERROR,
|
||||||
|
data?: AdvancedSearchModalData,
|
||||||
|
error?: Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object containing the data {@link ModalHelper.createIdSearchModal} returns.
|
||||||
|
* On {@link ModalResultCode.SUCCESS} this contains {@link IdSearchModalData}.
|
||||||
|
* On {@link ModalResultCode.ERROR} this contains a reference to that error.
|
||||||
|
*/
|
||||||
|
export interface IdSearchModalResult {
|
||||||
|
code: ModalResultCode.SUCCESS | ModalResultCode.CLOSE | ModalResultCode.ERROR,
|
||||||
|
data?: IdSearchModalData,
|
||||||
|
error?: Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object containing the data {@link ModalHelper.createSelectModal} returns.
|
||||||
|
* On {@link ModalResultCode.SUCCESS} this contains {@link SelectModalData}.
|
||||||
|
* On {@link ModalResultCode.ERROR} this contains a reference to that error.
|
||||||
|
*/
|
||||||
|
export interface SelectModalResult {
|
||||||
|
code: ModalResultCode.SUCCESS | ModalResultCode.CLOSE | ModalResultCode.SKIP | ModalResultCode.ERROR,
|
||||||
|
data?: SelectModalData,
|
||||||
|
error?: Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object containing the data {@link ModalHelper.createPreviewModal} returns.
|
||||||
|
* On {@link ModalResultCode.SUCCESS} this contains {@link PreviewModalData}.
|
||||||
|
* On {@link ModalResultCode.ERROR} this contains a reference to that error.
|
||||||
|
*/
|
||||||
|
export interface PreviewModalResult {
|
||||||
|
code: ModalResultCode.SUCCESS | ModalResultCode.CLOSE | ModalResultCode.ERROR,
|
||||||
|
data?: PreviewModalData,
|
||||||
|
error?: Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The data the advanced search modal returns.
|
||||||
|
* - query: the query string
|
||||||
|
* - apis: the selected APIs
|
||||||
|
*/
|
||||||
|
export interface AdvancedSearchModalData {
|
||||||
|
query: string,
|
||||||
|
apis: string[],
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The data the id search modal returns.
|
||||||
|
* - query: the query string
|
||||||
|
* - apis: the selected APIs
|
||||||
|
*/
|
||||||
|
export interface IdSearchModalData {
|
||||||
|
query: string,
|
||||||
|
api: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The data the select modal returns.
|
||||||
|
* - selected: the selected items
|
||||||
|
*/
|
||||||
|
export interface SelectModalData {
|
||||||
|
selected: MediaTypeModel[],
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The data the preview modal returns.
|
||||||
|
* - confirmed: whether the selected element has been confirmed
|
||||||
|
*/
|
||||||
|
export interface PreviewModalData {
|
||||||
|
confirmed: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for the advanced search modal.
|
||||||
|
* - modalTitle: the title of the modal
|
||||||
|
* - preselectedAPIs: a list of preselected APIs
|
||||||
|
* - prefilledSearchString: prefilled query
|
||||||
|
*/
|
||||||
|
export interface AdvancedSearchModalOptions {
|
||||||
|
modalTitle?: string,
|
||||||
|
preselectedAPIs?: string[],
|
||||||
|
prefilledSearchString?: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for the id search modal.
|
||||||
|
* - modalTitle: the title of the modal
|
||||||
|
* - preselectedAPIs: a list of preselected APIs
|
||||||
|
* - prefilledSearchString: prefilled query
|
||||||
|
*/
|
||||||
|
export interface IdSearchModalOptions {
|
||||||
|
modalTitle?: string,
|
||||||
|
preselectedAPI?: string,
|
||||||
|
prefilledSearchString?: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for the select modal.
|
||||||
|
* - modalTitle: the title of the modal
|
||||||
|
* - elements: the elements the user can select from
|
||||||
|
* - multiSelect: whether to allow multiselect
|
||||||
|
* - skipButton: whether to add a skip button to the modal
|
||||||
|
*/
|
||||||
|
export interface SelectModalOptions {
|
||||||
|
modalTitle?: string,
|
||||||
|
elements?: MediaTypeModel[],
|
||||||
|
multiSelect?: boolean,
|
||||||
|
skipButton?: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for the preview modal.
|
||||||
|
* - modalTitle: the title of the modal
|
||||||
|
* - elements: the elements to preview
|
||||||
|
*/
|
||||||
|
export interface PreviewModalOptions {
|
||||||
|
modalTitle?: string,
|
||||||
|
elements?: MediaTypeModel[],
|
||||||
|
createNoteOptions?: CreateNoteOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ADVANCED_SEARCH_MODAL_DEFAULT_OPTIONS: AdvancedSearchModalOptions = {
|
||||||
|
modalTitle: 'Media DB Advanced Search',
|
||||||
|
preselectedAPIs: [],
|
||||||
|
prefilledSearchString: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ID_SEARCH_MODAL_DEFAULT_OPTIONS: IdSearchModalOptions = {
|
||||||
|
modalTitle: 'Media DB Id Search',
|
||||||
|
preselectedAPI: '',
|
||||||
|
prefilledSearchString: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SELECT_MODAL_OPTIONS_DEFAULT: SelectModalOptions = {
|
||||||
|
modalTitle: 'Media DB Search Results',
|
||||||
|
elements: [],
|
||||||
|
multiSelect: true,
|
||||||
|
skipButton: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PREVIEW_MODAL_DEFAULT_OPTIONS: PreviewModalOptions = {
|
||||||
|
modalTitle: 'Media DB Preview',
|
||||||
|
elements: [],
|
||||||
|
createNoteOptions: {attachTemplate: true},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class providing multiple usefull functions for dealing with the plugins modals.
|
||||||
|
*/
|
||||||
|
export class ModalHelper {
|
||||||
|
plugin: MediaDbPlugin;
|
||||||
|
|
||||||
|
|
||||||
|
constructor(plugin: MediaDbPlugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an {@link MediaDbAdvancedSearchModal}, then sets callbacks and awaits them,
|
||||||
|
* returning either the user input once submitted or nothing once closed.
|
||||||
|
* The modal needs ot be manually closed by calling `close()` on the modal reference.
|
||||||
|
*
|
||||||
|
* @param advancedSearchModalOptions the options for the modal, see {@link ADVANCED_SEARCH_MODAL_DEFAULT_OPTIONS}
|
||||||
|
* @returns the user input or nothing and a reference to the modal.
|
||||||
|
*/
|
||||||
|
async createAdvancedSearchModal(advancedSearchModalOptions: AdvancedSearchModalOptions): Promise<{ advancedSearchModalResult: AdvancedSearchModalResult, advancedSearchModal: MediaDbAdvancedSearchModal }> {
|
||||||
|
const modal = new MediaDbAdvancedSearchModal(this.plugin, advancedSearchModalOptions);
|
||||||
|
const res: AdvancedSearchModalResult = await new Promise((resolve, reject) => {
|
||||||
|
modal.setSubmitCallback(res => resolve({code: ModalResultCode.SUCCESS, data: res}));
|
||||||
|
modal.setCloseCallback(err => {
|
||||||
|
if (err) {
|
||||||
|
resolve({code: ModalResultCode.ERROR, error: err});
|
||||||
|
}
|
||||||
|
resolve({code: ModalResultCode.CLOSE});
|
||||||
|
});
|
||||||
|
|
||||||
|
modal.open();
|
||||||
|
});
|
||||||
|
return {advancedSearchModalResult: res, advancedSearchModal: modal};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens an {@link MediaDbAdvancedSearchModal} and awaits its result,
|
||||||
|
* then executes the `submitCallback` returning the callbacks result and closing the modal.
|
||||||
|
*
|
||||||
|
* @param advancedSearchModalOptions the options for the modal, see {@link ADVANCED_SEARCH_MODAL_DEFAULT_OPTIONS}
|
||||||
|
* @param submitCallback the callback that gets executed after the modal has been submitted, but after it has been closed
|
||||||
|
* @returns the user input or nothing and a reference to the modal.
|
||||||
|
*/
|
||||||
|
async openAdvancedSearchModal(advancedSearchModalOptions: AdvancedSearchModalOptions, submitCallback: (advancedSearchModalData: AdvancedSearchModalData) => Promise<MediaTypeModel[]>): Promise<MediaTypeModel[]> {
|
||||||
|
const {advancedSearchModalResult, advancedSearchModal} = await this.createAdvancedSearchModal(advancedSearchModalOptions);
|
||||||
|
|
||||||
|
if (advancedSearchModalResult.code === ModalResultCode.ERROR) {
|
||||||
|
// there was an error in the modal itself
|
||||||
|
console.warn(advancedSearchModalResult.error);
|
||||||
|
new Notice(advancedSearchModalResult.error.toString());
|
||||||
|
advancedSearchModal.close();
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (advancedSearchModalResult.code === ModalResultCode.CLOSE) {
|
||||||
|
// modal is already being closed
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let callbackRes: MediaTypeModel[];
|
||||||
|
callbackRes = await submitCallback(advancedSearchModalResult.data);
|
||||||
|
advancedSearchModal.close();
|
||||||
|
return callbackRes;
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
new Notice(e.toString());
|
||||||
|
advancedSearchModal.close();
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an {@link MediaDbIdSearchModal}, then sets callbacks and awaits them,
|
||||||
|
* returning either the user input once submitted or nothing once closed.
|
||||||
|
* The modal needs ot be manually closed by calling `close()` on the modal reference.
|
||||||
|
*
|
||||||
|
* @param idSearchModalOptions the options for the modal, see {@link ID_SEARCH_MODAL_DEFAULT_OPTIONS}
|
||||||
|
* @returns the user input or nothing and a reference to the modal.
|
||||||
|
*/
|
||||||
|
async createIdSearchModal(idSearchModalOptions: IdSearchModalOptions): Promise<{ idSearchModalResult: IdSearchModalResult, idSearchModal: MediaDbIdSearchModal }> {
|
||||||
|
const modal = new MediaDbIdSearchModal(this.plugin, idSearchModalOptions);
|
||||||
|
const res: IdSearchModalResult = await new Promise((resolve, reject) => {
|
||||||
|
modal.setSubmitCallback(res => resolve({code: ModalResultCode.SUCCESS, data: res}));
|
||||||
|
modal.setCloseCallback(err => {
|
||||||
|
if (err) {
|
||||||
|
resolve({code: ModalResultCode.ERROR, error: err});
|
||||||
|
}
|
||||||
|
resolve({code: ModalResultCode.CLOSE});
|
||||||
|
});
|
||||||
|
|
||||||
|
modal.open();
|
||||||
|
});
|
||||||
|
return {idSearchModalResult: res, idSearchModal: modal};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens an {@link MediaDbIdSearchModal} and awaits its result,
|
||||||
|
* then executes the `submitCallback` returning the callbacks result and closing the modal.
|
||||||
|
*
|
||||||
|
* @param idSearchModalOptions the options for the modal, see {@link ID_SEARCH_MODAL_DEFAULT_OPTIONS}
|
||||||
|
* @param submitCallback the callback that gets executed after the modal has been submitted, but after it has been closed
|
||||||
|
* @returns the user input or nothing and a reference to the modal.
|
||||||
|
*/
|
||||||
|
async openIdSearchModal(idSearchModalOptions: IdSearchModalOptions, submitCallback: (idSearchModalData: IdSearchModalData) => Promise<MediaTypeModel>): Promise<MediaTypeModel> {
|
||||||
|
const {idSearchModalResult, idSearchModal} = await this.createIdSearchModal(idSearchModalOptions);
|
||||||
|
|
||||||
|
if (idSearchModalResult.code === ModalResultCode.ERROR) {
|
||||||
|
// there was an error in the modal itself
|
||||||
|
console.warn(idSearchModalResult.error);
|
||||||
|
new Notice(idSearchModalResult.error.toString());
|
||||||
|
idSearchModal.close();
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idSearchModalResult.code === ModalResultCode.CLOSE) {
|
||||||
|
// modal is already being closed
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let callbackRes: MediaTypeModel;
|
||||||
|
callbackRes = await submitCallback(idSearchModalResult.data);
|
||||||
|
idSearchModal.close();
|
||||||
|
return callbackRes;
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
new Notice(e.toString());
|
||||||
|
idSearchModal.close();
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an {@link MediaDbSearchResultModal}, then sets callbacks and awaits them,
|
||||||
|
* returning either the user input once submitted or nothing once closed.
|
||||||
|
* The modal needs ot be manually closed by calling `close()` on the modal reference.
|
||||||
|
*
|
||||||
|
* @param selectModalOptions the options for the modal, see {@link SELECT_MODAL_OPTIONS_DEFAULT}
|
||||||
|
* @returns the user input or nothing and a reference to the modal.
|
||||||
|
*/
|
||||||
|
async createSelectModal(selectModalOptions: SelectModalOptions): Promise<{ selectModalResult: SelectModalResult, selectModal: MediaDbSearchResultModal }> {
|
||||||
|
const modal = new MediaDbSearchResultModal(this.plugin, selectModalOptions);
|
||||||
|
const res: SelectModalResult = await new Promise((resolve, reject) => {
|
||||||
|
modal.setSubmitCallback(res => resolve({code: ModalResultCode.SUCCESS, data: res}));
|
||||||
|
modal.setSkipCallback(() => resolve({code: ModalResultCode.SKIP}));
|
||||||
|
modal.setCloseCallback(err => {
|
||||||
|
if (err) {
|
||||||
|
resolve({code: ModalResultCode.ERROR, error: err});
|
||||||
|
}
|
||||||
|
resolve({code: ModalResultCode.CLOSE});
|
||||||
|
});
|
||||||
|
|
||||||
|
modal.open();
|
||||||
|
});
|
||||||
|
return {selectModalResult: res, selectModal: modal};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens an {@link MediaDbSearchResultModal} and awaits its result,
|
||||||
|
* then executes the `submitCallback` returning the callbacks result and closing the modal.
|
||||||
|
*
|
||||||
|
* @param selectModalOptions the options for the modal, see {@link SELECT_MODAL_OPTIONS_DEFAULT}
|
||||||
|
* @param submitCallback the callback that gets executed after the modal has been submitted, but after it has been closed
|
||||||
|
* @returns the user input or nothing and a reference to the modal.
|
||||||
|
*/
|
||||||
|
async openSelectModal(selectModalOptions: SelectModalOptions, submitCallback: (selectModalData: SelectModalData) => Promise<MediaTypeModel[]>): Promise<MediaTypeModel[]> {
|
||||||
|
const {selectModalResult, selectModal} = await this.createSelectModal(selectModalOptions);
|
||||||
|
|
||||||
|
if (selectModalResult.code === ModalResultCode.ERROR) {
|
||||||
|
// there was an error in the modal itself
|
||||||
|
console.warn(selectModalResult.error);
|
||||||
|
new Notice(selectModalResult.error.toString());
|
||||||
|
selectModal.close();
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectModalResult.code === ModalResultCode.CLOSE) {
|
||||||
|
// modal is already being closed
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectModalResult.code === ModalResultCode.SKIP) {
|
||||||
|
// selection was skipped
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let callbackRes: MediaTypeModel[];
|
||||||
|
callbackRes = await submitCallback(selectModalResult.data);
|
||||||
|
selectModal.close();
|
||||||
|
return callbackRes;
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
new Notice(e.toString());
|
||||||
|
selectModal.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async createPreviewModal(previewModalOptions: PreviewModalOptions): Promise<{ previewModalResult: PreviewModalResult, previewModal: MediaDbPreviewModal }> {
|
||||||
|
//todo: handle attachFile for existing files
|
||||||
|
const modal = new MediaDbPreviewModal(this.plugin, previewModalOptions);
|
||||||
|
const res: PreviewModalResult = await new Promise((resolve, reject) => {
|
||||||
|
modal.setSubmitCallback(res => resolve({code: ModalResultCode.SUCCESS, data: res}));
|
||||||
|
modal.setCloseCallback(err => {
|
||||||
|
if (err) {
|
||||||
|
resolve({code: ModalResultCode.ERROR, error: err});
|
||||||
|
}
|
||||||
|
resolve({code: ModalResultCode.CLOSE});
|
||||||
|
});
|
||||||
|
|
||||||
|
modal.open();
|
||||||
|
});
|
||||||
|
return {previewModalResult: res, previewModal: modal};
|
||||||
|
}
|
||||||
|
|
||||||
|
async openPreviewModal(previewModalOptions: PreviewModalOptions, submitCallback: (previewModalData: PreviewModalData) => Promise<boolean>): Promise<boolean> {
|
||||||
|
const {previewModalResult, previewModal} = await this.createPreviewModal(previewModalOptions);
|
||||||
|
|
||||||
|
if (previewModalResult.code === ModalResultCode.ERROR) {
|
||||||
|
// there was an error in the modal itself
|
||||||
|
console.warn(previewModalResult.error);
|
||||||
|
new Notice(previewModalResult.error.toString());
|
||||||
|
previewModal.close();
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previewModalResult.code === ModalResultCode.CLOSE) {
|
||||||
|
// modal is already being closed
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let callbackRes: boolean;
|
||||||
|
callbackRes = await submitCallback(previewModalResult.data);
|
||||||
|
previewModal.close();
|
||||||
|
return callbackRes;
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
new Notice(e.toString());
|
||||||
|
previewModal.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import {MediaTypeModel} from '../models/MediaTypeModel';
|
import {MediaTypeModel} from '../models/MediaTypeModel';
|
||||||
|
import {TFile} from 'obsidian';
|
||||||
|
|
||||||
|
|
||||||
export const pluginName: string = 'obsidian-media-db-plugin';
|
export const pluginName: string = 'obsidian-media-db-plugin';
|
||||||
|
|
@ -159,12 +160,18 @@ export function dateTimeToString(dateTime: Date) {
|
||||||
return `${dateToString(dateTime)} ${timeToString(dateTime)}`;
|
return `${dateToString(dateTime)} ${timeToString(dateTime)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
export class UserCancelError extends Error {
|
export class UserCancelError extends Error {
|
||||||
constructor(message: string) {
|
constructor(message: string) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
export class UserSkipError extends Error {
|
export class UserSkipError extends Error {
|
||||||
constructor(message: string) {
|
constructor(message: string) {
|
||||||
super(message);
|
super(message);
|
||||||
|
|
@ -194,3 +201,14 @@ export class PropertyMappingNameConflictError extends Error {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* - attachTemplate: whether to attach the template (DEFAULT: false)
|
||||||
|
* - attachFie: a file to attach (DEFAULT: undefined)
|
||||||
|
* - openNote: whether to open the note after creation (DEFAULT: false)
|
||||||
|
*/
|
||||||
|
export interface CreateNoteOptions {
|
||||||
|
attachTemplate?: boolean,
|
||||||
|
attachFile?: TFile,
|
||||||
|
openNote?: boolean,
|
||||||
|
}
|
||||||
|
|
|
||||||
20
styles.css
20
styles.css
|
|
@ -23,13 +23,13 @@ small.media-db-plugin-list-text{
|
||||||
}
|
}
|
||||||
|
|
||||||
.media-db-plugin-select-modal {
|
.media-db-plugin-select-modal {
|
||||||
display: flex;
|
display: contents;
|
||||||
flex-direction: column;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.media-db-plugin-select-wrapper {
|
.media-db-plugin-select-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
flex: 1;
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,6 +52,16 @@ small.media-db-plugin-list-text{
|
||||||
background: var(--background-secondary-alt);
|
background: var(--background-secondary-alt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.media-db-plugin-preview-modal {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-db-plugin-preview-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.media-db-plugin-spacer {
|
.media-db-plugin-spacer {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
@ -105,4 +115,8 @@ small.media-db-plugin-list-text{
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.media-db-plugin-button:focus {
|
||||||
|
/*outline: 1px solid white;*/
|
||||||
|
}
|
||||||
|
|
||||||
/* endregion */
|
/* endregion */
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue