Cleanup and light changes

This commit is contained in:
mProjectsCode 2022-10-09 15:40:37 +02:00
parent 3a4b69717d
commit 918c5b6fd7
8 changed files with 190 additions and 113 deletions

View file

@ -2,7 +2,7 @@ import {MarkdownView, Notice, parseYaml, Plugin, stringifyYaml, TFile, TFolder}
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} from './utils/Utils'; import {CreateNoteOptions, dateTimeToString, markdownTable, replaceIllegalFileNameCharactersInString} from './utils/Utils';
import {OMDbAPI} from './api/apis/OMDbAPI'; import {OMDbAPI} from './api/apis/OMDbAPI';
import {MALAPI} from './api/apis/MALAPI'; import {MALAPI} from './api/apis/MALAPI';
import {WikipediaAPI} from './api/apis/WikipediaAPI'; import {WikipediaAPI} from './api/apis/WikipediaAPI';
@ -179,9 +179,9 @@ export default class MediaDbPlugin extends Plugin {
return; return;
} }
proceed = await this.modalHelper.openPreviewModal(selectResults, async () => { proceed = await this.modalHelper.openPreviewModal({elements: selectResults}, async (previewModalData) => {
return true; return previewModalData.confirmed;
}) });
} }
await this.createMediaDbNotes(selectResults); await this.createMediaDbNotes(selectResults);
@ -194,14 +194,14 @@ export default class MediaDbPlugin extends Plugin {
while (!proceed) { while (!proceed) {
idSearchResult = await this.modalHelper.openIdSearchModal({}, async (idSearchModalData) => { idSearchResult = await this.modalHelper.openIdSearchModal({}, async (idSearchModalData) => {
return await this.apiManager.queryDetailedInfoById(idSearchModalData.query, idSearchModalData.api); return await this.apiManager.queryDetailedInfoById(idSearchModalData.query, idSearchModalData.api);
}) });
if (!idSearchResult) { if (!idSearchResult) {
return; return;
} }
proceed = await this.modalHelper.openPreviewModal([idSearchResult], async () => { proceed = await this.modalHelper.openPreviewModal({elements: [idSearchResult]}, async (previewModalData) => {
return true; return previewModalData.confirmed;
}) });
} }
await this.createMediaDbNoteFromModel(idSearchResult, {attachTemplate: true, openNote: true}); await this.createMediaDbNoteFromModel(idSearchResult, {attachTemplate: true, openNote: true});
@ -226,11 +226,11 @@ export default class MediaDbPlugin extends Plugin {
return detailModels; return detailModels;
} }
async createMediaDbNoteFromModel(mediaTypeModel: MediaTypeModel, options: { attachTemplate?: boolean, attachFile?: TFile, openNote?: boolean }): 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, {attachTemplate: options.attachTemplate, attachFile: options.attachFile}); let fileContent = await this.generateMediaDbNoteContents(mediaTypeModel, options);
await this.createNote(this.mediaTypeManager.getFileName(mediaTypeModel), fileContent, options.openNote); await this.createNote(this.mediaTypeManager.getFileName(mediaTypeModel), fileContent, options.openNote);
} catch (e) { } catch (e) {
@ -239,15 +239,13 @@ export default class MediaDbPlugin extends Plugin {
} }
} }
async generateMediaDbNoteContents(mediaTypeModel: MediaTypeModel, options: {attachTemplate?: boolean, 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, options.attachFile)); ({fileMetadata, fileContent} = await this.attachFile(fileMetadata, fileContent, options.attachFile));
({ ({fileMetadata, fileContent} = await this.attachTemplate(fileMetadata, fileContent, template));
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; fileContent = `---\n${this.settings.useCustomYamlStringifier ? YAMLConverter.toYaml(fileMetadata) : stringifyYaml(fileMetadata)}---\n` + fileContent;
return fileContent; return fileContent;

View file

@ -1,26 +1,34 @@
import { MarkdownRenderer, Modal, Setting, TFile } from "obsidian"; import {ButtonComponent, MarkdownRenderer, Modal, Setting} from 'obsidian';
import MediaDbPlugin from "src/main"; import MediaDbPlugin from 'src/main';
import { MediaTypeModel } from "src/models/MediaTypeModel"; 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 { export class MediaDbPreviewModal extends Modal {
selectedSearchResults: MediaTypeModel[];
options: { attachTemplate: boolean, attachFile: TFile};
submitCallback: (res: boolean) => void;
closeCallback: (err?: Error) => void;
plugin: MediaDbPlugin; plugin: MediaDbPlugin;
searchBtn: any;
cancelButton: any;
submitButton: any;
busy: any;
constructor(plugin: MediaDbPlugin, mediaTypeModel: MediaTypeModel[], options: any) { 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); super(plugin.app);
this.plugin = plugin; this.plugin = plugin;
this.selectedSearchResults = mediaTypeModel; this.title = previewModalOptions.modalTitle;
this.options = options; this.elements = previewModalOptions.elements;
this.createNoteOptions = previewModalOptions.createNoteOptions;
} }
setSubmitCallback(submitCallback: (res: boolean) => void): void { setSubmitCallback(submitCallback: (previewModalData: PreviewModalData) => void): void {
this.submitCallback = submitCallback; this.submitCallback = submitCallback;
} }
@ -29,16 +37,24 @@ export class MediaDbPreviewModal extends Modal {
} }
async preview(): Promise<void> { async preview(): Promise<void> {
let { contentEl } = this; let {contentEl} = this;
for (let result of this.selectedSearchResults) { contentEl.addClass('media-db-plugin-preview-modal');
let fileContent = await this.plugin.generateMediaDbNoteContents(result, { attachTemplate: this.options.attachTemplate, attachFile: this.options.attachFile });
this.contentEl.createEl("h3", {text: result.englishTitle}); contentEl.createEl('h2', {text: this.title});
const fileDiv = this.contentEl.createDiv();
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`; fileContent = `\n${fileContent}\n`;
MarkdownRenderer.renderMarkdown(fileContent, fileDiv, null, null); MarkdownRenderer.renderMarkdown(fileContent, fileDiv, null, null);
} }
contentEl.createDiv({ cls: 'media-db-plugin-spacer' }); contentEl.createDiv({cls: 'media-db-plugin-spacer'});
const bottomSettingRow = new Setting(contentEl); const bottomSettingRow = new Setting(contentEl);
bottomSettingRow.addButton(btn => { bottomSettingRow.addButton(btn => {
@ -50,21 +66,13 @@ export class MediaDbPreviewModal extends Modal {
bottomSettingRow.addButton(btn => { bottomSettingRow.addButton(btn => {
btn.setButtonText('Ok'); btn.setButtonText('Ok');
btn.setCta(); btn.setCta();
btn.onClick(() => this.submitCallback(true)); btn.onClick(() => this.submitCallback({confirmed: true}));
btn.buttonEl.addClass('media-db-plugin-button'); btn.buttonEl.addClass('media-db-plugin-button');
this.submitButton = btn; this.submitButton = btn;
}) });
}
submit() {
if (!this.busy) {
this.busy = true;
this.submitButton.setButtonText('Creating entry...');
this.submitCallback(true);
}
} }
onOpen(): void { onOpen(): void {
this.preview() this.preview();
} }
} }

View file

@ -79,11 +79,11 @@ export abstract class SelectModal<T> extends Modal {
async onOpen() { async onOpen() {
const {contentEl} = this; const {contentEl} = this;
contentEl.addClass('media-db-plugin-select-modal');
contentEl.createEl('h2', {text: this.title}); 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 = contentEl.createDiv({cls: 'media-db-plugin-select-wrapper'});
this.elementWrapper.tabIndex = 0; this.elementWrapper.tabIndex = 0;

View file

@ -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>

View file

@ -4,7 +4,8 @@ import {MediaTypeModel} from '../models/MediaTypeModel';
import {MediaDbSearchResultModal} from '../modals/MediaDbSearchResultModal'; import {MediaDbSearchResultModal} from '../modals/MediaDbSearchResultModal';
import {Notice} from 'obsidian'; import {Notice} from 'obsidian';
import MediaDbPlugin from '../main'; import MediaDbPlugin from '../main';
import { MediaDbPreviewModal } from 'src/modals/MediaDbPreviewModal'; import {MediaDbPreviewModal} from 'src/modals/MediaDbPreviewModal';
import {CreateNoteOptions} from './Utils';
export enum ModalResultCode { export enum ModalResultCode {
@ -47,10 +48,21 @@ export interface SelectModalResult {
error?: Error, 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. * The data the advanced search modal returns.
* query: the query string * - query: the query string
* apis: the selected APIs * - apis: the selected APIs
*/ */
export interface AdvancedSearchModalData { export interface AdvancedSearchModalData {
query: string, query: string,
@ -59,8 +71,8 @@ export interface AdvancedSearchModalData {
/** /**
* The data the id search modal returns. * The data the id search modal returns.
* query: the query string * - query: the query string
* apis: the selected APIs * - apis: the selected APIs
*/ */
export interface IdSearchModalData { export interface IdSearchModalData {
query: string, query: string,
@ -69,17 +81,25 @@ export interface IdSearchModalData {
/** /**
* The data the select modal returns. * The data the select modal returns.
* selected: the selected items * - selected: the selected items
*/ */
export interface SelectModalData { export interface SelectModalData {
selected: MediaTypeModel[], 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. * Options for the advanced search modal.
* modalTitle: the title of the modal * - modalTitle: the title of the modal
* preselectedAPIs: a list of preselected APIs * - preselectedAPIs: a list of preselected APIs
* prefilledSearchString: prefilled query * - prefilledSearchString: prefilled query
*/ */
export interface AdvancedSearchModalOptions { export interface AdvancedSearchModalOptions {
modalTitle?: string, modalTitle?: string,
@ -89,9 +109,9 @@ export interface AdvancedSearchModalOptions {
/** /**
* Options for the id search modal. * Options for the id search modal.
* modalTitle: the title of the modal * - modalTitle: the title of the modal
* preselectedAPIs: a list of preselected APIs * - preselectedAPIs: a list of preselected APIs
* prefilledSearchString: prefilled query * - prefilledSearchString: prefilled query
*/ */
export interface IdSearchModalOptions { export interface IdSearchModalOptions {
modalTitle?: string, modalTitle?: string,
@ -101,10 +121,10 @@ export interface IdSearchModalOptions {
/** /**
* Options for the select modal. * Options for the select modal.
* modalTitle: the title of the modal * - modalTitle: the title of the modal
* elements: the elements the user can select from * - elements: the elements the user can select from
* multiSelect: whether to allow multiselect * - multiSelect: whether to allow multiselect
* skipButton: whether to add a skip button to the modal * - skipButton: whether to add a skip button to the modal
*/ */
export interface SelectModalOptions { export interface SelectModalOptions {
modalTitle?: string, modalTitle?: string,
@ -113,6 +133,17 @@ export interface SelectModalOptions {
skipButton?: 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 = { export const ADVANCED_SEARCH_MODAL_DEFAULT_OPTIONS: AdvancedSearchModalOptions = {
modalTitle: 'Media DB Advanced Search', modalTitle: 'Media DB Advanced Search',
preselectedAPIs: [], preselectedAPIs: [],
@ -132,6 +163,12 @@ export const SELECT_MODAL_OPTIONS_DEFAULT: SelectModalOptions = {
skipButton: false, 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. * A class providing multiple usefull functions for dealing with the plugins modals.
*/ */
@ -332,33 +369,42 @@ export class ModalHelper {
} }
} }
async createPreviewModal(mediaTypeModel: MediaTypeModel[]): Promise<{ result: boolean, previewModal: MediaDbPreviewModal }> { async createPreviewModal(previewModalOptions: PreviewModalOptions): Promise<{ previewModalResult: PreviewModalResult, previewModal: MediaDbPreviewModal }> {
//todo: handle attachFile for existing files //todo: handle attachFile for existing files
const modal = new MediaDbPreviewModal(this.plugin, mediaTypeModel, { attachTemplate: true, attachFile: false }); const modal = new MediaDbPreviewModal(this.plugin, previewModalOptions);
const booleanResult: boolean = await new Promise((resolve, reject) => { const res: PreviewModalResult = await new Promise((resolve, reject) => {
modal.setSubmitCallback(res => resolve(res)); modal.setSubmitCallback(res => resolve({code: ModalResultCode.SUCCESS, data: res}));
modal.setCloseCallback(err => { modal.setCloseCallback(err => {
if (err) { if (err) {
reject(err); resolve({code: ModalResultCode.ERROR, error: err});
} }
resolve(undefined); resolve({code: ModalResultCode.CLOSE});
}); });
modal.open(); modal.open();
}); });
return { result: booleanResult, previewModal: modal }; return {previewModalResult: res, previewModal: modal};
} }
async openPreviewModal(mediaModels: MediaTypeModel[], submitCallback: (result: boolean) => Promise<boolean>): Promise<boolean> { async openPreviewModal(previewModalOptions: PreviewModalOptions, submitCallback: (previewModalData: PreviewModalData) => Promise<boolean>): Promise<boolean> {
const { result, previewModal } = await this.createPreviewModal(mediaModels); const {previewModalResult, previewModal} = await this.createPreviewModal(previewModalOptions);
if (!result) {
if (previewModalResult.code === ModalResultCode.ERROR) {
// there was an error in the modal itself
console.warn(previewModalResult.error);
new Notice(previewModalResult.error.toString());
previewModal.close(); previewModal.close();
return; return undefined;
}
if (previewModalResult.code === ModalResultCode.CLOSE) {
// modal is already being closed
return undefined;
} }
try { try {
let callbackRes: boolean; let callbackRes: boolean;
callbackRes = await submitCallback(result); callbackRes = await submitCallback(previewModalResult.data);
previewModal.close(); previewModal.close();
return callbackRes; return callbackRes;
} catch (e) { } catch (e) {

View file

@ -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,
}

View file

@ -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: flex;
flex-direction: column;
}
.media-db-plugin-preview-wrapper {
flex: 1;
overflow-y: auto;
}
.media-db-plugin-spacer { .media-db-plugin-spacer {
margin-bottom: 10px; margin-bottom: 10px;
} }