Add super basic and ugly search with openlibrary
This commit is contained in:
parent
34ae314e74
commit
21f9c41eaa
|
@ -1,17 +1,25 @@
|
|||
import html from 'choo/html';
|
||||
|
||||
import { homeView } from './home';
|
||||
import { searchView } from './search';
|
||||
|
||||
export const viewManager = (state, emit) => {
|
||||
// In viewManager all we are doing is checking the app's state
|
||||
// and passing the state and emit to the relevant view.
|
||||
let htmlContent = html`<div>loading</div>`;
|
||||
if (state.query.hasOwnProperty('search')) {
|
||||
state.currentView = 'search'; // Override view if there's a search query
|
||||
}
|
||||
switch (state.currentView) {
|
||||
case 'home':
|
||||
default: {
|
||||
htmlContent = homeView(state, emit);
|
||||
break;
|
||||
}
|
||||
case 'search': {
|
||||
htmlContent = searchView(state, emit);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a wrapper for view content that includes global header/footer
|
||||
|
@ -29,6 +37,11 @@ export const viewManager = (state, emit) => {
|
|||
<label for="navMenu" class="burger pseudo button">≡</label>
|
||||
|
||||
<div class="menu">
|
||||
<form method="GET" style="display:inline-block;">
|
||||
<label>
|
||||
<input type="text" name="search" placeholder="Search">
|
||||
</label>
|
||||
</form>
|
||||
<a href="https://gitlab.com/Alamantus/book-tracker" class="pseudo button">Repo</a>
|
||||
<a href="https://gitter.im/book-tracker/general" class="pseudo button">Chat</a>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
import { ViewController } from '../controller';
|
||||
|
||||
export class SearchController extends ViewController {
|
||||
constructor(state) {
|
||||
// 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', {
|
||||
done: false,
|
||||
results: [],
|
||||
});
|
||||
|
||||
// If using controller methods in an input's onchange or onclick instance,
|
||||
// either bind the class's 'this' instance to the method first...
|
||||
// or use `onclick=${() => controller.submit()}` to maintain the 'this' of the class instead.
|
||||
}
|
||||
|
||||
get results() {
|
||||
return [...this.state.results];
|
||||
}
|
||||
|
||||
search(term) {
|
||||
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;
|
||||
this.state.done = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
import html from 'choo/html';
|
||||
|
||||
// We'll see if code splitting is worth it in the end or if we should combine everything into `src/index.scss`
|
||||
import { SearchController } from './controller'; // The controller for this view, where processing should happen.
|
||||
|
||||
// This is the view function that is exported and used in the view manager.
|
||||
export const searchView = (state, emit) => {
|
||||
const controller = new SearchController(state);
|
||||
|
||||
if (!controller.state.done) {
|
||||
controller.search(state.query.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.
|
||||
return [
|
||||
html`<section>
|
||||
<h2 class="subtitle">An attempt at a viable alternative to Goodreads</h2>
|
||||
|
||||
<article>
|
||||
${controller.results.map(result => {
|
||||
return html`<div class="card">
|
||||
<header>
|
||||
${result.covers.map(cover => {
|
||||
return html`<img src=${cover} />`;
|
||||
})}
|
||||
<h1 class="title">${result.title}</h1>
|
||||
${result.authors.map(author => {
|
||||
return html`<h2 class="subtitle">${author}</h2>`;
|
||||
})}
|
||||
</header>
|
||||
</div>`;
|
||||
})}
|
||||
</article>
|
||||
</section>`,
|
||||
];
|
||||
}
|
Loading…
Reference in New Issue