diff --git a/app/javascript/packs/searchParams.js b/app/javascript/packs/searchParams.js new file mode 100644 index 000000000..ac495f948 --- /dev/null +++ b/app/javascript/packs/searchParams.js @@ -0,0 +1,220 @@ +/* global checkUserLoggedIn, showLoginModal, userData, buildArticleHTML, initializeReadingListIcons, initializeAllFollowButts, initializeUserFollowButts */ +/* eslint no-undef: "error" */ + +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) { + 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 = + '

'; + if (document.getElementById('query-wrapper')) { + search(query, filters, sortBy, sortDirection); + initializeFilters(query, filters); + initializeSortingTabs(query); + } +} + +function initializeSortingTabs(query) { + const sortingTabs = document.querySelectorAll( + '#sorting-option-tabs .crayons-tabs__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); + + 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-tabs__item--current'); + sortingTabs[j].setAttribute('aria-current', ''); + } + } + + e.target.classList.add('crayons-tabs__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-link--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(); + 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-link--current'); + } + if (className.indexOf('crayons-link--current') === -1) { + e.target.classList.add('crayons-link--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 utlity 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); + } + }); + + 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 = []; + content.result.forEach((story) => { + resultDivs.push(buildArticleHTML(story)); + }); + document.getElementById('substories').innerHTML = resultDivs.join(''); + initializeReadingListIcons(); + initializeAllFollowButts(); + initializeUserFollowButts(); + document + .getElementById('substories') + .classList.add('search-results-loaded'); + if (content.result.length === 0) { + document.getElementById('substories').innerHTML = + '
No results match that query
'; + } + }); +} + +const waitingOnSearch = setInterval(() => { + if ( + typeof search === 'function' && + typeof filterXSS === 'function' && + typeof buildArticleHTML === 'function' + ) { + clearInterval(waitingOnSearch); + const substories = document.getElementById('substories'); + if ( + substories && + document.getElementsByClassName('search-results-loaded').length === 0 + ) { + searchMain(substories); + } + } +}, 1); diff --git a/app/views/articles/_search.html.erb b/app/views/articles/_search.html.erb deleted file mode 100644 index 080282540..000000000 --- a/app/views/articles/_search.html.erb +++ /dev/null @@ -1,197 +0,0 @@ - diff --git a/app/views/articles/search.html.erb b/app/views/articles/search.html.erb index 770fa4de0..dfa50305e 100644 --- a/app/views/articles/search.html.erb +++ b/app/views/articles/search.html.erb @@ -54,6 +54,5 @@ -<%= render "articles/search" %> -<%= javascript_packs_with_chunks_tag "storiesList", defer: true %> +<%= javascript_packs_with_chunks_tag "searchParams", "storiesList", defer: true %>