add catalog filters

This commit is contained in:
Tom (plebeius.eth)
2024-09-05 15:36:00 +02:00
parent 5d2e28eb5b
commit c70fa57cba
4 changed files with 95 additions and 32 deletions
+16 -1
View File
@@ -1,6 +1,11 @@
import { create } from 'zustand'; import { create } from 'zustand';
import { persist } from 'zustand/middleware'; import { persist } from 'zustand/middleware';
interface FilterItem {
text: string;
enabled: boolean;
}
interface CatalogFiltersStore { interface CatalogFiltersStore {
showAdultBoards: boolean; showAdultBoards: boolean;
setShowAdultBoards: (value: boolean) => void; setShowAdultBoards: (value: boolean) => void;
@@ -10,11 +15,14 @@ interface CatalogFiltersStore {
setShowTextOnlyThreads: (value: boolean) => void; setShowTextOnlyThreads: (value: boolean) => void;
filterText: string; filterText: string;
setFilterText: (value: string) => void; setFilterText: (value: string) => void;
filterItems: FilterItem[];
setFilterItems: (items: FilterItem[]) => void; // New method to set all filter items at once
saveAndApplyFilters: (items: FilterItem[]) => void; // Updated to accept items
} }
const useCatalogFiltersStore = create( const useCatalogFiltersStore = create(
persist<CatalogFiltersStore>( persist<CatalogFiltersStore>(
(set) => ({ (set, get) => ({
showTextOnlyThreads: false, showTextOnlyThreads: false,
setShowTextOnlyThreads: (value: boolean) => set({ showTextOnlyThreads: value }), setShowTextOnlyThreads: (value: boolean) => set({ showTextOnlyThreads: value }),
showAdultBoards: false, showAdultBoards: false,
@@ -23,6 +31,13 @@ const useCatalogFiltersStore = create(
setShowGoreBoards: (value: boolean) => set({ showGoreBoards: value }), setShowGoreBoards: (value: boolean) => set({ showGoreBoards: value }),
filterText: '', filterText: '',
setFilterText: (value: string) => set({ filterText: value }), setFilterText: (value: string) => set({ filterText: value }),
filterItems: [],
setFilterItems: (items: FilterItem[]) => set({ filterItems: items }),
saveAndApplyFilters: (items: FilterItem[]) => {
set({ filterItems: items });
console.log('Filters saved and applied:', items);
// Example: refreshCatalog();
},
}), }),
{ {
name: 'catalog-filters-storage', name: 'catalog-filters-storage',
@@ -111,7 +111,7 @@
border-spacing: 1px; border-spacing: 1px;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
padding-top: 10px; padding: 10px 2px 2px 2px;
} }
.filtersTable th { .filtersTable th {
@@ -1,4 +1,4 @@
import { useState } from 'react'; import { useState, useCallback, useEffect } from 'react';
import { useLocation, useParams } from 'react-router-dom'; import { useLocation, useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { isAllView, isCatalogView } from '../../../lib/utils/view-utils'; import { isAllView, isCatalogView } from '../../../lib/utils/view-utils';
@@ -7,8 +7,37 @@ import styles from './catalog-filters.module.css';
const FiltersTable = () => { const FiltersTable = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { filterText, setFilterText } = useCatalogFiltersStore(); const { filterItems, saveAndApplyFilters } = useCatalogFiltersStore();
const [localFilterText, setLocalFilterText] = useState(filterText); const [localFilterItems, setLocalFilterItems] = useState<any[]>([]);
useEffect(() => {
setLocalFilterItems(filterItems);
}, [filterItems]);
const handleAddFilter = useCallback(() => {
setLocalFilterItems([...localFilterItems, { text: '', enabled: true }]);
}, [localFilterItems]);
const handleSave = useCallback(() => {
saveAndApplyFilters(localFilterItems);
}, [saveAndApplyFilters, localFilterItems]);
const updateLocalFilterItem = useCallback((index: number, item: any) => {
setLocalFilterItems((items) => items.map((f, i) => (i === index ? item : f)));
}, []);
const removeLocalFilterItem = useCallback((index: number) => {
setLocalFilterItems((items) => items.filter((_, i) => i !== index));
}, []);
const moveLocalFilterItemUp = useCallback((index: number) => {
if (index === 0) return;
setLocalFilterItems((items) => {
const newItems = [...items];
[newItems[index - 1], newItems[index]] = [newItems[index], newItems[index - 1]];
return newItems;
});
}, []);
return ( return (
<table className={styles.filtersTable}> <table className={styles.filtersTable}>
@@ -21,26 +50,41 @@ const FiltersTable = () => {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> {localFilterItems.map((item, index) => (
<td> <tr key={index}>
<span className={styles.orderButton}></span> <td>
</td> <span className={styles.orderButton} onClick={() => moveLocalFilterItemUp(index)}>
<td>
<input type='checkbox' className={styles.onCheckbox} /> </span>
</td> </td>
<td> <td>
<input type='text' onChange={(e) => setLocalFilterText(e.target.value)} value={localFilterText} /> <input
</td> type='checkbox'
<td> className={styles.onCheckbox}
<span className={styles.deleteButton}>×</span> checked={item.enabled}
</td> onChange={(e) => updateLocalFilterItem(index, { ...item, enabled: e.target.checked })}
</tr> />
</td>
<td>
<input type='text' value={item.text} onChange={(e) => updateLocalFilterItem(index, { ...item, text: e.target.value })} />
</td>
<td>
<span className={styles.deleteButton} onClick={() => removeLocalFilterItem(index)}>
×
</span>
</td>
</tr>
))}
</tbody> </tbody>
<tfoot> <tfoot>
<tr> <tr>
<td colSpan={9}> <td colSpan={4}>
<button className={styles.addButton}>Add</button> <button className={styles.addButton} onClick={handleAddFilter}>
<button className={styles.saveButton}>Save</button> {t('add')}
</button>
<button className={styles.saveButton} onClick={handleSave}>
{t('save')}
</button>
</td> </td>
</tr> </tr>
</tfoot> </tfoot>
@@ -89,8 +133,8 @@ const FiltersModal = ({ closeModal }: { closeModal: () => void }) => {
</div> </div>
</div> </div>
)} )}
<FiltersTable />
</div> </div>
<FiltersTable />
</div> </div>
</> </>
); );
+13 -9
View File
@@ -27,13 +27,17 @@ const threadsWithImagesFilter = (comment: Comment) => {
return hasThumbnail; return hasThumbnail;
}; };
const textFilter = (comment: Comment, filterText: string) => { const textFilter = (comment: Comment, filterItems: any[]) => {
if (!filterText) return true; if (!filterItems) return true;
const filterWords = filterText.toLowerCase().split(/\s+/); if (filterItems.length === 0) return true;
const titleWords = comment?.title?.toLowerCase().split(/\s+/) || []; const titleLower = comment?.title?.toLowerCase() || '';
const contentWords = comment?.content?.toLowerCase().split(/\s+/) || []; const contentLower = comment?.content?.toLowerCase() || '';
return !filterWords.every((filterWord) => titleWords.includes(filterWord) || contentWords.includes(filterWord)); return filterItems.every((item) => {
if (!item.enabled) return true;
const pattern = item.text.toLowerCase();
return !(titleLower.includes(pattern) || contentLower.includes(pattern));
});
}; };
const Catalog = () => { const Catalog = () => {
@@ -43,7 +47,7 @@ const Catalog = () => {
const isInAllView = isAllView(location.pathname, useParams()); const isInAllView = isAllView(location.pathname, useParams());
const defaultSubplebbits = useDefaultSubplebbits(); const defaultSubplebbits = useDefaultSubplebbits();
const { showAdultBoards, showGoreBoards, showTextOnlyThreads, filterText } = useCatalogFiltersStore(); const { showAdultBoards, showGoreBoards, showTextOnlyThreads, filterItems } = useCatalogFiltersStore();
const account = useAccount(); const account = useAccount();
const subscriptions = account?.subscriptions; const subscriptions = account?.subscriptions;
@@ -92,10 +96,10 @@ const Catalog = () => {
if (!showTextOnlyThreads && !threadsWithImagesFilter(comment)) { if (!showTextOnlyThreads && !threadsWithImagesFilter(comment)) {
return false; return false;
} }
return textFilter(comment, filterText); return textFilter(comment, filterItems);
}, },
}), }),
[subplebbitAddresses, sortType, isInAllView, isInSubscriptionsView, postsPerPage, showTextOnlyThreads, filterText], [subplebbitAddresses, sortType, isInAllView, isInSubscriptionsView, postsPerPage, showTextOnlyThreads, filterItems],
); );
if (isInAllView || isInSubscriptionsView) { if (isInAllView || isInSubscriptionsView) {