From fb4f553653e7f29e9a3f468ffb7e7fa37c8ac47a Mon Sep 17 00:00:00 2001 From: Andre Beging Date: Tue, 4 Jan 2022 10:17:54 +0100 Subject: [PATCH] Update foodsharing_planner.user.js --- foodsharing_planner.user.js | 226 ++++++++++++++++++++++++++++++++---- 1 file changed, 202 insertions(+), 24 deletions(-) diff --git a/foodsharing_planner.user.js b/foodsharing_planner.user.js index c563dd7..cf6c7f2 100644 --- a/foodsharing_planner.user.js +++ b/foodsharing_planner.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Foodsharing Planner // @namespace http://tampermonkey.net/ -// @version 0.9 +// @version 0.11 // @updateURL https://github.com/TroogS/userscripts/raw/master/foodsharing_planner.user.js // @downloadURL https://github.com/TroogS/userscripts/raw/master/foodsharing_planner.user.js // @description Generate a calendar like view as addition to the foodsharing website germany, austria and switzerland @@ -17,6 +17,96 @@ // @grant none // ==/UserScript== +class CacheHelper { + + static Write(key, value) { + + var data = {"data" : value}; + console.log("storing data", data); + + var serializedData = JSON.stringify(data); + + window.localStorage.setItem("fspl_" + key, serializedData); + } + + static Read(key) { + var serializedData = window.localStorage.getItem("fspl_" + key); + if(!serializedData) return null; + var data = JSON.parse(serializedData); + return data.data; + } + + static ReadDate(key) { + var serializedData = window.localStorage.getItem("fspl_" + key); + if(!serializedData) return null; + + var data = JSON.parse(serializedData); + var date = new Date(data.data); + + return date; + } + + static PickupsValid() { + + var lastLoadString = this.ReadDate("pickupLoaded"); + if(!lastLoadString) return false; + + var lastLoad = new Date(lastLoadString); + + var diff = new Date() - lastLoad; + diff = diff / 1000; + + if(diff > 600) return false; + + var pickupData = this.Read("pickupData"); + if(!pickupData) return false; + + return true; + } + + static InvalidatePickups() { + this.Write("pickupLoaded", null); + } + + static async GetStoreDetails(storeId) { + + var localStorageKey = "storeDetails-" + storeId; + var storeDetails = this.Read(localStorageKey); + if(storeDetails) return storeDetails; + + var storeDetailsApi = await Api.GetStoreDetails(storeId); + + var storeDetails = { + id: storeId, + name: storeDetailsApi.store.name, + group: storeDetailsApi.store.group + }; + + this.Write(localStorageKey, storeDetails); + return storeDetails; + } + + static async LoadPickupsAsync() { + + var valid = this.PickupsValid(); + + if(valid) { + var pickupData = this.Read("pickupData"); + pickupData.forEach(pickup => { + pickup.pickup.dateObj = new Date(pickup.pickup.date); + }); + return pickupData; + } + + var pickupData = await Api.LoadPickupsAsync(); + this.Write("pickupData", pickupData); + this.Write("pickupLoaded", new Date()); + + return pickupData; + } + +} + class Api { static PickupData; @@ -31,15 +121,20 @@ class Api { var confResult = confirm("Bitte bestätigen!\nAbholung " + dateText + " - " + timeText + " bei " + data.store.name + " buchen?"); if(confResult) { + Convenience.ShowLoadingOverlay(); var endpoint = "stores/" + data.store.id + "/pickups/" + data.pickup.dateObj.toISOString() + "/" + this.User.id; var result = await this.PostAsync(endpoint); // Invalidate and reload - DataStore.Loaded = false; + CacheHelper.InvalidatePickups(); BuildPlannerAsync(); } } + static async GetStoreDetails(storeId) { + return await this.GetAsync("stores/" + storeId); + } + // Api get call function static async GetAsync(endpoint) { @@ -71,7 +166,6 @@ class Api { "X-CSRF-Token": this.Token, }, data : JSON.stringify(data), - //data: {name:'yogesh',salary: 35000,email: 'yogesh@makitweb.com'}, success: function(response){ } @@ -112,11 +206,11 @@ class Api { var apiStoreData = await this.GetAsync("user/current/stores"); await Convenience.AsyncForEach(apiStoreData, async (store) => { + var apiPickups = await this.GetAsync('stores/' + store.id + '/pickups'); if(apiPickups.pickups.length > 0) { apiPickups.pickups.forEach(pickup => { - pickup.dateObj = new Date(pickup.date); pickup.freeSlots = pickup.totalSlots - pickup.occupiedSlots.length; @@ -133,8 +227,6 @@ class Api { } }); - DataStore.Loaded = true; - return pickupData; } @@ -273,8 +365,8 @@ class StyleHelper { align-items: center; } -.fspl .loading-overlay.hidden { - display: none; +.hidden { + display: none !important; } .fspl .loading-overlay > div { @@ -316,6 +408,10 @@ class StyleHelper { background-color: #d8ffbe; } +.day .pickup.highlight { + background-color: #9ef6ff; +} + .day .pickup > a { color: var(--fs-brown); } @@ -334,7 +430,7 @@ class StyleHelper { .day .pickup .img-container { display: grid; - grid-template-columns: min-content min-content; + grid-template-columns: min-content min-content min-content; gap: 5px; } @@ -392,7 +488,6 @@ class StyleHelper { } class DataStore { - static Loaded = false; static FirstDay; } @@ -420,6 +515,13 @@ class DOMHelper { document.querySelectorAll(".navbar-nav.nav-row")[0].append(div); } + static CreateNavigationSeparator(parentElement) { + + var separatorElement = this.CreateElement("div", "d-inline"); + separatorElement.innerHTML = "•"; + parentElement.append(separatorElement); + } + static CreateNavigationButton(parentElement, title, iconClass, callback) { var buttonElement = this.CreateElement("button", "button m-1"); @@ -433,6 +535,19 @@ class DOMHelper { return buttonElement; } + static CreateNavigationTextButton(parentElement, title, callback) { + + var buttonElement = this.CreateElement("button", "button m-1"); + buttonElement.setAttribute("title", title); + buttonElement.innerHTML = title; + buttonElement.addEventListener('click',function () { + callback(); + }); + + parentElement.append(buttonElement); + return buttonElement; + } + static CreateLoadingOverlay(parentElement) { var loadingOverlay = this.CreateElement("div", "loading-overlay hidden"); @@ -498,12 +613,16 @@ async function BuildPlannerAsync() { weekPanel.append(sat); weekPanel.append(sun); - if(!DataStore.Loaded) Api.PickupData = await Api.LoadPickupsAsync(); + var pickupData = await CacheHelper.LoadPickupsAsync(); var lastDayDate = Convenience.GetLastDay(DataStore.FirstDay); - Api.PickupData.sort(function(a, b){return a.pickup.dateObj > b.pickup.dateObj}); - Api.PickupData.forEach(pickup => { + pickupData.sort(function(a, b){return a.pickup.dateObj > b.pickup.dateObj}); + + await Convenience.AsyncForEach(pickupData, async (pickup) => { + + var storeDetails = await CacheHelper.GetStoreDetails(pickup.store.id); + pickup.store.details = storeDetails; if(pickup.pickup.dateObj > DataStore.FirstDay && pickup.pickup.dateObj < lastDayDate) { var pickupDiv = CreatePickupDiv(pickup); @@ -534,12 +653,48 @@ async function BuildPlannerAsync() { } }); + // Get distinct list of store groups + const groups = []; + const map = new Map(); + for (const item of pickupData) { + if(!map.has(item.store.details.group.id)){ + map.set(item.store.details.group.id, true); // set any value to Map + groups.push({ + id: item.store.details.group.id, + name: item.store.details.group.name + }); + } + } + + if(groups && groups.length > 1) { + var groupButtonDiv = document.querySelectorAll("#groupbuttons")[0]; + groupButtonDiv.innerHTML = ""; + groups.forEach(group => { + + DOMHelper.CreateNavigationTextButton(groupButtonDiv, group.name, () => { + + var targetPickups = document.querySelectorAll(".group-" + group.id); + targetPickups.forEach(targetPickup => { + targetPickup.classList.toggle("hidden"); + }); + + }); + + }); + + DOMHelper.CreateNavigationSeparator(groupButtonDiv); + } + Convenience.HideLoadingOverlay(); } function CreateNavigationButtons(mainPanel) { var navigationPanel = DOMHelper.CreateElement("div", "fspl-nav text-center"); + var groupToggleDiv = DOMHelper.CreateElement("div", "d-inline"); + groupToggleDiv.setAttribute("id", "groupbuttons"); + navigationPanel.append(groupToggleDiv); + DOMHelper.CreateNavigationButton(navigationPanel, "Vorherige Woche", "fas fa-arrow-left", () => { DataStore.FirstDay.setDate(DataStore.FirstDay.getDate() - 7); BuildPlannerAsync(); @@ -550,33 +705,56 @@ function CreateNavigationButtons(mainPanel) { BuildPlannerAsync(); }); - var closeButton = DOMHelper.CreateNavigationButton(navigationPanel, "Heute", "fas fa-times", () => { - TogglePlanner(); - }); - closeButton.classList.add("button-red"); - - DOMHelper.CreateNavigationButton(navigationPanel, "Neu laden", "fas fa-sync", () => { - DataStore.Loaded = false; - BuildPlannerAsync(); - }); - DOMHelper.CreateNavigationButton(navigationPanel, "Nächste Woche", "fas fa-arrow-right", () => { DataStore.FirstDay.setDate(DataStore.FirstDay.getDate() + 7); BuildPlannerAsync(); }); + DOMHelper.CreateNavigationSeparator(navigationPanel); + + DOMHelper.CreateNavigationButton(navigationPanel, "Toggle Marker", "fas fa-highlighter", () => { + + var highlightElements = document.querySelectorAll(".highlighter"); + highlightElements.forEach(highlightElement => { + highlightElement.classList.toggle("hidden"); + }); + }); + + DOMHelper.CreateNavigationSeparator(navigationPanel); + + DOMHelper.CreateNavigationButton(navigationPanel, "Neu laden", "fas fa-sync", () => { + CacheHelper.InvalidatePickups(); + BuildPlannerAsync(); + }); + + var closeButton = DOMHelper.CreateNavigationButton(navigationPanel, "Heute", "fas fa-times", () => { + TogglePlanner(); + }); + closeButton.classList.add("button-red"); + mainPanel.prepend(navigationPanel); } function CreatePickupDiv(data) { - var elementClass = "pickup"; + var elementClass = "pickup store-" + data.store.id + " group-" + data.store.details.group.id; if(data.pickup.occupiedSlots.length == data.pickup.totalSlots) elementClass += " pickup-green"; if(data.pickup.occupiedSlots.length < data.pickup.totalSlots && data.pickup.occupiedSlots.length > 0) elementClass += " pickup-yellow"; if(data.pickup.occupiedSlots.length == 0) elementClass += " pickup-red"; var element = DOMHelper.CreateElement("div", elementClass); + var highlightLink = DOMHelper.CreateElement("a", "highlighter hidden"); + highlightLink.innerHTML = ' '; + highlightLink.setAttribute("href", "#"); + highlightLink.addEventListener('click', function () { + var hlPickups = document.querySelectorAll(".pickup.store-" + data.store.id); + hlPickups.forEach(hlPickup => { + hlPickup.classList.toggle("highlight"); + }); + }); + element.append(highlightLink); + var headerSpan = DOMHelper.CreateElement("a", "font-weight-bold", data.store.name); headerSpan.setAttribute("href", "https://foodsharing.de/?page=fsbetrieb&id=" + data.store.id); headerSpan.setAttribute("target", "_blank");