Update search frontend to call API

This commit is contained in:
Robbie Antenesse 2019-09-09 17:09:25 -06:00
parent 951d07e11f
commit e5dbcc9ab8
2 changed files with 23 additions and 183 deletions

View File

@ -5,6 +5,7 @@ export class SearchController extends ViewController {
// Super passes state, view name, and default state to ViewController,
// which stores state in this.appState and the view controller's state to this.state
super(state, 'search', {
lastSearch: undefined,
done: false,
results: [],
});
@ -15,192 +16,28 @@ export class SearchController extends ViewController {
}
get results() {
return [...this.state.results];
return [];
}
get hasQuery() {
return this.appState.query.hasOwnProperty('for') && this.appState.query.for.trim() !== '';
}
/**
* Query a MediaWiki api.php instance with the given options
*/
mediaWikiQuery(endpoint, options) {
/**
* Create a uniquely-named callback that will process the JSONP results
*/
var createCallback = function (k) {
var i = 1;
var callbackName;
do {
callbackName = 'searchCallback' + i;
i = i + 1;
} while (window[callbackName])
window[callbackName] = k;
return callbackName;
}
search() {
if (this.hasQuery) {
this.state.done = false;
this.state.lastSearch = this.appState.query.for;
/**
* Flatten an object into a URL query string.
* For example: { foo: 'bar', baz: 42 } becomes 'foo=bar&baz=42'
*/
var queryStr = function (options) {
var query = [];
for (var i in options) {
if (options.hasOwnProperty(i)) {
query.push(encodeURIComponent(i) + '=' + encodeURIComponent(options[i]));
}
}
return query.join('&');
}
const searchTerm = this.appState.query.for.trim();
/**
* Build a function that can be applied to a callback. The callback processes
* the JSON results of the API call.
*/
return function (k) {
options.format = 'json';
options.callback = createCallback(k);
var script = document.createElement('script');
script.id = 'searchResults';
script.src = endpoint + '?' + queryStr(options);
var head = document.getElementsByTagName('head')[0];
head.appendChild(script);
};
}
search(term) {
const query = this.mediaWikiQuery('https://en.wikibooks.org/w/api.php', {
action: 'query',
list: 'search',
// list: 'categorymembers',
// cmtitle: 'Category:Subject:Books by subject/all books',
srsearch: term,
srprop: '',
// pageids: 20308,
// prop: 'categories|pageprops',
});
query(response => {
console.log(response);
const searchScript = document.getElementById('searchResults');
searchScript.parentNode.removeChild(searchScript);
for (let property in window) {
if (property.includes('searchCallback')) {
delete window[property];
}
}
const bookResults = [];
const pageids = response.query.search.map(item => item.pageid);
const propsQuery = this.mediaWikiQuery('https://en.wikibooks.org/w/api.php', {
action: 'query',
pageids: pageids.join('|'),
prop: 'categories|pageprops',
});
propsQuery(propsResponse => {
console.log(propsResponse);
for (let pageid in propsResponse.query.pages) {
if (propsResponse.query.pages[pageid].hasOwnProperty('categories')) {
}
}
});
// this.state.results = results;
this.state.done = true;
});
// return fetch(`https://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=${term}&format=json&callback=searchCallback`, {
// method: 'GET',
// mode: 'no-cors',
// headers: new Headers(
// {
// "Accept": "text/plain"
// }
// ),
// // body: JSON.stringify({
// // action: 'opensearch',
// // search: term,
// // format: 'json',
// // }),
// })
// .then(res => res.text())
// .then(response => {
// console.log(response);
// // if (response.hasOwnProperty('docs')) {
// // // Format the response into usable objects
// // const docs = response.docs.map(doc => {
// // return {
// // title: doc.title_suggest.trim(),
// // authors: doc.hasOwnProperty('author_name') ? doc.author_name.map(name => name.trim()) : [],
// // cover: doc.hasOwnProperty('cover_i') ? `//covers.openlibrary.org/b/id/${doc.cover_i}-S.jpg` : false,
// // };
// // });
// // // Filter out duplicate items with the same title and author
// // const results = docs.filter((doc, index, allDocs) => {
// // return typeof allDocs.find((filterResult, filterIndex) => {
// // return index !== filterIndex && filterResult.title === doc.title
// // && JSON.stringify(filterResult.authors) === JSON.stringify(doc.authors);
// // }) === 'undefined';
// // }).map(result => {
// // // Find any duplicates in case they have different cover data
// // const duplicates = docs.filter(doc => {
// // return doc.title.toLowerCase() === result.title.toLowerCase() && JSON.stringify(doc.authors) === JSON.stringify(result.authors);
// // });
// // result.covers = [];
// // duplicates.forEach(duplicate => {
// // if (duplicate.cover !== false) {
// // result.covers.push(duplicate.cover);
// // }
// // });
// // return result;
// // });
// // this.state.results = results;
// this.state.done = true;
// // }
// });
}
searchOpenLibrary(term) {
this.state.done = false;
return fetch('http://openlibrary.org/search.json?q=' + encodeURIComponent(term))
.then(res => res.json())
.then(response => {
if (response.hasOwnProperty('docs')) {
// Format the response into usable objects
const docs = response.docs.map(doc => {
return {
title: doc.title_suggest.trim(),
authors: doc.hasOwnProperty('author_name') ? doc.author_name.map(name => name.trim()) : [],
cover: doc.hasOwnProperty('cover_i') ? `//covers.openlibrary.org/b/id/${doc.cover_i}-S.jpg` : false,
};
});
// Filter out duplicate items with the same title and author
const results = docs.filter((doc, index, allDocs) => {
return typeof allDocs.find((filterResult, filterIndex) => {
return index !== filterIndex && filterResult.title === doc.title
&& JSON.stringify(filterResult.authors) === JSON.stringify(doc.authors);
}) === 'undefined';
}).map(result => {
// Find any duplicates in case they have different cover data
const duplicates = docs.filter(doc => {
return doc.title.toLowerCase() === result.title.toLowerCase() && JSON.stringify(doc.authors) === JSON.stringify(result.authors);
});
result.covers = [];
duplicates.forEach(duplicate => {
if (duplicate.cover !== false) {
result.covers.push(duplicate.cover);
}
});
return result;
});
this.state.results = results;
return fetch(`/api/search?for=${searchTerm}&lang=${this.appState.language}`)
.then(response => response.json())
.then(responseJSON => {
console.log(responseJSON);
this.state.results = responseJSON;
this.state.done = true;
}
});
});
}
return Promise.resolve();
}
}

View File

@ -8,11 +8,12 @@ export const searchView = (state, emit) => {
const i18n = new I18n(state);
const controller = new SearchController(state);
// if (!controller.state.done && controller.hasQuery) {
// controller.searchOpenLibrary(state.query.for).then(() => {
// emit('render');
// });
// }
if (controller.state.lastSearch !== state.query.for) {
console.log('searching!');
controller.search().then(() => {
emit('render');
});
}
// Returning an array in a view allows non-shared parent HTML elements.
// This one doesn't have the problem right now, but it's good to remember.
@ -21,6 +22,8 @@ export const searchView = (state, emit) => {
<h2 class="subtitle">${i18n.__('search.header')}</h2>
<article>
${controller.state.done ? 'Done searching' : 'Loading...'}
${controller.results.map(result => {
return html`<div class="card">
<header>