New Search Modal
This commit is contained in:
parent
cc2962220e
commit
6dc813a14c
9 changed files with 203 additions and 28 deletions
|
|
@ -8,11 +8,20 @@ export class APIManager {
|
|||
this.apis = [];
|
||||
}
|
||||
|
||||
async query(query: string, types: string[] = []): Promise<MediaTypeModel[]> {
|
||||
async query(query: string, apisToQuery: any): Promise<MediaTypeModel[]> {
|
||||
console.log('MDB | api manager queried');
|
||||
|
||||
let res: MediaTypeModel[] = [];
|
||||
|
||||
for (const api of this.apis) {
|
||||
if (Object.keys(apisToQuery).contains(api.apiName) && apisToQuery[api.apiName]) {
|
||||
const apiRes = await api.searchByTitle(query);
|
||||
// console.log(apiRes);
|
||||
res = res.concat(apiRes);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
for (const api of this.apis) {
|
||||
if (types.length === 0 || api.hasTypeOverlap(types)) {
|
||||
const apiRes = await api.searchByTitle(query);
|
||||
|
|
@ -20,6 +29,7 @@ export class APIManager {
|
|||
res = res.concat(apiRes);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export class OMDbAPI extends APIModel {
|
|||
|
||||
for (const value of data.Search) {
|
||||
if (value.Type === 'movie') {
|
||||
ret.push(new MovieModel({title: value.Title, id: value.imdbID, dataSource: this.apiName, type: 'movie'} as MovieModel))
|
||||
ret.push(new MovieModel({title: value.Title, id: value.imdbID, dataSource: this.apiName, type: 'movie'} as MovieModel));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -61,6 +61,6 @@ export class OMDbAPI extends APIModel {
|
|||
return model;
|
||||
}
|
||||
|
||||
return ;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ export class TestAPI extends APIModel {
|
|||
console.log(`MDB | api "${this.apiName}" queried`);
|
||||
|
||||
return [
|
||||
new MovieModel ({title: 'test_1', type: this.types[0], dataSource: this.apiName}) ,
|
||||
new MovieModel ({title: 'test_2', type: this.types[0], dataSource: this.apiName, producer: 'Max Musterman'}),
|
||||
new MovieModel({title: 'test_1', type: this.types[0], dataSource: this.apiName}),
|
||||
new MovieModel({title: 'test_2', type: this.types[0], dataSource: this.apiName, producer: 'Max Musterman'}),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
51
src/main.ts
51
src/main.ts
|
|
@ -1,11 +1,12 @@
|
|||
import {Plugin} from 'obsidian';
|
||||
import {Notice, Plugin} from 'obsidian';
|
||||
import {DEFAULT_SETTINGS, MediaDbPluginSettings, MediaDbSettingTab} from './settings/Settings';
|
||||
import {MediaDbSearchModal} from './modals/MediaDbSearchModal';
|
||||
import {APIManager} from './api/APIManager';
|
||||
import {TestAPI} from './api/apis/TestAPI';
|
||||
import {MediaTypeModel} from './models/MediaTypeModel';
|
||||
import {getFileName} from './utils/Utils';
|
||||
import {OMDbAPI} from './api/apis/OMDbAPI';
|
||||
import {MediaDbAdvancedSearchModal} from './modals/MediaDbAdvancedSearchModal';
|
||||
import {MediaDbSearchResultModal} from './modals/MediaDbSearchResultModal';
|
||||
|
||||
export default class MediaDbPlugin extends Plugin {
|
||||
settings: MediaDbPluginSettings;
|
||||
|
|
@ -15,7 +16,7 @@ export default class MediaDbPlugin extends Plugin {
|
|||
await this.loadSettings();
|
||||
|
||||
// add icon to the left ribbon
|
||||
const ribbonIconEl = this.addRibbonIcon('book', 'Add new Media DB entry', (evt: MouseEvent) =>
|
||||
const ribbonIconEl = this.addRibbonIcon('database', 'Add new Media DB entry', (evt: MouseEvent) =>
|
||||
this.createMediaDbNote(),
|
||||
);
|
||||
ribbonIconEl.addClass('obsidian-media-db-plugin-ribbon-class');
|
||||
|
|
@ -38,35 +39,43 @@ export default class MediaDbPlugin extends Plugin {
|
|||
}
|
||||
|
||||
async createMediaDbNote(): Promise<void> {
|
||||
let data: MediaTypeModel = await this.openMediaDbSearchModal();
|
||||
console.log('MDB | Create new note or something...');
|
||||
try {
|
||||
let data: MediaTypeModel = await this.openMediaDbSearchModal();
|
||||
console.log('MDB | Create new note or something...');
|
||||
|
||||
data = await this.apiManager.queryDetailedInfo(data);
|
||||
data = await this.apiManager.queryDetailedInfo(data);
|
||||
|
||||
console.log(data);
|
||||
console.log(data);
|
||||
|
||||
data.toMetaData()
|
||||
data.toMetaData();
|
||||
|
||||
const fileContent = `---\n${data.toMetaData()}\n---\n`;
|
||||
const fileContent = `---\n${data.toMetaData()}\n---\n`;
|
||||
|
||||
const fileName = getFileName(data);
|
||||
const filePath = `${this.settings.folder.replace(/\/$/, '')}/${fileName}.md`;
|
||||
const targetFile = await this.app.vault.create(filePath, fileContent);
|
||||
const fileName = getFileName(data);
|
||||
const filePath = `${this.settings.folder.replace(/\/$/, '')}/${fileName}.md`;
|
||||
const targetFile = await this.app.vault.create(filePath, fileContent);
|
||||
|
||||
// open file
|
||||
const activeLeaf = this.app.workspace.getLeaf();
|
||||
if (!activeLeaf) {
|
||||
console.warn('No active leaf');
|
||||
return;
|
||||
// open file
|
||||
const activeLeaf = this.app.workspace.getLeaf();
|
||||
if (!activeLeaf) {
|
||||
console.warn('No active leaf');
|
||||
return;
|
||||
}
|
||||
await activeLeaf.openFile(targetFile, {state: {mode: 'source'}});
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
new Notice(e.toString());
|
||||
}
|
||||
await activeLeaf.openFile(targetFile, { state: { mode: 'source' } });
|
||||
}
|
||||
|
||||
async openMediaDbSearchModal(): Promise<any> {
|
||||
async openMediaDbSearchModal(): Promise<MediaTypeModel> {
|
||||
return new Promise(((resolve, reject) => {
|
||||
new MediaDbSearchModal(this.app, this.apiManager, (err, result) => {
|
||||
new MediaDbAdvancedSearchModal(this.app, this.apiManager, (err, results) => {
|
||||
if (err) return reject(err);
|
||||
resolve(result);
|
||||
new MediaDbSearchResultModal(this.app, results, (err2, res) => {
|
||||
if (err) return reject(err2);
|
||||
resolve(res);
|
||||
}).open();
|
||||
}).open();
|
||||
}));
|
||||
}
|
||||
|
|
|
|||
110
src/modals/MediaDbAdvancedSearchModal.ts
Normal file
110
src/modals/MediaDbAdvancedSearchModal.ts
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
import {App, ButtonComponent, Component, Modal, Setting, TextComponent, ToggleComponent} from 'obsidian';
|
||||
import {MediaTypeModel} from '../models/MediaTypeModel';
|
||||
import {APIManager} from '../api/APIManager';
|
||||
|
||||
export class MediaDbAdvancedSearchModal extends Modal {
|
||||
query: string;
|
||||
isBusy: boolean;
|
||||
apiManager: APIManager;
|
||||
searchBtn: ButtonComponent;
|
||||
selectedApis: any;
|
||||
onSubmit: (err: Error, result?: MediaTypeModel[]) => void;
|
||||
|
||||
constructor(app: App, apiManager: APIManager, onSubmit?: (err: Error, result?: MediaTypeModel[]) => void) {
|
||||
super(app);
|
||||
this.apiManager = apiManager;
|
||||
this.onSubmit = onSubmit;
|
||||
this.selectedApis = [];
|
||||
for (const api of this.apiManager.apis) {
|
||||
this.selectedApis[api.apiName] = true;
|
||||
}
|
||||
}
|
||||
|
||||
submitCallback(event: KeyboardEvent) {
|
||||
if (event.key === 'Enter') {
|
||||
this.search();
|
||||
}
|
||||
}
|
||||
|
||||
async search(): Promise<MediaTypeModel[]> {
|
||||
|
||||
console.log(this.selectedApis);
|
||||
|
||||
if (!this.query) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isBusy) {
|
||||
try {
|
||||
this.isBusy = true;
|
||||
this.searchBtn.setDisabled(false);
|
||||
this.searchBtn.setButtonText('Searching...');
|
||||
|
||||
console.log('MDB | query started with ' + this.query);
|
||||
|
||||
const res = await this.apiManager.query(this.query, this.selectedApis);
|
||||
|
||||
// console.log(res)
|
||||
|
||||
this.onSubmit(null, res);
|
||||
} catch (e) {
|
||||
this.onSubmit(e);
|
||||
} finally {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
const {contentEl} = this;
|
||||
|
||||
contentEl.createEl('h2', {text: 'Search media db'});
|
||||
|
||||
const placeholder = 'Search by title';
|
||||
const searchComponent = new TextComponent(contentEl);
|
||||
searchComponent.inputEl.style.width = '100%';
|
||||
searchComponent.setPlaceholder(placeholder);
|
||||
searchComponent.onChange(value => (this.query = value));
|
||||
searchComponent.inputEl.addEventListener('keydown', this.submitCallback.bind(this));
|
||||
|
||||
contentEl.appendChild(searchComponent.inputEl);
|
||||
searchComponent.inputEl.focus();
|
||||
|
||||
contentEl.createEl('h3', {text: 'APIs to search'});
|
||||
|
||||
const apiToggleComponents: Component[] = [];
|
||||
for (const api of this.apiManager.apis) {
|
||||
const apiToggleListElementWrapper = contentEl.createEl('div', {cls: 'media-db-plugin-list-wrapper'});
|
||||
|
||||
apiToggleListElementWrapper.createEl('span', {text: api.apiName, cls: 'media-db-plugin-list-text'});
|
||||
const apiToggleComponentWrapper = apiToggleListElementWrapper.createEl('div', {cls: 'media-db-plugin-list-toggle'});
|
||||
|
||||
const apiToggleComponent = new ToggleComponent(apiToggleComponentWrapper);
|
||||
apiToggleComponent.setTooltip(api.apiName);
|
||||
apiToggleComponent.setValue(this.selectedApis[api.apiName]);
|
||||
apiToggleComponent.onChange((value) => {
|
||||
this.selectedApis[api.apiName] = value;
|
||||
});
|
||||
apiToggleComponentWrapper.appendChild(apiToggleComponent.toggleEl);
|
||||
}
|
||||
|
||||
|
||||
new Setting(contentEl)
|
||||
.addButton(btn => btn.setButtonText('Cancel').onClick(() => this.close()))
|
||||
.addButton(btn => {
|
||||
return (this.searchBtn = btn
|
||||
.setButtonText('Ok')
|
||||
.setCta()
|
||||
.onClick(() => {
|
||||
this.search();
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
onClose() {
|
||||
const {contentEl} = this;
|
||||
contentEl.empty();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
31
src/modals/MediaDbSearchResultModal.ts
Normal file
31
src/modals/MediaDbSearchResultModal.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import {App, SuggestModal} from 'obsidian';
|
||||
import {MediaTypeModel} from '../models/MediaTypeModel';
|
||||
|
||||
export class MediaDbSearchResultModal extends SuggestModal<MediaTypeModel> {
|
||||
suggestion: MediaTypeModel[];
|
||||
onChoose: (error: Error, result?: MediaTypeModel) => void;
|
||||
|
||||
constructor(app: App, suggestion: MediaTypeModel[], onChoose: (error: Error, result?: MediaTypeModel) => void) {
|
||||
super(app);
|
||||
this.suggestion = suggestion;
|
||||
this.onChoose = onChoose;
|
||||
}
|
||||
|
||||
getSuggestions(query: string): MediaTypeModel[] {
|
||||
return this.suggestion.filter(item => {
|
||||
const searchQuery = query.toLowerCase();
|
||||
return item.title.toLowerCase().includes(searchQuery);
|
||||
});
|
||||
}
|
||||
|
||||
// Renders each suggestion item.
|
||||
renderSuggestion(item: MediaTypeModel, el: HTMLElement) {
|
||||
el.createEl('div', {text: item.title});
|
||||
el.createEl('small', {text: `${item.type} from ${item.dataSource}`});
|
||||
}
|
||||
|
||||
// Perform action on the selected suggestion.
|
||||
onChooseSuggestion(item: MediaTypeModel, evt: MouseEvent | KeyboardEvent) {
|
||||
this.onChoose(null, item);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
import {MediaTypeModel} from './MediaTypeModel';
|
||||
import {stringifyYaml} from 'obsidian';
|
||||
|
||||
|
||||
export class MovieModel extends MediaTypeModel {
|
||||
type: string;
|
||||
|
|
@ -29,7 +31,7 @@ export class MovieModel extends MediaTypeModel {
|
|||
}
|
||||
|
||||
toMetaData(): string {
|
||||
return JSON.stringify(this);
|
||||
return stringifyYaml(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ export function wrapAround(value: number, size: number): number {
|
|||
return ((value % size) + size) % size;
|
||||
}
|
||||
|
||||
export function sleep (ms: number) {
|
||||
export function sleep(ms: number) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
|
|
|
|||
13
styles.css
13
styles.css
|
|
@ -0,0 +1,13 @@
|
|||
.media-db-plugin-list-wrapper {
|
||||
display: flex;
|
||||
align-content: center;
|
||||
/* margin: 5px; */
|
||||
}
|
||||
|
||||
.media-db-plugin-list-toggle {
|
||||
|
||||
}
|
||||
|
||||
.media-db-plugin-list-text {
|
||||
flex: 1;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue