feat(catalog filter): add filtering with complex patterns, including regex, help modal

This commit is contained in:
Tom (plebeius.eth)
2025-03-06 23:48:17 +01:00
parent ad60404792
commit d7a9c1640b
45 changed files with 500 additions and 85 deletions

Before

Width:  |  Height:  |  Size: 150 B

After

Width:  |  Height:  |  Size: 150 B

Before

Width:  |  Height:  |  Size: 138 B

After

Width:  |  Height:  |  Size: 138 B

Before

Width:  |  Height:  |  Size: 147 B

After

Width:  |  Height:  |  Size: 147 B

Before

Width:  |  Height:  |  Size: 157 B

After

Width:  |  Height:  |  Size: 157 B

+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "تمت إزالة هذه الرد من قبل مشرفي المنتدى", "this_reply_was_removed": "تمت إزالة هذه الرد من قبل مشرفي المنتدى",
"not_mod_of_any_board": "أنت لست مشرفًا في أي لوحة.", "not_mod_of_any_board": "أنت لست مشرفًا في أي لوحة.",
"filters_and_highlights": "الفلاتر والإبرازات", "filters_and_highlights": "الفلاتر والإبرازات",
"search_results_for": "نتائج البحث عن" "search_results_for": "نتائج البحث عن",
"help": "مساعدة",
"filter_and_highlights_help": "مساعدة الفلاتر والإبرازات",
"filter_and_highlights": "التصفية والتسليط الضوء"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "এই প্রতিক্রিয়া বোর্ড পরিচালকদের দ্বারা সরানো হয়েছে", "this_reply_was_removed": "এই প্রতিক্রিয়া বোর্ড পরিচালকদের দ্বারা সরানো হয়েছে",
"not_mod_of_any_board": "আপনি কোনও বোর্ডের মড নন।", "not_mod_of_any_board": "আপনি কোনও বোর্ডের মড নন।",
"filters_and_highlights": "ফিল্টার এবং হাইলাইট", "filters_and_highlights": "ফিল্টার এবং হাইলাইট",
"search_results_for": "অনুসন্ধান ফলাফল" "search_results_for": "অনুসন্ধান ফলাফল",
"help": "সাহায্য",
"filter_and_highlights_help": "ফিল্টার এবং হাইলাইট সাহায্য",
"filter_and_highlights": "ফিল্টার এবং হাইলাইট"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "Tato odpověď byla odstraněna moderátory fóra", "this_reply_was_removed": "Tato odpověď byla odstraněna moderátory fóra",
"not_mod_of_any_board": "Nejste moderátor žádné desky.", "not_mod_of_any_board": "Nejste moderátor žádné desky.",
"filters_and_highlights": "Filtry a zvýraznění", "filters_and_highlights": "Filtry a zvýraznění",
"search_results_for": "Výsledky hledání pro" "search_results_for": "Výsledky hledání pro",
"help": "Pomoc",
"filter_and_highlights_help": "Pomoc s filtry a zvýrazněním",
"filter_and_highlights": "Filtry a zvýraznění"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "Dette svar blev fjernet af forummoderatorer", "this_reply_was_removed": "Dette svar blev fjernet af forummoderatorer",
"not_mod_of_any_board": "Du er ikke moderator for nogen tavler.", "not_mod_of_any_board": "Du er ikke moderator for nogen tavler.",
"filters_and_highlights": "Filtre og fremhævninger", "filters_and_highlights": "Filtre og fremhævninger",
"search_results_for": "Søgeresultater for" "search_results_for": "Søgeresultater for",
"help": "Hjælp",
"filter_and_highlights_help": "Filtre og fremhævelser Hjælp",
"filter_and_highlights": "Filtre og fremhævninger"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "Diese Antwort wurde von den Forenmoderatoren entfernt", "this_reply_was_removed": "Diese Antwort wurde von den Forenmoderatoren entfernt",
"not_mod_of_any_board": "Du bist kein Moderator eines Boards.", "not_mod_of_any_board": "Du bist kein Moderator eines Boards.",
"filters_and_highlights": "Filter & Highlights", "filters_and_highlights": "Filter & Highlights",
"search_results_for": "Suchergebnisse für" "search_results_for": "Suchergebnisse für",
"help": "Hilfe",
"filter_and_highlights_help": "Filter- und Markierungs-Hilfe",
"filter_and_highlights": "Filter und Hervorhebungen"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "Η απάντηση αυτή αφαιρέθηκε από τους διαχειριστές του φόρουμ", "this_reply_was_removed": "Η απάντηση αυτή αφαιρέθηκε από τους διαχειριστές του φόρουμ",
"not_mod_of_any_board": "Δεν είστε διαχειριστής σε καμία κοινότητα.", "not_mod_of_any_board": "Δεν είστε διαχειριστής σε καμία κοινότητα.",
"filters_and_highlights": "Φίλτρα και επισημάνσεις", "filters_and_highlights": "Φίλτρα και επισημάνσεις",
"search_results_for": "Αποτελέσματα αναζήτησης για" "search_results_for": "Αποτελέσματα αναζήτησης για",
"help": "Βοήθεια",
"filter_and_highlights_help": "Βοήθεια φίλτρων και επισήμανσης",
"filter_and_highlights": "Φίλτρα και Επισημάνσεις"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "This reply was removed by board moderators", "this_reply_was_removed": "This reply was removed by board moderators",
"not_mod_of_any_board": "You are not a mod of any board.", "not_mod_of_any_board": "You are not a mod of any board.",
"filters_and_highlights": "Filters & Highlights", "filters_and_highlights": "Filters & Highlights",
"search_results_for": "Search results for" "search_results_for": "Search results for",
"help": "Help",
"filter_and_highlights_help": "Filters & Highlights Help",
"filter_and_highlights": "Filters & Highlights"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "Esta respuesta fue eliminada por los moderadores del foro", "this_reply_was_removed": "Esta respuesta fue eliminada por los moderadores del foro",
"not_mod_of_any_board": "No eres moderador de ningún tablero.", "not_mod_of_any_board": "No eres moderador de ningún tablero.",
"filters_and_highlights": "Filtros y resaltar", "filters_and_highlights": "Filtros y resaltar",
"search_results_for": "Resultados de búsqueda para" "search_results_for": "Resultados de búsqueda para",
"help": "Ayuda",
"filter_and_highlights_help": "Ayuda de filtros y resaltes",
"filter_and_highlights": "Filtros y resaltados"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "این پاسخ توسط مدیران انجمن حذف شد", "this_reply_was_removed": "این پاسخ توسط مدیران انجمن حذف شد",
"not_mod_of_any_board": "شما هیچ تابلو مدیر نیستید.", "not_mod_of_any_board": "شما هیچ تابلو مدیر نیستید.",
"filters_and_highlights": "فیلترها و هایلایت‌ها", "filters_and_highlights": "فیلترها و هایلایت‌ها",
"search_results_for": "نتایج جستجو برای" "search_results_for": "نتایج جستجو برای",
"help": "کمک",
"filter_and_highlights_help": "کمک فیلترها و برجسته‌ها",
"filter_and_highlights": "فیلترها و برجسته‌ها"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "Tämä vastaus poistettiin foorumin moderaattoreiden toimesta", "this_reply_was_removed": "Tämä vastaus poistettiin foorumin moderaattoreiden toimesta",
"not_mod_of_any_board": "Et ole moderaattori missään taulussa.", "not_mod_of_any_board": "Et ole moderaattori missään taulussa.",
"filters_and_highlights": "Suodattimet ja korostukset", "filters_and_highlights": "Suodattimet ja korostukset",
"search_results_for": "Hakutulokset hakusanalle" "search_results_for": "Hakutulokset hakusanalle",
"help": "Apua",
"filter_and_highlights_help": "Suodattimet ja korostukset -apu",
"filter_and_highlights": "Suodattimet ja korostukset"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "Ang sagot na ito ay tinanggal ng mga moderator ng board", "this_reply_was_removed": "Ang sagot na ito ay tinanggal ng mga moderator ng board",
"not_mod_of_any_board": "Hindi ka mod ng anumang board.", "not_mod_of_any_board": "Hindi ka mod ng anumang board.",
"filters_and_highlights": "Mga filter at highlight", "filters_and_highlights": "Mga filter at highlight",
"search_results_for": "Mga resulta ng paghahanap para sa" "search_results_for": "Mga resulta ng paghahanap para sa",
"help": "Tulong",
"filter_and_highlights_help": "Tulong sa mga filter at highlight",
"filter_and_highlights": "Mga filter at highlight"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "Cette réponse a été supprimée par les modérateurs du forum", "this_reply_was_removed": "Cette réponse a été supprimée par les modérateurs du forum",
"not_mod_of_any_board": "Vous n'êtes pas modérateur de n'aucun forum.", "not_mod_of_any_board": "Vous n'êtes pas modérateur de n'aucun forum.",
"filters_and_highlights": "Filtres et mises en évidence", "filters_and_highlights": "Filtres et mises en évidence",
"search_results_for": "Résultats de recherche pour" "search_results_for": "Résultats de recherche pour",
"help": "Aide",
"filter_and_highlights_help": "Aide sur les filtres et surlignages",
"filter_and_highlights": "Filtres et surlignages"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "תשובה זו הוסרה על ידי מנהלי הפורום", "this_reply_was_removed": "תשובה זו הוסרה על ידי מנהלי הפורום",
"not_mod_of_any_board": "אתה לא מוד בכל לוח.", "not_mod_of_any_board": "אתה לא מוד בכל לוח.",
"filters_and_highlights": "סינונים והדגשות", "filters_and_highlights": "סינונים והדגשות",
"search_results_for": "תוצאות חיפוש עבור" "search_results_for": "תוצאות חיפוש עבור",
"help": "עזרה",
"filter_and_highlights_help": "עזרה במסננים והדגשות",
"filter_and_highlights": "מסננים והדגשות"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "यह उत्तर बोर्ड मॉडरेटर द्वारा हटा दिया गया था", "this_reply_was_removed": "यह उत्तर बोर्ड मॉडरेटर द्वारा हटा दिया गया था",
"not_mod_of_any_board": "आप किसी बोर्ड के मॉड नहीं हैं।", "not_mod_of_any_board": "आप किसी बोर्ड के मॉड नहीं हैं।",
"filters_and_highlights": "फ़िल्टर और हाइलाइट्स", "filters_and_highlights": "फ़िल्टर और हाइलाइट्स",
"search_results_for": "के लिए खोज परिणाम" "search_results_for": "के लिए खोज परिणाम",
"help": "मदद",
"filter_and_highlights_help": "फ़िल्टर और हाइलाइट्स सहायता",
"filter_and_highlights": "फ़िल्टर और हाइलाइट्स"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "Ez a válasz törlésre került a fórum moderátorai által", "this_reply_was_removed": "Ez a válasz törlésre került a fórum moderátorai által",
"not_mod_of_any_board": "Nem vagy moderátor egyetlen táblán sem.", "not_mod_of_any_board": "Nem vagy moderátor egyetlen táblán sem.",
"filters_and_highlights": "Szűrők és kiemelések", "filters_and_highlights": "Szűrők és kiemelések",
"search_results_for": "Keresési eredmények" "search_results_for": "Keresési eredmények",
"help": "Segítség",
"filter_and_highlights_help": "Szűrők és kiemelések súgó",
"filter_and_highlights": "Szűrők és kiemelések"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "Balasan ini dihapus oleh moderator papan", "this_reply_was_removed": "Balasan ini dihapus oleh moderator papan",
"not_mod_of_any_board": "Anda bukan mod di papan manapun.", "not_mod_of_any_board": "Anda bukan mod di papan manapun.",
"filters_and_highlights": "Filter dan Sorotan", "filters_and_highlights": "Filter dan Sorotan",
"search_results_for": "Hasil pencarian untuk" "search_results_for": "Hasil pencarian untuk",
"help": "Bantuan",
"filter_and_highlights_help": "Bantuan Filter dan Sorotan",
"filter_and_highlights": "Filter dan Sorotan"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "Questa risposta è stata rimossa dai moderatori del forum", "this_reply_was_removed": "Questa risposta è stata rimossa dai moderatori del forum",
"not_mod_of_any_board": "Non sei un mod di nessuna board.", "not_mod_of_any_board": "Non sei un mod di nessuna board.",
"filters_and_highlights": "Filtri e Evidenziazioni", "filters_and_highlights": "Filtri e Evidenziazioni",
"search_results_for": "Risultati di ricerca per" "search_results_for": "Risultati di ricerca per",
"help": "Aiuto",
"filter_and_highlights_help": "Aiuto su Filtri e Evidenziazioni",
"filter_and_highlights": "Filtri e Evidenziazioni"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "この返信は掲示板のモデレーターによって削除されました", "this_reply_was_removed": "この返信は掲示板のモデレーターによって削除されました",
"not_mod_of_any_board": "あなたはどのボードのモデレーターでもありません。", "not_mod_of_any_board": "あなたはどのボードのモデレーターでもありません。",
"filters_and_highlights": "フィルターとハイライト", "filters_and_highlights": "フィルターとハイライト",
"search_results_for": "検索結果" "search_results_for": "検索結果",
"help": "ヘルプ",
"filter_and_highlights_help": "フィルターとハイライトのヘルプ",
"filter_and_highlights": "フィルターとハイライト"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "이 답글은 게시판 관리자에 의해 삭제되었습니다", "this_reply_was_removed": "이 답글은 게시판 관리자에 의해 삭제되었습니다",
"not_mod_of_any_board": "당신은 어떤 게시판의 관리자도 아닙니다.", "not_mod_of_any_board": "당신은 어떤 게시판의 관리자도 아닙니다.",
"filters_and_highlights": "필터 및 하이라이트", "filters_and_highlights": "필터 및 하이라이트",
"search_results_for": "검색 결과" "search_results_for": "검색 결과",
"help": "도움말",
"filter_and_highlights_help": "필터 및 하이라이트 도움말",
"filter_and_highlights": "필터 및 하이라이트"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "या उत्तराला बोर्ड मॉडरेटरने काढले आहे", "this_reply_was_removed": "या उत्तराला बोर्ड मॉडरेटरने काढले आहे",
"not_mod_of_any_board": "तुम्ही कोणत्याही बोर्डचे मॉड नाहीत.", "not_mod_of_any_board": "तुम्ही कोणत्याही बोर्डचे मॉड नाहीत.",
"filters_and_highlights": "फिल्टर आणि हायलाइट्स", "filters_and_highlights": "फिल्टर आणि हायलाइट्स",
"search_results_for": "सर्च परिणाम" "search_results_for": "सर्च परिणाम",
"help": "मदत",
"filter_and_highlights_help": "फिल्टर आणि हायलाइट्स सहाय्य",
"filter_and_highlights": "फिल्टर आणि हायलाइट्स"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "Dit antwoord is verwijderd door de forum moderators", "this_reply_was_removed": "Dit antwoord is verwijderd door de forum moderators",
"not_mod_of_any_board": "Je bent geen mod van een bord.", "not_mod_of_any_board": "Je bent geen mod van een bord.",
"filters_and_highlights": "Filters en markeringen", "filters_and_highlights": "Filters en markeringen",
"search_results_for": "Zoekresultaten voor" "search_results_for": "Zoekresultaten voor",
"help": "Help",
"filter_and_highlights_help": "Filters en Markeringen Help",
"filter_and_highlights": "Filters en markeringen"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "Dette svaret ble fjernet av forummoderatorer", "this_reply_was_removed": "Dette svaret ble fjernet av forummoderatorer",
"not_mod_of_any_board": "Du er ikke moderator på noen brett.", "not_mod_of_any_board": "Du er ikke moderator på noen brett.",
"filters_and_highlights": "Filtre og fremhevinger", "filters_and_highlights": "Filtre og fremhevinger",
"search_results_for": "Søkeresultater for" "search_results_for": "Søkeresultater for",
"help": "Hjelp",
"filter_and_highlights_help": "Filtre og fremhevinger Hjelp",
"filter_and_highlights": "Filtre og uthevinger"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "Ta odpowiedź została usunięta przez moderatorów forum", "this_reply_was_removed": "Ta odpowiedź została usunięta przez moderatorów forum",
"not_mod_of_any_board": "Nie jesteś moderatorem żadnej tablicy.", "not_mod_of_any_board": "Nie jesteś moderatorem żadnej tablicy.",
"filters_and_highlights": "Filtry i wyróżnienia", "filters_and_highlights": "Filtry i wyróżnienia",
"search_results_for": "Wyniki wyszukiwania dla" "search_results_for": "Wyniki wyszukiwania dla",
"help": "Pomoc",
"filter_and_highlights_help": "Pomoc dotycząca filtrów i wyróżnień",
"filter_and_highlights": "Filtry i wyróżnienia"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "Esta resposta foi removida pelos moderadores do fórum", "this_reply_was_removed": "Esta resposta foi removida pelos moderadores do fórum",
"not_mod_of_any_board": "Você não é um moderador de nenhum quadro.", "not_mod_of_any_board": "Você não é um moderador de nenhum quadro.",
"filters_and_highlights": "Filtros e Destaques", "filters_and_highlights": "Filtros e Destaques",
"search_results_for": "Resultados de pesquisa para" "search_results_for": "Resultados de pesquisa para",
"help": "Ajuda",
"filter_and_highlights_help": "Ajuda de Filtros e Realces",
"filter_and_highlights": "Filtros e Destaques"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "Acest răspuns a fost eliminat de moderatorii forumului", "this_reply_was_removed": "Acest răspuns a fost eliminat de moderatorii forumului",
"not_mod_of_any_board": "Nu ești moderator pe niciun tablou.", "not_mod_of_any_board": "Nu ești moderator pe niciun tablou.",
"filters_and_highlights": "Filtre și evidențiere", "filters_and_highlights": "Filtre și evidențiere",
"search_results_for": "Rezultatele căutării pentru" "search_results_for": "Rezultatele căutării pentru",
"help": "Ajutor",
"filter_and_highlights_help": "Ajutor pentru filtre și evidențieri",
"filter_and_highlights": "Filtre și evidențieri"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "Этот ответ был удален модераторами форума", "this_reply_was_removed": "Этот ответ был удален модераторами форума",
"not_mod_of_any_board": "Вы не модератор ни одной доски.", "not_mod_of_any_board": "Вы не модератор ни одной доски.",
"filters_and_highlights": "Фильтры и выделения", "filters_and_highlights": "Фильтры и выделения",
"search_results_for": "Результаты поиска для" "search_results_for": "Результаты поиска для",
"help": "Помощь",
"filter_and_highlights_help": "Помощь с фильтрами и выделениями",
"filter_and_highlights": "Фильтры и выделения"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "Ky përgjigje u hoq nga moderatorët e forumit", "this_reply_was_removed": "Ky përgjigje u hoq nga moderatorët e forumit",
"not_mod_of_any_board": "Nuk jeni mod në asnjë bord.", "not_mod_of_any_board": "Nuk jeni mod në asnjë bord.",
"filters_and_highlights": "Filtra dhe theksime", "filters_and_highlights": "Filtra dhe theksime",
"search_results_for": "Rezultatet e kërkimit për" "search_results_for": "Rezultatet e kërkimit për",
"help": "Ndihmë",
"filter_and_highlights_help": "Ndihma për Filtro & Thekso",
"filter_and_highlights": "Filtra dhe thekson"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "Detta svar togs bort av forumets moderatorer", "this_reply_was_removed": "Detta svar togs bort av forumets moderatorer",
"not_mod_of_any_board": "Du är inte moderator på någon tavla.", "not_mod_of_any_board": "Du är inte moderator på någon tavla.",
"filters_and_highlights": "Filter och markeringar", "filters_and_highlights": "Filter och markeringar",
"search_results_for": "Sökresultat för" "search_results_for": "Sökresultat för",
"help": "Hjälp",
"filter_and_highlights_help": "Filter och markeringar Hjälp",
"filter_and_highlights": "Filter och markeringar"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "ఈ స్పందన బోర్డ్ మోడరేటర్లచే తొలగించబడింది", "this_reply_was_removed": "ఈ స్పందన బోర్డ్ మోడరేటర్లచే తొలగించబడింది",
"not_mod_of_any_board": "మీరు ఎటువంటి బోర్డుకు మోడ్ కంటే లేరు.", "not_mod_of_any_board": "మీరు ఎటువంటి బోర్డుకు మోడ్ కంటే లేరు.",
"filters_and_highlights": "ఫిల్టర్లు మరియు హైలైట్‌లు", "filters_and_highlights": "ఫిల్టర్లు మరియు హైలైట్‌లు",
"search_results_for": "శోధన ఫలితాలు" "search_results_for": "శోధన ఫలితాలు",
"help": "సహాయం",
"filter_and_highlights_help": "ఫిల్టర్లు మరియు హైలైట్ సహాయం",
"filter_and_highlights": "ఫిల్టర్లు మరియు హైలైట్స్"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "คำตอบนี้ถูกลบโดยผู้ดูแลบอร์ด", "this_reply_was_removed": "คำตอบนี้ถูกลบโดยผู้ดูแลบอร์ด",
"not_mod_of_any_board": "คุณไม่ใช่ผู้ดูแลบอร์ดใดๆ", "not_mod_of_any_board": "คุณไม่ใช่ผู้ดูแลบอร์ดใดๆ",
"filters_and_highlights": "ตัวกรองและไฮไลท์", "filters_and_highlights": "ตัวกรองและไฮไลท์",
"search_results_for": "ผลการค้นหาสำหรับ" "search_results_for": "ผลการค้นหาสำหรับ",
"help": "ช่วยเหลือ",
"filter_and_highlights_help": "ตัวกรองและการเน้นความช่วยเหลือ",
"filter_and_highlights": "ตัวกรองและไฮไลท์"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "Bu yanıt, forum moderatörleri tarafından kaldırıldı", "this_reply_was_removed": "Bu yanıt, forum moderatörleri tarafından kaldırıldı",
"not_mod_of_any_board": "Hiçbir tahtanın moderatörü değilsiniz.", "not_mod_of_any_board": "Hiçbir tahtanın moderatörü değilsiniz.",
"filters_and_highlights": "Filtreler ve Vurgular", "filters_and_highlights": "Filtreler ve Vurgular",
"search_results_for": "Arama sonuçları için" "search_results_for": "Arama sonuçları için",
"help": "Yardım",
"filter_and_highlights_help": "Filtreler ve Vurgular Yardımı",
"filter_and_highlights": "Filtreler ve Vurgular"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "Цю відповідь було видалено модераторами форуму", "this_reply_was_removed": "Цю відповідь було видалено модераторами форуму",
"not_mod_of_any_board": "Ви не модератор жодної дошки.", "not_mod_of_any_board": "Ви не модератор жодної дошки.",
"filters_and_highlights": "Фільтри та виділення", "filters_and_highlights": "Фільтри та виділення",
"search_results_for": "Результати пошуку для" "search_results_for": "Результати пошуку для",
"help": "Допомога",
"filter_and_highlights_help": "Допомога з фільтрами та підсвічуваннями",
"filter_and_highlights": "Фільтри та виділення"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "اس جواب کو بورڈ کے منتظمین نے حذف کر دیا ہے", "this_reply_was_removed": "اس جواب کو بورڈ کے منتظمین نے حذف کر دیا ہے",
"not_mod_of_any_board": "آپ کسی بورڈ کے موڈ نہیں ہیں۔", "not_mod_of_any_board": "آپ کسی بورڈ کے موڈ نہیں ہیں۔",
"filters_and_highlights": "فلٹرز اور ہائیلائٹس", "filters_and_highlights": "فلٹرز اور ہائیلائٹس",
"search_results_for": "تلاش کے نتائج" "search_results_for": "تلاش کے نتائج",
"help": "مدد",
"filter_and_highlights_help": "فلٹرز اور ہائی لائٹس کی مدد",
"filter_and_highlights": "فلٹرز اور ہائلाइटس"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "Phản hồi này đã bị xóa bởi các quản trị viên diễn đàn", "this_reply_was_removed": "Phản hồi này đã bị xóa bởi các quản trị viên diễn đàn",
"not_mod_of_any_board": "Bạn không phải là quản trị viên của bất kỳ bảng nào.", "not_mod_of_any_board": "Bạn không phải là quản trị viên của bất kỳ bảng nào.",
"filters_and_highlights": "Bộ lọc và Nổi bật", "filters_and_highlights": "Bộ lọc và Nổi bật",
"search_results_for": "Kết quả tìm kiếm cho" "search_results_for": "Kết quả tìm kiếm cho",
"help": "Trợ giúp",
"filter_and_highlights_help": "Trợ giúp bộ lọc và đánh dấu",
"filter_and_highlights": "Bộ lọc và Nổi bật"
} }
+4 -1
View File
@@ -293,5 +293,8 @@
"this_reply_was_removed": "该回复已被版主删除", "this_reply_was_removed": "该回复已被版主删除",
"not_mod_of_any_board": "您不是任何板块的管理员。", "not_mod_of_any_board": "您不是任何板块的管理员。",
"filters_and_highlights": "筛选器和高亮", "filters_and_highlights": "筛选器和高亮",
"search_results_for": "搜索结果" "search_results_for": "搜索结果",
"help": "帮助",
"filter_and_highlights_help": "过滤器和高亮帮助",
"filter_and_highlights": "筛选器与高亮"
} }
@@ -33,7 +33,7 @@
.modal { .modal {
top: 60px; top: 60px;
left: 50%; left: calc(50% + 5px);
transform: translateX(-50%); transform: translateX(-50%);
width: 380px; width: 380px;
height: auto; height: auto;
@@ -41,14 +41,23 @@
overflow-y: auto; overflow-y: auto;
position: fixed; position: fixed;
z-index: 5; z-index: 5;
padding: 2px; padding: 2px 5px 5px 5px;
font-size: var(--settings-modal-font-size); font-size: var(--settings-modal-font-size);
box-shadow: 0 0 5px rgba(0, 0, 0, 0.25); box-shadow: 0 0 5px rgba(0, 0, 0, 0.25);
background-color: var(--settings-modal-background-color); background-color: var(--settings-modal-background-color);
border-top: var(--settings-modal-border-top); }
border-right: var(--settings-modal-border-right);
border-bottom: var(--settings-modal-border-bottom); .filtersProtipModal {
border-left: var(--settings-modal-border-left); top: 50px;
width: 570px;
left: 50%;
padding: 2px 10px 5px 10px;
}
@media (max-width: 614px) {
.filtersProtipModal {
width: 90vw;
}
} }
@media (max-width: 420px) { @media (max-width: 420px) {
@@ -82,6 +91,54 @@
top: 5px; top: 5px;
image-rendering: pixelated; image-rendering: pixelated;
background-image: var(--settings-modal-close-button-background-image); background-image: var(--settings-modal-close-button-background-image);
cursor: pointer;
}
.openHelpButton {
right: 23px;
width: 18px;
height: 18px;
display: block;
background-size: 100%;
position: absolute;
top: 5px;
image-rendering: pixelated;
background-image: var(--open-help-button-background-image);
cursor: pointer;
}
.filtersProtip {
text-transform: none;
}
.filtersProtip code {
padding: 1px 5px 1px 5px;
background-color: #EEE;
color: #000;
}
.filtersProtip li {
list-style: none;
margin: 5px 0px;
}
.filtersProtip ul {
margin-bottom: 10px;
}
.filtersProtip h4 {
font-size: 15px;
margin: 10px 0px 5px 0px;
padding-top: 10px;
}
.filtersProtip h4:first-child {
padding-top: 0px;
}
.filtersProtip h4::before {
content: "»";
margin-right: 3px;
} }
.filters { .filters {
@@ -108,16 +165,12 @@
padding-bottom: 5px; padding-bottom: 5px;
} }
.saveButton {
margin-left: 5px;
}
.filtersTable { .filtersTable {
width: 100%; width: 100%;
border-spacing: 1px; border-spacing: 1px;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
padding: 10px 2px 2px 2px; padding: 0px 2px 2px 2px;
} }
.filtersTable th { .filtersTable th {
@@ -168,7 +221,8 @@
} }
.saveButton { .saveButton {
float: right; position: absolute;
right: 5px;
} }
.filterHits { .filterHits {
@@ -1,11 +1,13 @@
import { useState, useCallback, useRef, useEffect } from 'react'; import { useState, useCallback, useRef, useEffect } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import useCatalogFiltersStore from '../../stores/use-catalog-filters-store'; import useCatalogFiltersStore from '../../stores/use-catalog-filters-store';
import useFeedResetStore from '../../stores/use-feed-reset-store';
import styles from './catalog-filters.module.css'; import styles from './catalog-filters.module.css';
const FiltersTable = ({ onSave }: { onSave: () => void }) => { const FiltersTable = ({ onSave }: { onSave: () => void }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { filterItems, saveAndApplyFilters, currentSubplebbitAddress } = useCatalogFiltersStore(); const { filterItems, saveAndApplyFilters, currentSubplebbitAddress } = useCatalogFiltersStore();
const resetFeed = useFeedResetStore((state) => state.reset);
const [localFilterItems, setLocalFilterItems] = useState( const [localFilterItems, setLocalFilterItems] = useState(
filterItems.map((item) => ({ filterItems.map((item) => ({
@@ -52,9 +54,17 @@ const FiltersTable = ({ onSave }: { onSave: () => void }) => {
const handleSave = useCallback(() => { const handleSave = useCallback(() => {
const nonEmptyFilters = localFilterItems.filter((item) => item.text.trim() !== ''); const nonEmptyFilters = localFilterItems.filter((item) => item.text.trim() !== '');
saveAndApplyFilters(nonEmptyFilters); saveAndApplyFilters(nonEmptyFilters);
useCatalogFiltersStore.getState().resetCountsForCurrentSubplebbit();
if (resetFeed) {
resetFeed();
}
onSave(); onSave();
}, [saveAndApplyFilters, localFilterItems, onSave]); }, [saveAndApplyFilters, localFilterItems, onSave, resetFeed]);
const handleKeyDown = useCallback( const handleKeyDown = useCallback(
(e: KeyboardEvent) => { (e: KeyboardEvent) => {
@@ -142,7 +152,7 @@ const FiltersTable = ({ onSave }: { onSave: () => void }) => {
</span> </span>
</td> </td>
<td className={styles.filterHits}> <td className={styles.filterHits}>
{currentSubplebbitAddress && (item.subplebbitCounts?.get(currentSubplebbitAddress) ?? 0) > 0 && `x${item.subplebbitCounts?.get(currentSubplebbitAddress)}`} {currentSubplebbitAddress && item.subplebbitFilteredCids?.has(currentSubplebbitAddress) && `x${item.subplebbitCounts?.get(currentSubplebbitAddress) ?? 0}`}
</td> </td>
</tr> </tr>
))} ))}
@@ -163,18 +173,107 @@ const FiltersTable = ({ onSave }: { onSave: () => void }) => {
); );
}; };
const FiltersProtip = () => {
return (
<div className={styles.filtersProtip}>
<h4>Patterns</h4>
<ul>
<li>
<strong>Matching whole words:</strong>
</li>
<li>
<code>feel</code> will match <em>"feel"</em> but not <em>"feeling"</em>. This search is case-insensitive.
</li>
</ul>
<ul>
<li>
<strong>AND operator:</strong>
</li>
<li>
<code>feel girlfriend</code> will match <em>"feel"</em> AND <em>"girlfriend"</em> in any order.
</li>
</ul>
<ul>
<li>
<strong>OR operator:</strong>
</li>
<li>
<code>feel|girlfriend</code> will match <em>"feel"</em> OR <em>"girlfriend"</em>.
</li>
</ul>
<ul>
<li>
<strong>Mixing both operators:</strong>
</li>
<li>
<code>girlfriend|boyfriend feel</code> matches <em>"feel"</em> AND <em>"girlfriend"</em>, or <em>"feel"</em> AND <em>"boyfriend"</em>.
</li>
</ul>
<ul>
<li>
<strong>Exact match search:</strong>
</li>
<li>
<code>"that feel when"</code> place double quotes around the pattern to search for an exact string.
</li>
</ul>
<ul>
<li>
<strong>Wildcards:</strong>
</li>
<li>
<code>feel*</code> matches expressions such as <em>"feel"</em>, <em>"feels"</em>, <em>"feeling"</em>, <em>"feeler"</em>, etc
</li>
<li>
<code>idolm*ster</code> this can match <em>"idolmaster"</em> or <em>"idolm@ster"</em>, etc
</li>
</ul>
<ul>
<strong>It is also possible to filter by regular expression:</strong>
<li>
<code>/^(?=.*detachable)(?=.*hats).*$/i</code> AND operator.
</li>
<li>
<code>/^(?!.*touhou).*$/i</code> NOT operator.
</li>
<li>
<code>{'/^&gt;/'}</code> threads starting with a quote (<em>{'">"'}</em> character as an html entity).
</li>
<li>
<code>/^$/</code> threads with no text.
</li>
</ul>
<h4>Controls</h4>
<ul>
<li>
<strong>On</strong> enables or disables the filter.
</li>
<li>
<strong>Hide</strong> hides matched threads.
</li>
<li>
<strong>Top</strong> moves the filter to the top of the feed.
</li>
</ul>
</div>
);
};
const FiltersModal = ({ closeModal }: { closeModal: () => void }) => { const FiltersModal = ({ closeModal }: { closeModal: () => void }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [showHelp, setShowHelp] = useState(false);
const openHelp = () => setShowHelp(true);
const closeHelp = () => setShowHelp(false);
return ( return (
<> <>
<div className={styles.overlay} onClick={closeModal} /> <div className={styles.overlay} onClick={showHelp ? closeHelp : closeModal} />
<div className={styles.modal}> <div className={`${styles.modal} ${showHelp ? styles.filtersProtipModal : ''}`}>
<div className={styles.header}> <div className={styles.header}>
<span className={styles.title}>{t('filters_and_highlights')}</span> <span className={styles.title}>{showHelp ? t('filter_and_highlights_help') : t('filter_and_highlights')}</span>
<span className={styles.closeButton} title='close' onClick={closeModal} /> {!showHelp && <span className={styles.openHelpButton} title={t('help')} onClick={openHelp} />}
<span className={styles.closeButton} title={t('close')} onClick={closeModal} />
</div> </div>
<FiltersTable onSave={closeModal} /> {showHelp ? <FiltersProtip /> : <FiltersTable onSave={closeModal} />}
</div> </div>
</> </>
); );
+110
View File
@@ -0,0 +1,110 @@
import { Comment } from '@plebbit/plebbit-react-hooks';
/**
* Checks if a text matches a pattern according to various pattern matching rules:
* - Whole word matching: 'feel' matches 'feel' but not 'feeling'
* - AND operator: 'feel girlfriend' matches both words in any order
* - OR operator: 'feel|girlfriend' matches either word
* - Mixed operators: 'girlfriend|boyfriend feel' matches complex logic
* - Exact match: "that feel when" matches the exact phrase
* - Wildcards: 'feel*' matches 'feel', 'feels', 'feeling', etc.
* - Regex: /pattern/flags for regular expressions
*
* @param text The text to check against the pattern
* @param pattern The pattern to match
* @returns True if the text matches the pattern, false otherwise
*/
export const matchesPattern = (text: string, pattern: string): boolean => {
if (!pattern || !text) return false;
const textLower = text.toLowerCase();
try {
// Check if it's a regex pattern (starts and ends with /)
if (pattern.startsWith('/') && pattern.length > 2 && /\/[gimsuy]*$/.test(pattern)) {
const lastSlashIndex = pattern.lastIndexOf('/');
const regexPattern = pattern.substring(1, lastSlashIndex);
const flags = pattern.substring(lastSlashIndex + 1);
const regex = new RegExp(regexPattern, flags);
return regex.test(textLower);
}
// Check if it's an exact match pattern (surrounded by quotes)
else if (pattern.startsWith('"') && pattern.endsWith('"') && pattern.length > 2) {
const exactPattern = pattern.substring(1, pattern.length - 1).toLowerCase();
return textLower.includes(exactPattern);
}
// Check for OR patterns (contains |)
else if (pattern.includes('|')) {
const orGroups = pattern.split(' ').filter(Boolean);
// Process each group (which might contain OR operators)
return orGroups.every((group) => {
const orTerms = group.split('|').map((term) => term.toLowerCase());
return orTerms.some((term) => {
// Handle wildcards in OR terms
if (term.includes('*')) {
const regexPattern = term.replace(/\*/g, '.*').toLowerCase();
const regex = new RegExp(`\\b${regexPattern}\\b`, 'i');
return regex.test(textLower);
} else {
// Match whole word only
const regex = new RegExp(`\\b${term}\\b`, 'i');
return regex.test(textLower);
}
});
});
}
// Check for AND patterns (space-separated words)
else if (pattern.includes(' ')) {
const andTerms = pattern
.split(' ')
.filter(Boolean)
.map((term) => term.toLowerCase());
return andTerms.every((term) => {
// Handle wildcards in AND terms
if (term.includes('*')) {
const regexPattern = term.replace(/\*/g, '.*').toLowerCase();
const regex = new RegExp(`\\b${regexPattern}\\b`, 'i');
return regex.test(textLower);
} else {
// Match whole word only
const regex = new RegExp(`\\b${term}\\b`, 'i');
return regex.test(textLower);
}
});
}
// Handle wildcard patterns
else if (pattern.includes('*')) {
const regexPattern = pattern.replace(/\*/g, '.*').toLowerCase();
const regex = new RegExp(`\\b${regexPattern}\\b`, 'i');
return regex.test(textLower);
}
// Simple whole word match
else {
const regex = new RegExp(`\\b${pattern.toLowerCase()}\\b`, 'i');
return regex.test(textLower);
}
} catch (error) {
// If regex parsing fails, fall back to simple includes
console.error('Pattern matching error:', error);
const simplifiedPattern = pattern.toLowerCase();
return textLower.includes(simplifiedPattern);
}
};
/**
* Checks if a comment matches a pattern in its title or content
*
* @param comment The comment to check
* @param pattern The pattern to match
* @returns True if the comment matches the pattern, false otherwise
*/
export const commentMatchesPattern = (comment: Comment, pattern: string): boolean => {
if (!pattern || !comment) return false;
const titleLower = comment?.title?.toLowerCase() || '';
const contentLower = comment?.content?.toLowerCase() || '';
const textToMatch = titleLower + ' ' + contentLower;
return matchesPattern(textToMatch, pattern);
};
+66 -11
View File
@@ -1,6 +1,7 @@
import { create } from 'zustand'; import { create } from 'zustand';
import { persist } from 'zustand/middleware'; import { persist } from 'zustand/middleware';
import { Comment } from '@plebbit/plebbit-react-hooks'; import { Comment } from '@plebbit/plebbit-react-hooks';
import { commentMatchesPattern } from '../lib/utils/pattern-utils';
interface FilterItem { interface FilterItem {
text: string; text: string;
@@ -34,6 +35,7 @@ interface CatalogFiltersStore {
searchText: string; searchText: string;
setSearchFilter: (text: string) => void; setSearchFilter: (text: string) => void;
clearSearchFilter: () => void; clearSearchFilter: () => void;
resetCountsForCurrentSubplebbit: () => void;
} }
const useCatalogFiltersStore = create( const useCatalogFiltersStore = create(
@@ -137,10 +139,39 @@ const useCatalogFiltersStore = create(
hide: item.hide ?? true, hide: item.hide ?? true,
top: item.top ?? false, top: item.top ?? false,
})); }));
// Compare new filter items with existing ones to detect pattern changes
const existingItems = get().filterItems;
const updatedItems = nonEmptyItems.map((newItem) => {
// Try to find matching existing item by index or text
const existingItem = existingItems.find((item) => item.text === newItem.text);
// If we found a matching item, keep its counts
if (existingItem) {
return {
...newItem,
count: existingItem.count,
filteredCids: existingItem.filteredCids,
subplebbitCounts: existingItem.subplebbitCounts,
subplebbitFilteredCids: existingItem.subplebbitFilteredCids,
};
}
// If pattern has changed or it's a new filter, reset all counts
return {
...newItem,
count: 0,
filteredCids: new Set<string>(),
subplebbitCounts: new Map<string, number>(),
subplebbitFilteredCids: new Map<string, Set<string>>(),
};
});
set({ set({
filterItems: nonEmptyItems, filterItems: updatedItems,
filteredCids: new Set<string>(), filteredCids: new Set<string>(),
}); });
get().recalcFilteredCount(); get().recalcFilteredCount();
get().updateFilter(); get().updateFilter();
}, },
@@ -154,11 +185,7 @@ const useCatalogFiltersStore = create(
// Apply search filter // Apply search filter
if (state.searchText.trim() !== '') { if (state.searchText.trim() !== '') {
const searchPattern = state.searchText.toLowerCase(); if (!commentMatchesPattern(comment, state.searchText)) {
const title = comment?.title?.toLowerCase() || '';
const content = comment?.content?.toLowerCase() || '';
if (!title.includes(searchPattern) && !content.includes(searchPattern)) {
return false; return false;
} }
} }
@@ -170,11 +197,7 @@ const useCatalogFiltersStore = create(
for (let i = 0; i < filterItems.length; i++) { for (let i = 0; i < filterItems.length; i++) {
const item = filterItems[i]; const item = filterItems[i];
if (item.enabled && item.text.trim() !== '') { if (item.enabled && item.text.trim() !== '') {
const pattern = item.text.toLowerCase(); if (commentMatchesPattern(comment, item.text)) {
const title = comment?.title?.toLowerCase() || '';
const content = comment?.content?.toLowerCase() || '';
if (title.includes(pattern) || content.includes(pattern)) {
// If we have a current subplebbit and this is a match, increment the count // If we have a current subplebbit and this is a match, increment the count
if (currentSubplebbit && comment.subplebbitAddress === currentSubplebbit) { if (currentSubplebbit && comment.subplebbitAddress === currentSubplebbit) {
// We need to use a timeout to avoid modifying state during a state update // We need to use a timeout to avoid modifying state during a state update
@@ -272,6 +295,38 @@ const useCatalogFiltersStore = create(
return filteredCount; return filteredCount;
}, },
resetCountsForCurrentSubplebbit: () => {
const currentSubplebbit = get().currentSubplebbitAddress;
if (!currentSubplebbit) return;
set((state) => {
const updatedFilterItems = state.filterItems.map((item) => {
const newItem = { ...item };
// Ensure maps are properly initialized
if (!newItem.subplebbitCounts || !(newItem.subplebbitCounts instanceof Map)) {
newItem.subplebbitCounts = new Map();
}
if (!newItem.subplebbitFilteredCids || !(newItem.subplebbitFilteredCids instanceof Map)) {
newItem.subplebbitFilteredCids = new Map();
}
// Reset counts for current subplebbit
newItem.subplebbitCounts.set(currentSubplebbit, 0);
newItem.subplebbitFilteredCids.set(currentSubplebbit, new Set<string>());
return newItem;
});
return {
filterItems: updatedFilterItems,
filteredCount: 0,
};
});
// Trigger filter reapplication to start counting again
get().updateFilter();
},
}), }),
{ {
name: 'catalog-filters-storage', name: 'catalog-filters-storage',
+6 -3
View File
@@ -50,6 +50,7 @@
--button-desktop-text-color: #00e; --button-desktop-text-color: #00e;
--button-desktop-text-color-hover: red; --button-desktop-text-color-hover: red;
--close-button-background-image: url("/assets/buttons/cross-red.png"); --close-button-background-image: url("/assets/buttons/cross-red.png");
--open-help-button-background-image: url("/assets/buttons/help-red.png");
/* horizontal rule */ /* horizontal rule */
--hr-border: none; --hr-border: none;
@@ -263,6 +264,7 @@
--button-desktop-text-color: #34345c; --button-desktop-text-color: #34345c;
--button-desktop-text-color-hover: #d00; --button-desktop-text-color-hover: #d00;
--close-button-background-image: url("/assets/buttons/cross-blue.png"); --close-button-background-image: url("/assets/buttons/cross-blue.png");
--open-help-button-background-image: url("/assets/buttons/help-blue.png");
/* footer */ /* footer */
--footer-item-background-color-desktop: #eef2ff; --footer-item-background-color-desktop: #eef2ff;
@@ -479,6 +481,7 @@
--button-desktop-text-color-hover: red; --button-desktop-text-color-hover: red;
--button-text-decoration: underline; --button-text-decoration: underline;
--close-button-background-image: url("/assets/buttons/cross-red.png"); --close-button-background-image: url("/assets/buttons/cross-red.png");
--open-help-button-background-image: url("/assets/buttons/help-red.png");
/* footer */ /* footer */
--footer-item-background-color-desktop: #fed; --footer-item-background-color-desktop: #fed;
@@ -657,7 +660,7 @@
--button-text-decoration: underline; --button-text-decoration: underline;
--button-desktop-text-color-hover: #d00; --button-desktop-text-color-hover: #d00;
--close-button-background-image: url("/assets/buttons/cross-blue.png"); --close-button-background-image: url("/assets/buttons/cross-blue.png");
--open-help-button-background-image: url("/assets/buttons/help-blue.png");
/* footer */ /* footer */
--footer-item-background-color-desktop: #eef2ff; --footer-item-background-color-desktop: #eef2ff;
@@ -848,7 +851,7 @@
--button-desktop-text-color: #81a2be; --button-desktop-text-color: #81a2be;
--button-desktop-text-color-hover: #5f89ac; --button-desktop-text-color-hover: #5f89ac;
--close-button-background-image: url("/assets/buttons/cross-dark.png"); --close-button-background-image: url("/assets/buttons/cross-dark.png");
--open-help-button-background-image: url("/assets/buttons/help-dark.png");
/* filter for inputs */ /* filter for inputs */
--filter80: brightness(85%); --filter80: brightness(85%);
@@ -1067,7 +1070,7 @@
--button-desktop-text-color: #f60; --button-desktop-text-color: #f60;
--button-desktop-text-color-hover: #f30; --button-desktop-text-color-hover: #f30;
--close-button-background-image: url("/assets/buttons/cross-photon.png"); --close-button-background-image: url("/assets/buttons/cross-photon.png");
--open-help-button-background-image: url("/assets/buttons/help-photon.png");
/* horizontal rule */ /* horizontal rule */
--hr-border: none; --hr-border: none;
--hr-border-top: 1px solid #ddd; --hr-border-top: 1px solid #ddd;
+5 -16
View File
@@ -18,6 +18,7 @@ import useCatalogFiltersStore from '../../stores/use-catalog-filters-store';
import CatalogRow from '../../components/catalog-row'; import CatalogRow from '../../components/catalog-row';
import LoadingEllipsis from '../../components/loading-ellipsis'; import LoadingEllipsis from '../../components/loading-ellipsis';
import styles from './catalog.module.css'; import styles from './catalog.module.css';
import { commentMatchesPattern } from '../../lib/utils/pattern-utils';
const lastVirtuosoStates: { [key: string]: StateSnapshot } = {}; const lastVirtuosoStates: { [key: string]: StateSnapshot } = {};
@@ -39,15 +40,12 @@ const createContentFilter = (
if (enabledFilters.length === 0) return true; if (enabledFilters.length === 0) return true;
const titleLower = comment?.title?.toLowerCase() || '';
const contentLower = comment?.content?.toLowerCase() || '';
// Check if any enabled filter matches the content // Check if any enabled filter matches the content
for (let i = 0; i < enabledFilters.length; i++) { for (let i = 0; i < enabledFilters.length; i++) {
const item = enabledFilters[i]; const item = enabledFilters[i];
const pattern = item.text.toLowerCase(); const pattern = item.text;
if (titleLower.includes(pattern) || contentLower.includes(pattern)) { if (commentMatchesPattern(comment, pattern)) {
// Find the original filter index to increment count // Find the original filter index to increment count
const filterIndex = filterItems.findIndex((f) => f.text === item.text && f.enabled); const filterIndex = filterItems.findIndex((f) => f.text === item.text && f.enabled);
if (filterIndex !== -1) { if (filterIndex !== -1) {
@@ -102,12 +100,7 @@ const createCombinedFilter = (
const searchFilter = { const searchFilter = {
filter: (comment: Comment) => { filter: (comment: Comment) => {
if (!searchText.trim()) return true; if (!searchText.trim()) return true;
return commentMatchesPattern(comment, searchText);
const titleLower = comment?.title?.toLowerCase() || '';
const contentLower = comment?.content?.toLowerCase() || '';
const searchPattern = searchText.toLowerCase();
return titleLower.includes(searchPattern) || contentLower.includes(searchPattern);
}, },
key: searchText ? `search-filter-${searchText}` : 'no-search-filter', key: searchText ? `search-filter-${searchText}` : 'no-search-filter',
}; };
@@ -410,13 +403,9 @@ const Catalog = () => {
combinedFeed.forEach((comment) => { combinedFeed.forEach((comment) => {
if (!comment) return; if (!comment) return;
const titleLower = comment?.title?.toLowerCase() || '';
const contentLower = comment?.content?.toLowerCase() || '';
let isTop = false; let isTop = false;
for (const filter of enabledTopFilters) { for (const filter of enabledTopFilters) {
const pattern = filter.text.toLowerCase(); if (commentMatchesPattern(comment, filter.text)) {
if (titleLower.includes(pattern) || contentLower.includes(pattern)) {
isTop = true; isTop = true;
break; break;
} }