diff --git a/src/views/manager.js b/src/views/manager.js index c588776..a57fd95 100644 --- a/src/views/manager.js +++ b/src/views/manager.js @@ -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`
loading
`; + 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) => { diff --git a/src/views/search/controller.js b/src/views/search/controller.js new file mode 100644 index 0000000..423aa0b --- /dev/null +++ b/src/views/search/controller.js @@ -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; + } + }); + } +} \ No newline at end of file diff --git a/src/views/search/index.js b/src/views/search/index.js new file mode 100644 index 0000000..ba55e2a --- /dev/null +++ b/src/views/search/index.js @@ -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`
+

An attempt at a viable alternative to Goodreads

+ +
+ ${controller.results.map(result => { + return html`
+
+ ${result.covers.map(cover => { + return html``; + })} +

${result.title}

+ ${result.authors.map(author => { + return html`

${author}

`; + })} +
+
`; + })} +
+
`, + ]; +} \ No newline at end of file