import { h, Component } from 'preact'; import { SingleListing } from './singleListing'; export class Listings extends Component { state = { listings: [], query: '', tags: [], index: null, category: '', allCategories: [], initialFetch: true, currentUserId: null, openedListing: null, message: '', slug: null, page: 0, showNextPageButt: false, }; componentWillMount() { const params = this.getQueryParams(); const t = this; const algoliaId = document.querySelector("meta[name='algolia-public-id']") .content; const algoliaKey = document.querySelector("meta[name='algolia-public-key']") .content; const env = document.querySelector("meta[name='environment']").content; const client = algoliasearch(algoliaId, algoliaKey); const index = client.initIndex(`ClassifiedListing_${env}`); const container = document.getElementById('classifieds-index-container'); const category = container.dataset.category || ''; const allCategories = JSON.parse(container.dataset.allcategories || []); let tags = []; if (params.t) { tags = params.t.split(','); } const query = params.q || ''; let listings = []; if (tags.length === 0 && query === '') { listings = JSON.parse(container.dataset.listings); } let openedListing = null; let slug = null; if (container.dataset.displayedlisting) { openedListing = JSON.parse(container.dataset.displayedlisting); slug = openedListing.slug; document.body.classList.add('modal-open'); } t.setState({ query, tags, index, category, allCategories, listings, openedListing, slug, }); t.listingSearch(query, tags, category, slug); t.setUser(); document.body.addEventListener('keydown', t.handleKeyDown); /* The width of the columns also changes when the browser is resized so we will also call this function on window resize to recalculate each grid item's height to avoid content overflow */ window.addEventListener('resize', resizeAllMasonryItems); } componentDidUpdate() { this.triggerMasonry(); } componentWillUnmount() { document.body.removeEventListener('keydown', this.handleKeyDown); } addTag = (e, tag) => { e.preventDefault(); const { query, tags, category } = this.state; const newTags = tags; if (newTags.indexOf(tag) === -1) { newTags.push(tag); } this.setState({ tags: newTags, page: 0, listings: [] }); this.listingSearch(query, newTags, category, null); window.scroll(0, 0); }; removeTag = (e, tag) => { e.preventDefault(); const { query, tags, category } = this.state; const newTags = tags; const index = newTags.indexOf(tag); if (newTags.indexOf(tag) > -1) { newTags.splice(index, 1); } this.setState({ tags: newTags, page: 0, listings: [] }); this.listingSearch(query, newTags, category, null); }; selectCategory = (e, cat) => { e.preventDefault(); const { query, tags } = this.state; this.setState({ category: cat, page: 0, listings: [] }); this.listingSearch(query, tags, cat, null); }; handleKeyDown = e => { // Enable Escape key to close an open listing. this.handleCloseModal(e); }; handleCloseModal = e => { const { openedListing } = this.state; if ( (openedListing !== null && e.key === 'Escape') || e.target.id === 'single-classified-listing-container__inner' || e.target.id === 'classified-filters' || e.target.id === 'classified-listings-modal-background' ) { const { query, tags, category } = this.state; this.setState({ openedListing: null, page: 0 }); this.setLocation(query, tags, category, null); document.body.classList.remove('modal-open'); } }; handleOpenModal = (e, listing) => { e.preventDefault(); this.setState({ openedListing: listing }); window.history.replaceState( null, null, `/listings/${listing.category}/${listing.slug}`, ); this.setLocation(null, null, listing.category, listing.slug); document.body.classList.add('modal-open'); }; handleDraftingMessage = e => { e.preventDefault(); this.setState({ message: e.target.value }); }; handleSubmitMessage = e => { e.preventDefault(); const { message, openedListing } = this.state; if (this.state.message.replace(/\s/g, '').length === 0) { return; } const formData = new FormData(); formData.append('user_id', openedListing.user_id); formData.append('message', `**re: ${openedListing.title}** ${message}`); formData.append('controller', 'chat_channels'); const destination = `/connect/@${openedListing.author.username}`; const metaTag = document.querySelector("meta[name='csrf-token']"); window .fetch('/chat_channels/create_chat', { method: 'POST', headers: { 'X-CSRF-Token': metaTag.getAttribute('content'), }, body: formData, credentials: 'same-origin', }) .then(() => { window.location.href = destination; }); }; handleQuery = e => { const { tags, category } = this.state; this.setState({ query: e.target.value, page: 0, listings: [] }); this.listingSearch(e.target.value, tags, category, null); }; clearQuery = () => { const { tags, category } = this.state; document.getElementById('listings-search').value = ''; this.setState({ query: '', page: 0, listings: [] }); this.listingSearch('', tags, category, null); }; getQueryParams = () => { let qs = document.location.search; qs = qs.split('+').join(' '); const params = {}; let tokens; const re = /[?&]?([^=]+)=([^&]*)/g; // eslint-disable-next-line no-cond-assign while ((tokens = re.exec(qs))) { params[decodeURIComponent(tokens[1])] = decodeURIComponent(tokens[2]); } return params; }; loadNextPage = () => { const { query, tags, category, slug, page } = this.state; this.setState({ page: page + 1 }); this.listingSearch(query, tags, category, slug); }; setUser = () => { const t = this; setTimeout(function() { if (window.currentUser && t.state.currentUserId === null) { t.setState({ currentUserId: window.currentUser.id }); } }, 300); setTimeout(function() { if (window.currentUser && t.state.currentUserId === null) { t.setState({ currentUserId: window.currentUser.id }); } }, 1000); }; triggerMasonry = () => { resizeAllMasonryItems(); setTimeout(function() { resizeAllMasonryItems(); }, 1); setTimeout(function() { resizeAllMasonryItems(); }, 3); }; setLocation = (query, tags, category, slug) => { let newLocation = ''; if (slug) { newLocation = `/listings/${category}/${slug}`; } else if (query.length > 0 && tags.length > 0) { newLocation = `/listings/${category}?q=${query}&t=${tags}`; } else if (query.length > 0) { newLocation = `/listings/${category}?q=${query}`; } else if (tags.length > 0) { newLocation = `/listings/${category}?t=${tags}`; } else if (category.length > 0) { newLocation = `/listings/${category}`; } else { newLocation = '/listings'; } window.history.replaceState(null, null, newLocation); }; listingSearch(query, tags, category, slug) { const t = this; const { index, page, listings } = t.state; const filterObject = { tagFilters: tags, hitsPerPage: 75, page }; if (category.length > 0) { filterObject.filters = `category:${category}`; } index.search(query, filterObject).then(function searchDone(content) { const fullListings = listings; content.hits.forEach(listing => { if (!listings.map(l => l.id).includes(listing.id)) { fullListings.push(listing); } }); t.setState({ listings: fullListings, initialFetch: false, showNextPageButt: content.hits.length === 75, }); }); this.setLocation(query, tags, category, slug); } render() { const { listings, query, tags, category, allCategories, currentUserId, openedListing, showNextPageButt, initialFetch, } = this.state; const allListings = listings.map(listing => ( )); const selectedTags = tags.map(tag => ( this.removeTag(e, tag)} data-no-instant > {tag} this.removeTag(e, tag)} data-no-instant > × )); const categoryLinks = allCategories.map(cat => ( this.selectCategory(e, cat.slug)} data-no-instant > {cat.name} )); let nextPageButt = ''; if (showNextPageButt) { nextPageButt = (
); } const clearQueryButton = query.length > 0 ? ( ) : ( '' ); let modal = ''; let modalBg = ''; let messageModal = ''; if (openedListing) { modalBg = (