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 = [];
|
this.apis = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
async query(query: string, types: string[] = []): Promise<MediaTypeModel[]> {
|
async query(query: string, apisToQuery: any): Promise<MediaTypeModel[]> {
|
||||||
console.log('MDB | api manager queried');
|
console.log('MDB | api manager queried');
|
||||||
|
|
||||||
let res: MediaTypeModel[] = [];
|
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) {
|
for (const api of this.apis) {
|
||||||
if (types.length === 0 || api.hasTypeOverlap(types)) {
|
if (types.length === 0 || api.hasTypeOverlap(types)) {
|
||||||
const apiRes = await api.searchByTitle(query);
|
const apiRes = await api.searchByTitle(query);
|
||||||
|
|
@ -20,6 +29,7 @@ export class APIManager {
|
||||||
res = res.concat(apiRes);
|
res = res.concat(apiRes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ export class OMDbAPI extends APIModel {
|
||||||
|
|
||||||
for (const value of data.Search) {
|
for (const value of data.Search) {
|
||||||
if (value.Type === 'movie') {
|
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 model;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ export class TestAPI extends APIModel {
|
||||||
console.log(`MDB | api "${this.apiName}" queried`);
|
console.log(`MDB | api "${this.apiName}" queried`);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
new MovieModel ({title: 'test_1', type: this.types[0], dataSource: this.apiName}) ,
|
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_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 {DEFAULT_SETTINGS, MediaDbPluginSettings, MediaDbSettingTab} from './settings/Settings';
|
||||||
import {MediaDbSearchModal} from './modals/MediaDbSearchModal';
|
|
||||||
import {APIManager} from './api/APIManager';
|
import {APIManager} from './api/APIManager';
|
||||||
import {TestAPI} from './api/apis/TestAPI';
|
import {TestAPI} from './api/apis/TestAPI';
|
||||||
import {MediaTypeModel} from './models/MediaTypeModel';
|
import {MediaTypeModel} from './models/MediaTypeModel';
|
||||||
import {getFileName} from './utils/Utils';
|
import {getFileName} 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';
|
||||||
|
|
||||||
export default class MediaDbPlugin extends Plugin {
|
export default class MediaDbPlugin extends Plugin {
|
||||||
settings: MediaDbPluginSettings;
|
settings: MediaDbPluginSettings;
|
||||||
|
|
@ -15,7 +16,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
await this.loadSettings();
|
await this.loadSettings();
|
||||||
|
|
||||||
// add icon to the left ribbon
|
// 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(),
|
this.createMediaDbNote(),
|
||||||
);
|
);
|
||||||
ribbonIconEl.addClass('obsidian-media-db-plugin-ribbon-class');
|
ribbonIconEl.addClass('obsidian-media-db-plugin-ribbon-class');
|
||||||
|
|
@ -38,35 +39,43 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
async createMediaDbNote(): Promise<void> {
|
async createMediaDbNote(): Promise<void> {
|
||||||
let data: MediaTypeModel = await this.openMediaDbSearchModal();
|
try {
|
||||||
console.log('MDB | Create new note or something...');
|
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 fileName = getFileName(data);
|
||||||
const filePath = `${this.settings.folder.replace(/\/$/, '')}/${fileName}.md`;
|
const filePath = `${this.settings.folder.replace(/\/$/, '')}/${fileName}.md`;
|
||||||
const targetFile = await this.app.vault.create(filePath, fileContent);
|
const targetFile = await this.app.vault.create(filePath, fileContent);
|
||||||
|
|
||||||
// open file
|
// open file
|
||||||
const activeLeaf = this.app.workspace.getLeaf();
|
const activeLeaf = this.app.workspace.getLeaf();
|
||||||
if (!activeLeaf) {
|
if (!activeLeaf) {
|
||||||
console.warn('No active leaf');
|
console.warn('No active leaf');
|
||||||
return;
|
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) => {
|
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);
|
if (err) return reject(err);
|
||||||
resolve(result);
|
new MediaDbSearchResultModal(this.app, results, (err2, res) => {
|
||||||
|
if (err) return reject(err2);
|
||||||
|
resolve(res);
|
||||||
|
}).open();
|
||||||
}).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 {MediaTypeModel} from './MediaTypeModel';
|
||||||
|
import {stringifyYaml} from 'obsidian';
|
||||||
|
|
||||||
|
|
||||||
export class MovieModel extends MediaTypeModel {
|
export class MovieModel extends MediaTypeModel {
|
||||||
type: string;
|
type: string;
|
||||||
|
|
@ -29,7 +31,7 @@ export class MovieModel extends MediaTypeModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
toMetaData(): string {
|
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;
|
return ((value % size) + size) % size;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sleep (ms: number) {
|
export function sleep(ms: number) {
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
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