mirror of
				https://gitlab.com/Alamantus/Readlebee.git
				synced 2025-11-03 17:57:03 +01:00 
			
		
		
		
	Update backend format for search; Delay cover image load
This commit is contained in:
		
							parent
							
								
									63c7d3676d
								
							
						
					
					
						commit
						8283c1987a
					
				
					 5 changed files with 121 additions and 69 deletions
				
			
		| 
						 | 
				
			
			@ -6,7 +6,8 @@ import { modal } from '../partials/modal';
 | 
			
		|||
 | 
			
		||||
export const resultDetails = (searchController, result, emit = () => {}) => {
 | 
			
		||||
  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';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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 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">
 | 
			
		||||
    <div class="sixth-700" style="text-align:center;">
 | 
			
		||||
      <h4>${__('search.covers')}</h4>
 | 
			
		||||
      ${typeof result.covers === 'undefined'
 | 
			
		||||
        ? 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) => {
 | 
			
		||||
            return [
 | 
			
		||||
              html`<input id="cover_${cover.uri}" type="radio" name="${modalId}_covers" ${index === 0 ? 'checked' : null} />`,
 | 
			
		||||
              // html`<label class="small pseudo button toggle" for="cover_${cover.uri}">•</label>`,
 | 
			
		||||
              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.sourceId}">•</label>`,
 | 
			
		||||
            ];
 | 
			
		||||
          })}
 | 
			
		||||
          <div class="row">
 | 
			
		||||
          ${result.covers.map((cover, index, allCovers) => {
 | 
			
		||||
            return html`<div>
 | 
			
		||||
              <img src=${cover.url} alt="${cover.uri.replace(':', ' ').toUpperCase()}, Published: ${cover.publishDate}">
 | 
			
		||||
              ${typeof allCovers[index - 1] === 'undefined'
 | 
			
		||||
                ? 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 class="row" id="covers_${modalId}">${
 | 
			
		||||
            searchController.openModal === modalId
 | 
			
		||||
            ? coversHTMLArray
 | 
			
		||||
            : '' /* Leave the covers column empty until opened to prevent loading too many images */
 | 
			
		||||
          }</div>
 | 
			
		||||
        </div>`
 | 
			
		||||
      }
 | 
			
		||||
    </div>
 | 
			
		||||
| 
						 | 
				
			
			@ -72,7 +77,7 @@ export const resultDetails = (searchController, result, emit = () => {}) => {
 | 
			
		|||
            <h4>Top Reviews</h4>
 | 
			
		||||
          </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>${result.numberOfReviews}</span>
 | 
			
		||||
              <span>${__('search.see_interaction_details')}</span>
 | 
			
		||||
| 
						 | 
				
			
			@ -92,27 +97,29 @@ export const resultDetails = (searchController, result, emit = () => {}) => {
 | 
			
		|||
      </p>
 | 
			
		||||
      ${!searchController.showShelves ? null : html`<ul>${searchController.shelves.map(shelf => {
 | 
			
		||||
        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}
 | 
			
		||||
          </button>
 | 
			
		||||
        </li>`;
 | 
			
		||||
      })}</ul>`}
 | 
			
		||||
      <p>
 | 
			
		||||
        <a class="small button" href=${result.link} target="_blank">
 | 
			
		||||
        <a class="small button" href=${source.link} target="_blank">
 | 
			
		||||
          ${__('search.see_book_details')}
 | 
			
		||||
        </a>
 | 
			
		||||
      </p>
 | 
			
		||||
    </div>
 | 
			
		||||
  </article>`;
 | 
			
		||||
  
 | 
			
		||||
  const onShow = () => {
 | 
			
		||||
    const coversColumn = document.getElementById(`covers_${modalId}`);
 | 
			
		||||
    coversColumn.innerHTML = '';
 | 
			
		||||
    coversHTMLArray.forEach(element => coversColumn.appendChild(element));
 | 
			
		||||
  };
 | 
			
		||||
  
 | 
			
		||||
  return modal(modalId, searchController, modalContent, {
 | 
			
		||||
    styles: "width:90%;",
 | 
			
		||||
    buttonHTML, // This should be replaced with buttonHTML containing the ratings and number of reviews etc.
 | 
			
		||||
    headerText: result.name,
 | 
			
		||||
    onShow: () => {
 | 
			
		||||
      if (typeof result.covers === 'undefined') {
 | 
			
		||||
        searchController.getCovers(result.uri).then(() => emit('render'));
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    onShow,
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -15,6 +15,7 @@ class Inventaire {
 | 
			
		|||
 | 
			
		||||
  static handleQuickEntity(entityObject) {
 | 
			
		||||
    return {
 | 
			
		||||
      id: null,
 | 
			
		||||
      name: (
 | 
			
		||||
        typeof entityObject.label !== 'undefined'
 | 
			
		||||
          ? entityObject.label
 | 
			
		||||
| 
						 | 
				
			
			@ -25,22 +26,26 @@ class Inventaire {
 | 
			
		|||
          ? entityObject.description
 | 
			
		||||
          : null
 | 
			
		||||
      ),
 | 
			
		||||
      source: 'inventaire',
 | 
			
		||||
      link: (
 | 
			
		||||
        typeof entityObject.uri !== 'undefined'
 | 
			
		||||
          ? `${Inventaire.url}/entity/${entityObject.uri}`
 | 
			
		||||
          : null
 | 
			
		||||
      ),
 | 
			
		||||
      uri: (
 | 
			
		||||
        typeof entityObject.uri !== 'undefined'
 | 
			
		||||
          ? entityObject.uri
 | 
			
		||||
          : null
 | 
			
		||||
      ),
 | 
			
		||||
      sources: [
 | 
			
		||||
        {
 | 
			
		||||
          source: 'inventaire',
 | 
			
		||||
          uri: (
 | 
			
		||||
            typeof entityObject.uri !== 'undefined'
 | 
			
		||||
              ? entityObject.uri
 | 
			
		||||
              : null
 | 
			
		||||
          ),
 | 
			
		||||
          link: (
 | 
			
		||||
            typeof entityObject.uri !== 'undefined'
 | 
			
		||||
              ? `${Inventaire.url}/entity/${entityObject.uri}`
 | 
			
		||||
              : null
 | 
			
		||||
          ),
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
      covers: (
 | 
			
		||||
        typeof entityObject.image !== 'undefined'
 | 
			
		||||
          ? entityObject.image.map(imageId => {
 | 
			
		||||
            return {
 | 
			
		||||
              uri: imageId.toString(),
 | 
			
		||||
              sourceId: imageId.toString(),
 | 
			
		||||
              url: `${Inventaire.url}/img/entities/${imageId}`,
 | 
			
		||||
            }
 | 
			
		||||
          })
 | 
			
		||||
| 
						 | 
				
			
			@ -72,17 +77,21 @@ class Inventaire {
 | 
			
		|||
              : null
 | 
			
		||||
          )
 | 
			
		||||
      ),
 | 
			
		||||
      source: 'inventaire',
 | 
			
		||||
      link: (
 | 
			
		||||
        typeof entityObject.uri !== 'undefined'
 | 
			
		||||
          ? `${Inventaire.url}/entity/${entityObject.uri}`
 | 
			
		||||
          : null
 | 
			
		||||
      ),
 | 
			
		||||
      uri: (
 | 
			
		||||
        typeof entityObject.uri !== 'undefined'
 | 
			
		||||
          ? entityObject.uri
 | 
			
		||||
          : null
 | 
			
		||||
      ),
 | 
			
		||||
      sources: [
 | 
			
		||||
        {
 | 
			
		||||
          source: 'inventaire',
 | 
			
		||||
          uri: (
 | 
			
		||||
            typeof entityObject.uri !== 'undefined'
 | 
			
		||||
              ? entityObject.uri
 | 
			
		||||
              : null
 | 
			
		||||
          ),
 | 
			
		||||
          link: (
 | 
			
		||||
            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') {
 | 
			
		||||
        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;
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			@ -126,7 +135,7 @@ class Inventaire {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    // 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 => {
 | 
			
		||||
      console.error(exception);
 | 
			
		||||
      return {
 | 
			
		||||
| 
						 | 
				
			
			@ -180,7 +189,7 @@ class Inventaire {
 | 
			
		|||
      }).map(key => {
 | 
			
		||||
        const entity = responseJSON.entities[key];
 | 
			
		||||
        return {
 | 
			
		||||
          uri: entity.uri,
 | 
			
		||||
          sourceId: entity.uri,
 | 
			
		||||
          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,
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -193,7 +202,10 @@ class Inventaire {
 | 
			
		|||
        return a.publishDate < b.publishDate ? -1 : 1;
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      return covers;
 | 
			
		||||
      return covers.map(cover => {
 | 
			
		||||
        delete cover.publishDate;
 | 
			
		||||
        return cover;
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,7 +28,7 @@ class BookReferenceController {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    // Get formatted book data from source
 | 
			
		||||
    const bookData = dataClass.getBookData(sourceId);
 | 
			
		||||
    const bookData = await dataClass.getBookData(sourceId);
 | 
			
		||||
 | 
			
		||||
    if (typeof bookData.uri !== 'undefined') {
 | 
			
		||||
      // Check for references by exact name and author from source
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,7 +43,14 @@ function searchInventaire(searchTerm, language) {
 | 
			
		|||
        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;
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@ const { Op, fn, col } = require('sequelize');
 | 
			
		|||
 | 
			
		||||
const BooksController = require('../bookData');
 | 
			
		||||
const { quickSearchInventaire } = require('./Inventaire');
 | 
			
		||||
const Inventaire = require('../bookData/Inventaire');
 | 
			
		||||
 | 
			
		||||
const defaultSearchOptions = {
 | 
			
		||||
  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) {
 | 
			
		||||
    const searchBy = options.searchBy.replace('title', 'name').replace('author', 'description');
 | 
			
		||||
    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
 | 
			
		||||
    const urisToCheck = searchResults.filter(
 | 
			
		||||
      result => !bookReferences.some(ref => result.uri === ref.sources[source])
 | 
			
		||||
    ).map(result => result.uri);
 | 
			
		||||
      result => !bookReferences.some(ref => result.sources[0].uri === ref.sources[source])
 | 
			
		||||
    ).map(result => result.sources[0].uri);
 | 
			
		||||
 | 
			
		||||
    let extraReferences = [];
 | 
			
		||||
    if (urisToCheck.length > 0) {
 | 
			
		||||
      // Need to figure this out
 | 
			
		||||
      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 [
 | 
			
		||||
      ...bookReferences,
 | 
			
		||||
      ...extraReferences,
 | 
			
		||||
      ...searchResults.filter(  // Only show the rest of the search results
 | 
			
		||||
        result => !extraReferences.some(
 | 
			
		||||
          ref => result.uri === ref.sources[source]
 | 
			
		||||
        )
 | 
			
		||||
      ),
 | 
			
		||||
      ...bookReferences.map(match => SearchController.formatReferenceSources(match)),
 | 
			
		||||
      ...extraReferences.map(match => SearchController.formatReferenceSources(match)),
 | 
			
		||||
      ...searchResults,
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue