import {fetchQuery} from "./contentUtility.js"; import Dataset from "./dataset.js"; const apiEndpoint = "/api/v1/datasets"; const baseURL = location.origin; const defaultPagingValue = 20; export const lastQuery = { totalPages: 0, currentPage: 0 }; const votedIDs = window.localStorage; const loadedCategories = new Set; // definition of all buttons & sections const addButton = document.getElementById("add-btn"); const filterButton = document.getElementById("filter-btn"); const searchButton = document.getElementById("search-btn"); const searchBar = document.getElementById("search-entry"); const sortButton = document.getElementById("sort-btn"); const resetButton = document.getElementById("reset-tools-btn"); const upvoteButtons = document.getElementsByClassName("upvote-btn"); const downvoteButtons = document.getElementsByClassName("downvote-btn"); export const searchSection = document.getElementById("search"); const recentSection = document.getElementById("recents"); const mostLikedSection = document.getElementById("top"); // ID of the timeout, because we need to cancel it at some point export let searchBarTimeout; const searchDelay = 500; // Event listeners addButton.addEventListener("click", () => { navigateToAdd(); }); filterButton.addEventListener("change", () => { fetchQuery(createQuery(), true); }); filterButton.addEventListener("click", () => { fetchCategories(); }) searchButton.addEventListener("click", () => { fetchQuery(createQuery(), true); }); searchBar.addEventListener("input", () => { clearTimeout(searchBarTimeout); searchBarTimeout = setTimeout(() => { fetchQuery(createQuery(), true); updateSections(); }, searchDelay); }); searchBar.addEventListener('keypress', function (e) { if (e.key === 'Enter') { fetchQuery(createQuery(), true); } }); sortButton.addEventListener("change", () => { fetchQuery(createQuery(), true); }); resetButton.addEventListener("click", () => { searchBar.value = ""; filterButton.value = filterButton.querySelector("#default-filter").value; sortButton.value = sortButton.querySelector("#default-sort").value; updateSections(); }); // Consider moving this to datasets.js completely // TODO: we dont need them, do we? there in dataset.js const upvoteButtonClickListener = e => { const entryID = e.target.parentElement.parentElement.dataset.id; vote(entryID, true); }; for (const upvoteButton of upvoteButtons) { upvoteButton.addEventListener("click", upvoteButtonClickListener); } // Consider moving this to datasets.js completely // TODO: we dont need them, do we? there in dataset.js const downvoteButtonClickListener = e => { const entryID = e.target.parentElement.parentElement.dataset.id; vote(entryID, false); }; for (const downvoteButton of downvoteButtons) { downvoteButton.addEventListener("click", downvoteButtonClickListener); } // functions of the main page function navigateToAdd() { window.location.href = "/add.html"; //TODO: move to EventListener? } function getFilterQuery() { let filterString = filterButton.value.toUpperCase(); if (filterString === "NONE") { return ["type", "%"] } else if (document.querySelector('#filter-btn option:checked').parentElement.label === "Standard categories") { return ["type", filterString]; } else { return ["category", filterButton.options[filterButton.selectedIndex].value] } } function getSearchQuery() { let searchString = searchBar.value; return (searchString.length === 0 ? "%" : ("%" + searchString + "%")); } function getSortQuery() { let sortString = sortButton.value.toLowerCase().split(" "); if (sortString[1] === "a-z" || sortString[1] === "↑" || sortString[1] === "oldest-newest") { sortString[1] = "asc"; } else { sortString[1] = "desc"; } return sortString } // creates query for the whole toolbar, so that searching, sorting and filtering are always combined function createQuery() { updateSections(); let queryURL = new URL(apiEndpoint + "/search", baseURL); queryURL.searchParams.append("search", getSearchQuery()); let filterQuery = getFilterQuery(); queryURL.searchParams.append(filterQuery[0], filterQuery[1]); let sortQuery = getSortQuery(); queryURL.searchParams.append("sort", sortQuery[0]); queryURL.searchParams.append("direction", sortQuery[1]); queryURL.searchParams.append("size", defaultPagingValue.toString(10)); return queryURL; } export function vote(entryID, up) { const fetchURL = new URL( `${apiEndpoint}/id/${entryID}/${up ? "up" : "down"}vote`, baseURL, ); fetch(fetchURL, { method: "PUT", headers: { 'Content-Type': 'application/json', } }) .then(resp => resp.json()) .then((data) => { console.log(data.upvotes); // TODO: remove, check einbauen: data.id === entryID? let dataSets = document.querySelectorAll('[data-id= ' + CSS.escape(entryID) + ']'); for (const dataSetElement of dataSets) { dataSetElement.querySelector("span").innerText = data.upvotes; let votedButton = dataSetElement.querySelector(up? ".upvote-btn":".downvote-btn"); votedButton.classList.add("isVoted"); votedButton.disabled = true; let notVotedButton = dataSetElement.querySelector(up? ".downvote-btn":".upvote-btn"); notVotedButton.style.visibility = "hidden"; votedIDs.setItem(dataSetElement.getAttribute("data-id"), up); } }); } // updates the page display. If no query is present, the initial page is shown, otherwise the search results. function updateSections() { if (searchBar.value === "" && sortButton.value === sortButton.querySelector("#default-sort").value && filterButton.value === filterButton.querySelector("#default-filter").value) { searchSection.classList.add("hidden"); recentSection.classList.remove("hidden"); mostLikedSection.classList.remove("hidden"); resetButton.classList.add("hidden"); } else { searchSection.classList.remove("hidden"); recentSection.classList.add("hidden"); mostLikedSection.classList.add("hidden"); resetButton.classList.remove("hidden"); } } // fetches the further categories used in the filter function function fetchCategories() { const fetchURL = new URL("api/v1/categories", baseURL); fetch(fetchURL) .then(resp => resp.json()) .then((data) => { for (let i = 0; i < data.length; i++) { let categoryName = data[i].name.toLowerCase(); categoryName = categoryName.charAt(0).toUpperCase() + categoryName.slice(1); if (!loadedCategories.has(categoryName)) { let newCategory = new Option(categoryName, data[i].id); document.getElementById("other-categories").appendChild(newCategory); loadedCategories.add(categoryName); } } }); } // fetches entries for the initial page function fetchInitialEntries() { let recentsQueryURL = new URL(apiEndpoint + "/search", baseURL); recentsQueryURL.searchParams.append("sort", "date"); recentsQueryURL.searchParams.append("direction", "desc"); recentsQueryURL.searchParams.append("size", "6"); fetch(recentsQueryURL) .then(resp => resp.json()) .then((data) => { const datasets = data.content.map(dataset => new Dataset(dataset)); for (const dataset of datasets) { document.querySelector("#recents .datasets") .appendChild(dataset.createDatasetHTMLElement()); } }); let topVotedQueryURL = new URL(apiEndpoint + "/search", baseURL); topVotedQueryURL.searchParams.append("sort", "upvotes"); topVotedQueryURL.searchParams.append("direction", "desc"); topVotedQueryURL.searchParams.append("size", "1"); fetch(topVotedQueryURL) .then(resp => resp.json()) .then((data) => { let dataset = new Dataset(data.content[0]); document.querySelector("#top .datasets") .appendChild(dataset.createDatasetHTMLElement()); }); } window.onload = function () { fetchCategories(); fetchInitialEntries(); updateSections(); if (searchBar.value !== "") { fetchQuery(createQuery(), true); } let observer = new IntersectionObserver(() => { if (!searchSection.classList.contains("hidden")) { fetchPagingResults(); } }, {root: null, rootMargin: "0px", threshold: .9}); observer.observe(document.getElementById("observable")); } function fetchPagingResults() { lastQuery.currentPage++ if (lastQuery.currentPage < lastQuery.totalPages) { let pagingQuery = new URL(createQuery()); pagingQuery.searchParams.append("page", lastQuery.currentPage.toString(10)); fetchQuery(pagingQuery, false); } }