From f4be611c51473359e6d41780f0313c446079e9d3 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Tue, 09 Jun 2026 15:19:24 +0000
Subject: [PATCH] =Switched the /base/options.php to the same pattern as Site.php: a class based approached rather than a filter. Updated Meta.php to play along with the defined fields from there in Meta::forOptions. Had to change openingHoursSpecificationsTrait.php to not use the translater functions __('text','textdomain') for now, as we load before init.

---
 assets/js/concise/FrontendFavourites.js |  239 ++++++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 209 insertions(+), 30 deletions(-)

diff --git a/assets/js/concise/FrontendFavourites.js b/assets/js/concise/FrontendFavourites.js
index 2ef641b..f986ff1 100644
--- a/assets/js/concise/FrontendFavourites.js
+++ b/assets/js/concise/FrontendFavourites.js
@@ -1,21 +1,46 @@
-class FrontendFavourites {
+class Favourites {
 	constructor() {
 		// Initialize DataStore for queue persistence
-		this.store = new window.jvbStore({
-			name: 'favourites',
-			endpoint: 'favourites',
-			useIndexedDB: true,
-			TTL: Infinity,
-			showLoading: false,
-			filters: {
-				user: jvbSettings.currentUser,
-				content: 'all',
-				order: 'desc',
-				orderby: 'date',
-				page: 1,
-				all: true,
-			}
-		});
+		let store = window.jvbStore.register(
+			'favourites',
+			{
+				storeName: 'favourites',
+				keyPath: 'id',
+				endpoint: 'favourites',
+				headers: {
+					'X-Action-Nonce': window.auth.getNonce('favourites')
+				},
+				indexes: [
+					{name: 'content', keyPath: 'content'},
+					{name: 'listId', keyPath: 'listId'},
+				],
+				TTL: 6 * 60 * 1000,
+				showLoading: false,
+				filters: {
+					user: window.auth.getUser(),
+					content: 'all',
+					order: 'desc',
+					orderby: 'date',
+					page: 1,
+					all: true,
+				}
+			});
+
+		this.queue = {
+			add: new Map(),
+			remove: new Map(),
+		};
+
+		this.store = store.favourites;
+
+		// this.listStore = window.jvbStore.register(
+		// 	'favourites_lists',
+		// 	{
+		// 		storeName: 'lists',
+		// 		keyPath: 'listId',
+		// 		endpoint: 'favourites/lists',
+		// 		TTL: 6 * 60 * 1000,
+		// 	});
 
 		this.store.subscribe((event, data) => {
 			switch (event) {
@@ -32,12 +57,10 @@
 			}
 
 		});
-
-		this.store.fetch();
 	}
 
 	toggleFavourite(button) {
-		if (!jvbSettings.currentUser) {
+		if (!window.auth.getUser()) {
 			window.location.href = jvbSettings.redirect + '&action=register&type=favourites';
 			return;
 		}
@@ -53,27 +76,183 @@
 		// Update button icon
 		button.innerHTML = jvbSettings.icons[button.classList.contains('favourited') ? 'heart-filled' : 'heart'];
 
-		this.store.setItem(button.dataset.id, {
+		this.checkQueue(action, {
 			target_id: button.dataset.id,
 			action: action,
-			type: button.dataset.type,
-			artist: button.dataset.artist,
+			type: button.dataset.type
 		});
+		window.debouncer.schedule('favourites', this.postFavourites.bind(this), 200);
+
+
 	}
 
-	isFavourited(content, id){
-		if(!jvbSettings.currentUser){
-			return false;
+	checkQueue(action, item) {
+		switch (action) {
+			case 'add':
+				if (this.queue.remove.has(item.target_id)) {
+					this.queue.remove.delete(item.target_id);
+				}
+				this.queue.add.set(item.target_id, item);
+				break;
+			case 'remove':
+				if (this.queue.add.has(item.target_id)) {
+					this.queue.add.delete(item.target_id);
+				}
+				this.queue.remove.set(item.target_id, item);
+				break;
+			default:
+				return;
 		}
-		let item = this.store.getItem(id);
-		return (item) ? item.action === 'add' : false;
+	}
+	async postFavourites() {
+		console.log(this.queue,'Posting favourites');
+		const response = await window.auth.fetch(
+			`${jvbSettings.api}favourites`,
+			{
+				method: 'POST',
+				headers: {
+					'X-Action-Nonce': window.auth.getNonce('favourites')
+				},
+				body: {
+					user: window.auth.getUser(),
+					... this.queue
+				}
+			}
+		);
+
+		if (response.ok) {
+			console.log('Posted favourites - clearing queue');
+			//Add or remove from store
+			for (let item of this.queue.add.entries()) {
+				await this.store.setItem(item.target_id,
+					item);
+			}
+			for (let item of this.queue.remove.entries()) {
+				await this.store.delete(item.target_id);
+			}
+			//Clear the favourite queue
+			this.queue.add.clear();
+			this.queue.remove.clear();
+		} else {
+			console.log(await response.json(), 'Something went wrong');
+			window.debouncer.schedule('favourites', this.postFavourites.bind(this), 200);
+		}
+	}
+
+	// async toggleFavourite(itemType, itemId) {
+	// 	const favId = `${this.userId}_${itemType}_${itemId}`;
+	// 	const existing = this.store.get(favId);
+	//
+	// 	if (existing) {
+	// 		// Remove favorite
+	// 		await this.store.delete(favId);
+	//
+	// 		// Queue for server deletion
+	// 		if (window.jvbQueue) {
+	// 			window.jvbQueue.addOperation({
+	// 				type: 'remove_favourite',
+	// 				endpoint: 'favourites',
+	// 				data: { favId }
+	// 			});
+	// 		}
+	//
+	// 		return false; // Not favorited anymore
+	//
+	// 	} else {
+	// 		// Add favorite
+	// 		const favourite = {
+	// 			id: favId,
+	// 			userId: this.userId,
+	// 			itemType,
+	// 			itemId,
+	// 			listId: 'default',
+	// 			timestamp: Date.now()
+	// 		};
+	//
+	// 		await this.store.save(favourite);
+	//
+	// 		// Queue for server save
+	// 		if (window.jvbQueue) {
+	// 			window.jvbQueue.addOperation({
+	// 				type: 'add_favourite',
+	// 				endpoint: 'favourites',
+	// 				data: favourite
+	// 			});
+	// 		}
+	//
+	// 		return true; // Now favorited
+	// 	}
+	// }
+
+
+	// async createList(name, visibility = 'private') {
+	// 	const list = {
+	// 		listId: `list_${Date.now()}`,
+	// 		userId: this.userId,
+	// 		name,
+	// 		visibility,
+	// 		created: Date.now(),
+	// 		itemCount: 0
+	// 	};
+	//
+	// 	await this.listsStore.save(list);
+	//
+	// 	// Queue for server
+	// 	if (window.jvbQueue) {
+	// 		window.jvbQueue.addOperation({
+	// 			type: 'create_list',
+	// 			endpoint: 'favourite-lists',
+	// 			data: list
+	// 		});
+	// 	}
+	//
+	// 	return list;
+	// }
+	//
+	// async addToList(listId, itemType, itemId) {
+	// 	const favId = `${this.userId}_${itemType}_${itemId}_${listId}`;
+	//
+	// 	const favourite = {
+	// 		id: favId,
+	// 		userId: this.userId,
+	// 		itemType,
+	// 		itemId,
+	// 		listId,
+	// 		timestamp: Date.now()
+	// 	};
+	//
+	// 	await this.store.save(favourite);
+	//
+	// 	// Update list count
+	// 	const list = this.listsStore.get(listId);
+	// 	if (list) {
+	// 		list.itemCount++;
+	// 		await this.listsStore.save(list);
+	// 	}
+	//
+	// 	// Queue for server
+	// 	if (window.jvbQueue) {
+	// 		window.jvbQueue.addOperation({
+	// 			type: 'add_to_list',
+	// 			endpoint: 'favourites',
+	// 			data: favourite
+	// 		});
+	// 	}
+	// }
+
+	isFavourited(itemType, itemId) {
+		const favId = `${this.userId}_${itemType}_${itemId}`;
+		return this.store.get(favId) !== undefined;
 	}
 }
+
 document.addEventListener('DOMContentLoaded', function() {
 	window.jvbFavourites = false;
-	if (jvbSettings.currentUser !== '') {
-		window.jvbFavourites = new FrontendFavourites();
-	}
+	window.auth.subscribe((event) => {
+		if (event === 'auth-loaded') {
+			window.jvbFavourites = new Favourites();
+		}
+	});
 });
 
 

--
Gitblit v1.10.0