mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
add catalog filters
This commit is contained in:
@@ -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>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user