docbrown/app/javascript/packs/searchParams.js
Ben Halpern b3b0eafe76
Implement MVP frontend for Algolia search (#20909)
* Add proper frontend for Algolia search

* Update app/models/concerns/algolia_searchable/searchable_comment.rb

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Update app/models/concerns/algolia_searchable/searchable_article.rb

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Fix formatting

* Fix: copy to clipboard message (#20898)

* fix: copy to clipboard message

* chore: deletion of unused imports

* Pick winner in header field test (#20911)

* Updated the twitter icon to X icon - solved icon issue #20356  (#20910)

* changed the twitter icon to X icon in assets/images/twitter.svg

* Changed the twitter icon to X in assets/images/twitter.svg

* Add field test for Digest article list design (#20912)

* Add field test for Digest article list design

* Add check for cached user

* Include cached user in select (#20913)

* Include cached user in select

* Add tags

* update style for new follower email (#20908)

* Fix #19037 (#20917)

* Add alternate returns for html

* Remove console log

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Iasmim Pereira <100351576+IasmimCristina@users.noreply.github.com>
Co-authored-by: Darshan Padia <darshanpadia5@gmail.com>
Co-authored-by: Philip How <philip.j.how@gmail.com>
2024-05-06 15:20:53 -04:00

282 lines
9.5 KiB
JavaScript

/* global checkUserLoggedIn, showLoginModal, userData, buildArticleHTML, initializeReadingListIcons */
/* eslint no-undef: "error" */
// This is a lightweight version of the client, which we should be fine importing regardless of whether it is set up.
// Could be optimized for optional inclusion in the future.
import algoliasearch from 'algoliasearch/lite'
function getQueryParams(qs) {
qs = qs.split('+').join(' ');
const params = {},
re = /[?&]?([^=]+)=([^&]*)/g;
let tokens;
while ((tokens = re.exec(qs))) {
params[decodeURIComponent(tokens[1])] = decodeURIComponent(tokens[2]);
}
return params;
}
const params = getQueryParams(document.location.search);
function searchMain(substories, loadingHTML) {
const query = filterXSS(params.q);
const filters = filterXSS(params.filters || 'class_name:Article');
const sortBy = filterXSS(params.sort_by || '');
const sortDirection = filterXSS(params.sort_direction || '');
substories.innerHTML = loadingHTML;
if (document.getElementById('query-wrapper')) {
search(query, filters, sortBy, sortDirection);
initializeFilters(query, filters);
initializeSortingTabs(query, substories, loadingHTML);
}
}
function initializeSortingTabs(query, substories, loadingHTML) {
const sortingTabs = document.querySelectorAll(
'#sorting-option-tabs .crayons-navigation__item',
);
for (let i = 0; i < sortingTabs.length; i++) {
const tab = sortingTabs[i];
tab.addEventListener('click', (e) => {
const currentParams = getQueryParams(document.location.search);
const filters = filterXSS(currentParams.filters);
const { sortBy, sortDirection } = e.target.dataset;
const sortString = buildSortString(sortBy, sortDirection);
substories.innerHTML = loadingHTML;
if (filters) {
window.history.pushState(
null,
null,
`/search?q=${query}&filters=${filters}${sortString}`,
);
search(query, filters, sortBy, sortDirection);
} else {
window.history.pushState(null, null, `/search?q=${query}${sortString}`);
search(query, '', sortBy, sortDirection);
}
for (let j = 0; j < sortingTabs.length; j++) {
if (sortingTabs[j] !== e.target) {
sortingTabs[j].classList.remove('crayons-navigation__item--current');
sortingTabs[j].setAttribute('aria-current', '');
}
}
e.target.classList.add('crayons-navigation__item--current');
e.target.setAttribute('aria-current', 'page');
});
}
}
function initializeFilters(query, filters) {
const filterButts = document.getElementsByClassName('query-filter-button');
for (let i = 0; i < filterButts.length; i++) {
if (filters === filterButts[i].dataset.filter) {
filterButts[i].classList.add('crayons-navigation__item--current');
}
filterButts[i].onclick = function (e) {
const currentParams = getQueryParams(document.location.search);
const sortBy = filterXSS(currentParams.sort_by);
const sortDirection = filterXSS(currentParams.sort_direction);
const sortString = buildSortString(sortBy, sortDirection);
if (
e.target.classList.contains('my-posts-query-button') &&
!checkUserLoggedIn()
) {
showLoginModal({
referring_source: 'search',
trigger: 'my_posts_filter',
});
return;
}
const filters = e.target.dataset.filter;
window.history.pushState(
null,
null,
`/search?q=${query}&filters=${filters}${sortString}`,
);
const { className } = e.target;
for (let i = 0; i < filterButts.length; i++) {
filterButts[i].classList.remove('crayons-navigation__item--current');
}
if (className.indexOf('crayons-navigation__item--current') === -1) {
e.target.classList.add('crayons-navigation__item--current');
window.history.replaceState(
null,
null,
`/search?q=${query}&filters=${filters}${sortString}`,
);
search(query, filters, sortBy, sortDirection);
} else {
window.history.replaceState(
null,
null,
`/search?q=${query}${sortString}`,
);
search(query, '', sortBy, sortDirection);
}
};
}
}
function buildSortString(sortBy, sortDirection) {
return sortBy && sortDirection
? `&sort_by=${sortBy}&sort_direction=${sortDirection}`
: '';
}
function search(query, filters, sortBy, sortDirection) {
const hashtags = query.match(/#\w+/g);
const searchTerm = query.replace(/#/g, '').trim();
const searchHash = { per_page: 60, page: 0 };
if (sortBy && sortDirection) {
searchHash.sort_by = sortBy;
searchHash.sort_direction = sortDirection;
}
if (filters === 'MY_POSTS' && userData()) {
searchHash.user_id = userData()['id'];
searchHash.class_name = 'Article';
}
if (hashtags && hashtags.length > 0) {
for (let i = 0; i < hashtags.length; i++) {
hashtags[i] = hashtags[i].replace(/#/, '');
}
searchHash.tag_names = hashtags;
}
if (filters) {
filters.split('&').forEach((filter) => {
const [key, value] = filter.split(':');
searchHash[key] = value;
});
}
if (searchTerm) {
searchHash.search_fields = searchTerm;
}
// Brute force copying code from a utility for quick fix
const searchParams = new URLSearchParams();
Object.keys(searchHash).forEach((key) => {
const value = searchHash[key];
if (Array.isArray(value)) {
value.forEach((arrayValue) => {
searchParams.append(`${key}[]`, arrayValue);
});
} else {
searchParams.append(key, value);
}
});
// Run Algolia code only if the ID is live.
if (document.body.dataset.algoliaId?.length > 0 && !searchParams.toString().includes('MY_POSTS')) {
algoliaSearch(searchParams.toString());
return;
}
fetch(`/search/feed_content?${searchParams.toString()}`, {
method: 'GET',
headers: {
Accept: 'application/json',
'X-CSRF-Token': window.csrfToken,
'Content-Type': 'application/json',
},
credentials: 'same-origin',
})
.then((response) => response.json())
.then((content) => {
const resultDivs = [];
const currentUser = userData();
const currentUserId = currentUser && currentUser.id;
content.result.forEach((story) => {
resultDivs.push(buildArticleHTML(story, currentUserId));
});
document.getElementById('substories').innerHTML = resultDivs.join('');
initializeReadingListIcons();
document
.getElementById('substories')
.classList.add('search-results-loaded');
if (content.result.length === 0) {
document.getElementById('substories').innerHTML =
'<div class="p-9 align-center crayons-card">No results match that query</div>';
}
});
}
function algoliaSearch(searchParams) {
console.log('Algolia is a work in progress. You may want to turn it off if you see this message.') /* eslint-disable-line */
// console.log(searchParams) /* eslint-disable-line */
// const queryParams = getQueryParams(searchParams);
const paramsObj = getQueryParams(searchParams);
const env = document.querySelector('meta[name="environment"]').content;
const {algoliaId, algoliaSearchKey} = document.body.dataset;
console.log(paramsObj) /* eslint-disable-line */
const client = algoliasearch(algoliaId, algoliaSearchKey);
const indexName = paramsObj.sort_by ? `${paramsObj.class_name || 'Article'}_timestamp_${paramsObj.sort_direction}_${env}` : `${paramsObj.class_name || 'Article'}_${env}`;
const index = client.initIndex(indexName); // Hardcoded to user for now
console.log(index) /* eslint-disable-line */
// This is where we will add the functionality to get search results directly from index with client:
index
.search(paramsObj.search_fields, {
hitsPerPage: paramsObj.per_page,
page: paramsObj.page,
})
.then(({ hits }) => {
console.log('Algolia search results:') /* eslint-disable-line */
console.log(hits); /* eslint-disable-line */
const resultDivs = [];
const currentUser = userData();
const currentUserId = currentUser && currentUser.id;
hits.forEach((story) => {
story.class_name = paramsObj.class_name;
story.id = story.objectID;
// Add profile_image_90 to story object from profile image if profile_image_90 is not present
resultDivs.push(buildArticleHTML(story, currentUserId));
});
document.getElementById('substories').innerHTML = resultDivs.join('');
initializeReadingListIcons();
document
.getElementById('substories')
.classList.add('search-results-loaded');
if (hits.length === 0) {
document.getElementById('substories').innerHTML =
'<div class="p-9 align-center crayons-card">No results match that query</div>';
}
})
.catch(err => {
console.log('Algolia search error:') /* eslint-disable-line */
console.log(err); /* eslint-disable-line */
});
}
const waitingOnSearch = setInterval(() => {
if (
typeof search === 'function' &&
typeof filterXSS === 'function' &&
typeof buildArticleHTML === 'function'
) {
clearInterval(waitingOnSearch);
const substories = document.getElementById('substories');
const loadingHTML = document.querySelector(
'template[id=crayons-story-loading]',
).innerHTML;
if (
substories &&
document.getElementsByClassName('search-results-loaded').length === 0
) {
searchMain(substories, loadingHTML);
}
}
}, 1);