Update backend format for search; Delay cover image load

This commit is contained in:
Robbie Antenesse 2020-08-24 15:16:14 -06:00
parent 63c7d3676d
commit 8283c1987a
5 changed files with 121 additions and 69 deletions

View File

@ -6,7 +6,8 @@ import { modal } from '../partials/modal';
export const resultDetails = (searchController, result, emit = () => {}) => { export const resultDetails = (searchController, result, emit = () => {}) => {
const { __ } = searchController.i18n; const { __ } = searchController.i18n;
const modalId = `result_${result.uri}`; const source = result.sources[0];
const modalId = `result_${source.uri}`;
const hasReviews = typeof result.averageRating !== 'undefined' && typeof result.numberOfReviews !== 'undefined'; const hasReviews = typeof result.averageRating !== 'undefined' && typeof result.numberOfReviews !== 'undefined';
@ -27,37 +28,41 @@ export const resultDetails = (searchController, result, emit = () => {}) => {
const tabNames = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen', 'twenty']; const tabNames = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen', 'twenty'];
const coversHTMLArray = result.covers.map((cover, index, allCovers) => {
return html`<div>
<img src=${cover.url} alt="${cover.sourceId.replace(':', ' ').toUpperCase()}, Published: ${cover.publishDate}">
${typeof allCovers[index - 1] === 'undefined'
? null
: html`<label class="button" for="cover_${allCovers[index - 1].sourceId}" style="margin-right:8px;">
${'<'}
</label>`
}
${typeof allCovers[index + 1] === 'undefined'
? null
: html`<label class="button" for="cover_${allCovers[index + 1].sourceId}">
${'>'}
</label>`
}
</div>`;
});
const modalContent = html`<article class="flex"> const modalContent = html`<article class="flex">
<div class="sixth-700" style="text-align:center;"> <div class="sixth-700" style="text-align:center;">
<h4>${__('search.covers')}</h4> <h4>${__('search.covers')}</h4>
${typeof result.covers === 'undefined' ${typeof result.covers === 'undefined'
? html`<span style="font-size:3em;"><i class="icon-loading animate-spin"></i></span>` ? html`<span style="font-size:3em;"><i class="icon-loading animate-spin"></i></span>`
: html`<div class="tabs ${typeof tabNames[result.covers.length - 1] !== 'undefined' ? tabNames[result.covers.length - 1] : null}"> : html`<div class="tabs ${typeof tabNames[result.covers.length - 1] !== 'undefined' ? tabNames[result.covers.length - 1] : tabNames[19]}">
${result.covers.map((cover, index) => { ${result.covers.map((cover, index) => {
return [ return [
html`<input id="cover_${cover.uri}" type="radio" name="${modalId}_covers" ${index === 0 ? 'checked' : null} />`, html`<input id="cover_${cover.sourceId}" type="radio" name="${modalId}_covers" ${index === 0 ? 'checked' : null} />`,
// html`<label class="small pseudo button toggle" for="cover_${cover.uri}">•</label>`, // html`<label class="small pseudo button toggle" for="cover_${cover.sourceId}">•</label>`,
]; ];
})} })}
<div class="row"> <div class="row" id="covers_${modalId}">${
${result.covers.map((cover, index, allCovers) => { searchController.openModal === modalId
return html`<div> ? coversHTMLArray
<img src=${cover.url} alt="${cover.uri.replace(':', ' ').toUpperCase()}, Published: ${cover.publishDate}"> : '' /* Leave the covers column empty until opened to prevent loading too many images */
${typeof allCovers[index - 1] === 'undefined' }</div>
? null
: html`<label class="button" for="cover_${allCovers[index - 1].uri}" style="margin-right:8px;">
${'<'}
</label>`
}
${typeof allCovers[index + 1] === 'undefined'
? null
: html`<label class="button" for="cover_${allCovers[index + 1].uri}">
${'>'}
</label>`
}
</div>`;
})}
</div>
</div>` </div>`
} }
</div> </div>
@ -72,7 +77,7 @@ export const resultDetails = (searchController, result, emit = () => {}) => {
<h4>Top Reviews</h4> <h4>Top Reviews</h4>
</div> </div>
<div> <div>
<a href="/book/${result.uri}" class="small button"> <a href="/book/${source.uri}" class="small button">
<span style="margin-right:8px;"><i class="icon-chat"></i></span> <span style="margin-right:8px;"><i class="icon-chat"></i></span>
<span>${result.numberOfReviews}</span> <span>${result.numberOfReviews}</span>
<span>${__('search.see_interaction_details')}</span> <span>${__('search.see_interaction_details')}</span>
@ -92,27 +97,29 @@ export const resultDetails = (searchController, result, emit = () => {}) => {
</p> </p>
${!searchController.showShelves ? null : html`<ul>${searchController.shelves.map(shelf => { ${!searchController.showShelves ? null : html`<ul>${searchController.shelves.map(shelf => {
return html`<li> return html`<li>
<button class="pseudo" onclick=${() => searchController.addToShelf({source: 'inventaire', uri: result.uri}, shelf.id)}> <button class="pseudo" onclick=${() => searchController.addToShelf({ id: result.id, ...source }, shelf.id)}>
${shelf.name} ${shelf.name}
</button> </button>
</li>`; </li>`;
})}</ul>`} })}</ul>`}
<p> <p>
<a class="small button" href=${result.link} target="_blank"> <a class="small button" href=${source.link} target="_blank">
${__('search.see_book_details')} ${__('search.see_book_details')}
</a> </a>
</p> </p>
</div> </div>
</article>`; </article>`;
const onShow = () => {
const coversColumn = document.getElementById(`covers_${modalId}`);
coversColumn.innerHTML = '';
coversHTMLArray.forEach(element => coversColumn.appendChild(element));
};
return modal(modalId, searchController, modalContent, { return modal(modalId, searchController, modalContent, {
styles: "width:90%;", styles: "width:90%;",
buttonHTML, // This should be replaced with buttonHTML containing the ratings and number of reviews etc. buttonHTML, // This should be replaced with buttonHTML containing the ratings and number of reviews etc.
headerText: result.name, headerText: result.name,
onShow: () => { onShow,
if (typeof result.covers === 'undefined') {
searchController.getCovers(result.uri).then(() => emit('render'));
}
},
}); });
} }

View File

@ -15,6 +15,7 @@ class Inventaire {
static handleQuickEntity(entityObject) { static handleQuickEntity(entityObject) {
return { return {
id: null,
name: ( name: (
typeof entityObject.label !== 'undefined' typeof entityObject.label !== 'undefined'
? entityObject.label ? entityObject.label
@ -25,22 +26,26 @@ class Inventaire {
? entityObject.description ? entityObject.description
: null : null
), ),
source: 'inventaire', sources: [
link: ( {
typeof entityObject.uri !== 'undefined' source: 'inventaire',
? `${Inventaire.url}/entity/${entityObject.uri}` uri: (
: null typeof entityObject.uri !== 'undefined'
), ? entityObject.uri
uri: ( : null
typeof entityObject.uri !== 'undefined' ),
? entityObject.uri link: (
: null typeof entityObject.uri !== 'undefined'
), ? `${Inventaire.url}/entity/${entityObject.uri}`
: null
),
},
],
covers: ( covers: (
typeof entityObject.image !== 'undefined' typeof entityObject.image !== 'undefined'
? entityObject.image.map(imageId => { ? entityObject.image.map(imageId => {
return { return {
uri: imageId.toString(), sourceId: imageId.toString(),
url: `${Inventaire.url}/img/entities/${imageId}`, url: `${Inventaire.url}/img/entities/${imageId}`,
} }
}) })
@ -72,17 +77,21 @@ class Inventaire {
: null : null
) )
), ),
source: 'inventaire', sources: [
link: ( {
typeof entityObject.uri !== 'undefined' source: 'inventaire',
? `${Inventaire.url}/entity/${entityObject.uri}` uri: (
: null typeof entityObject.uri !== 'undefined'
), ? entityObject.uri
uri: ( : null
typeof entityObject.uri !== 'undefined' ),
? entityObject.uri link: (
: null typeof entityObject.uri !== 'undefined'
), ? `${Inventaire.url}/entity/${entityObject.uri}`
: null
),
},
],
}; };
} }
@ -109,7 +118,7 @@ class Inventaire {
if (typeof bookData.entities !== 'undefined' && typeof bookData.entities[uri] !== 'undefined') { if (typeof bookData.entities !== 'undefined' && typeof bookData.entities[uri] !== 'undefined') {
bookData = Inventaire.handleEntity(bookData.entities[uri], this.lang); bookData = Inventaire.handleEntity(bookData.entities[uri], this.lang);
bookData['covers'] = await this.getCovers(bookData.uri); bookData['covers'] = await this.getCovers(bookData.sources[0].uri);
return bookData; return bookData;
} }
@ -126,7 +135,7 @@ class Inventaire {
} }
// Note: property `wdt:P629` is a given entity (uri)'s list of editions (ISBNs). // Note: property `wdt:P629` is a given entity (uri)'s list of editions (ISBNs).
const editionsRequest = fetch(`${Inventaire.url}/api/entities?action=reverse-claims&uri=${encodeURIComponent(uri)}&property=wdt:P629`) const editionsRequest = fetch(`${Inventaire.url}/api/entities?action=reverse-claims&value=${encodeURIComponent(uri)}&property=wdt:P629`)
editionsRequest.catch(exception => { editionsRequest.catch(exception => {
console.error(exception); console.error(exception);
return { return {
@ -180,7 +189,7 @@ class Inventaire {
}).map(key => { }).map(key => {
const entity = responseJSON.entities[key]; const entity = responseJSON.entities[key];
return { return {
uri: entity.uri, sourceId: entity.uri,
url: typeof entity.claims['invp:P2'] !== 'undefined' ? `${Inventaire.url}/img/entities/${entity.claims['invp:P2'][0]}` : null, url: typeof entity.claims['invp:P2'] !== 'undefined' ? `${Inventaire.url}/img/entities/${entity.claims['invp:P2'][0]}` : null,
publishDate: typeof entity.claims['wdt:P577'] !== 'undefined' ? entity.claims['wdt:P577'][0] : null, publishDate: typeof entity.claims['wdt:P577'] !== 'undefined' ? entity.claims['wdt:P577'][0] : null,
} }
@ -193,7 +202,10 @@ class Inventaire {
return a.publishDate < b.publishDate ? -1 : 1; return a.publishDate < b.publishDate ? -1 : 1;
}); });
return covers; return covers.map(cover => {
delete cover.publishDate;
return cover;
});
}); });
} }
} }

View File

@ -28,7 +28,7 @@ class BookReferenceController {
} }
// Get formatted book data from source // Get formatted book data from source
const bookData = dataClass.getBookData(sourceId); const bookData = await dataClass.getBookData(sourceId);
if (typeof bookData.uri !== 'undefined') { if (typeof bookData.uri !== 'undefined') {
// Check for references by exact name and author from source // Check for references by exact name and author from source

View File

@ -43,7 +43,14 @@ function searchInventaire(searchTerm, language) {
message: 'An error occurred when trying read the response from Inventaire as JSON.', message: 'An error occurred when trying read the response from Inventaire as JSON.',
} }
}); });
return json.then(responseJSON => responseJSON.results.map(work => Inventaire.handleEntity(work, language))); return json.then(responseJSON => {
return responseJSON.results.map(async work => {
const inventaire = new Inventaire(langauge);
const bookData = Inventaire.handleEntity(work, language);
bookData['covers'] = await inventaire.getCovers(bookData.sources[0].uri);
return bookData;
});
});
} }
} }

View File

@ -3,6 +3,7 @@ const { Op, fn, col } = require('sequelize');
const BooksController = require('../bookData'); const BooksController = require('../bookData');
const { quickSearchInventaire } = require('./Inventaire'); const { quickSearchInventaire } = require('./Inventaire');
const Inventaire = require('../bookData/Inventaire');
const defaultSearchOptions = { const defaultSearchOptions = {
searchBy: 'name', // A column name in the BookReference model, mainly 'name' or 'description' searchBy: 'name', // A column name in the BookReference model, mainly 'name' or 'description'
@ -57,6 +58,30 @@ class SearchController {
}; };
} }
static formatReferenceSources(reference) {
const referenceSources = Object.keys(reference.sources);
const reformattedSources = referenceSources.map(source => {
const uri = reference.sources[source];
let link;
switch (source) {
default:
case 'inventaire': {
link = `${Inventaire.url}/entity/${uri}`
break;
}
}
return {
source,
uri,
link,
}
});
reference.sources = reformattedSources;
return reference;
}
async search(searchTerm, options = defaultSearchOptions) { async search(searchTerm, options = defaultSearchOptions) {
const searchBy = options.searchBy.replace('title', 'name').replace('author', 'description'); const searchBy = options.searchBy.replace('title', 'name').replace('author', 'description');
const { source, language } = options; const { source, language } = options;
@ -77,22 +102,23 @@ class SearchController {
// Add any search results that match refs with the same URI and delete from results array // Add any search results that match refs with the same URI and delete from results array
const urisToCheck = searchResults.filter( const urisToCheck = searchResults.filter(
result => !bookReferences.some(ref => result.uri === ref.sources[source]) result => !bookReferences.some(ref => result.sources[0].uri === ref.sources[source])
).map(result => result.uri); ).map(result => result.sources[0].uri);
let extraReferences = []; let extraReferences = [];
if (urisToCheck.length > 0) { if (urisToCheck.length > 0) {
// Need to figure this out // Need to figure this out
extraReferences = await this.searchReferencesBySourceCodes(source, urisToCheck); extraReferences = await this.searchReferencesBySourceCodes(source, urisToCheck);
} }
searchResults = searchResults.filter( // Only show the rest of the search results
result => !extraReferences.some(
ref => result.sources[0].uri === ref.sources[source]
)
);
return [ return [
...bookReferences, ...bookReferences.map(match => SearchController.formatReferenceSources(match)),
...extraReferences, ...extraReferences.map(match => SearchController.formatReferenceSources(match)),
...searchResults.filter( // Only show the rest of the search results ...searchResults,
result => !extraReferences.some(
ref => result.uri === ref.sources[source]
)
),
]; ];
} }