Merge pull request #791 from plebbit/development

Development
This commit is contained in:
Tom
2025-11-20 12:21:45 +01:00
committed by GitHub
64 changed files with 373 additions and 166 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 223 B

After

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 215 B

After

Width:  |  Height:  |  Size: 215 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 B

After

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 B

After

Width:  |  Height:  |  Size: 221 B

+2 -1
View File
@@ -295,5 +295,6 @@
"help": "مساعدة",
"filter_and_highlights_help": "مساعدة الفلاتر والإبرازات",
"filter_and_highlights": "التصفية والتسليط الضوء",
"boards_you_moderate": "المنتديات التي تقوم بالإشراف عليها"
"boards_you_moderate": "المنتديات التي تقوم بالإشراف عليها",
"more_threads_last_year": "{{count}} مواضيع منذ {{currentTimeFilterName}} — [<1>عرض المزيد من المواضيع من السنة الماضية</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "সাহায্য",
"filter_and_highlights_help": "ফিল্টার এবং হাইলাইট সাহায্য",
"filter_and_highlights": "ফিল্টার এবং হাইলাইট",
"boards_you_moderate": "আপনি যে বোর্ডগুলি পরিচালনা করেন"
"boards_you_moderate": "আপনি যে বোর্ডগুলি পরিচালনা করেন",
"more_threads_last_year": "{{count}} থ্রেড {{currentTimeFilterName}} থেকে — [<1>গত বছরের আরও থ্রেড দেখান</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "Pomoc",
"filter_and_highlights_help": "Pomoc s filtry a zvýrazněním",
"filter_and_highlights": "Filtry a zvýraznění",
"boards_you_moderate": "Fóra, která moderujete"
"boards_you_moderate": "Fóra, která moderujete",
"more_threads_last_year": "{{count}} příspěvků od {{currentTimeFilterName}} — [<1>Zobrazit více příspěvků z minulého roku</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "Hjælp",
"filter_and_highlights_help": "Filtre og fremhævelser Hjælp",
"filter_and_highlights": "Filtre og fremhævninger",
"boards_you_moderate": "Boards du modererer"
"boards_you_moderate": "Boards du modererer",
"more_threads_last_year": "{{count}} tråde siden {{currentTimeFilterName}} — [<1>Vis flere tråde fra sidste år</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "Hilfe",
"filter_and_highlights_help": "Filter- und Markierungs-Hilfe",
"filter_and_highlights": "Filter und Hervorhebungen",
"boards_you_moderate": "Boards, die Sie moderieren"
"boards_you_moderate": "Boards, die Sie moderieren",
"more_threads_last_year": "{{count}} Threads seit {{currentTimeFilterName}} — [<1>Mehr Threads vom letzten Jahr anzeigen</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "Βοήθεια",
"filter_and_highlights_help": "Βοήθεια φίλτρων και επισήμανσης",
"filter_and_highlights": "Φίλτρα και Επισημάνσεις",
"boards_you_moderate": "Boards που διαχειρίζεστε"
"boards_you_moderate": "Boards που διαχειρίζεστε",
"more_threads_last_year": "{{count}} νήματα από {{currentTimeFilterName}} — [<1>Προβολή περισσότερων νημάτων από το περασμένο έτος</1>]"
}
+3 -2
View File
@@ -296,5 +296,6 @@
"help": "Help",
"filter_and_highlights_help": "Filters & Highlights Help",
"filter_and_highlights": "Filters & Highlights",
"boards_you_moderate": "boards you moderate"
}
"boards_you_moderate": "boards you moderate",
"more_threads_last_year": "{{count}} threads since {{currentTimeFilterName}} — [<1>Show More Threads from Last Year</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "Ayuda",
"filter_and_highlights_help": "Ayuda de filtros y resaltes",
"filter_and_highlights": "Filtros y resaltados",
"boards_you_moderate": "tableros que moderas"
"boards_you_moderate": "tableros que moderas",
"more_threads_last_year": "{{count}} hilos desde {{currentTimeFilterName}} — [<1>Ver más hilos del año pasado</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "کمک",
"filter_and_highlights_help": "کمک فیلترها و برجسته‌ها",
"filter_and_highlights": "فیلترها و برجسته‌ها",
"boards_you_moderate": "تابلوهایی که مدیریت می‌کنید"
"boards_you_moderate": "تابلوهایی که مدیریت می‌کنید",
"more_threads_last_year": "{{count}} پست از {{currentTimeFilterName}} — [<1>نمایش بیشتر پست‌های سال گذشته</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "Apua",
"filter_and_highlights_help": "Suodattimet ja korostukset -apu",
"filter_and_highlights": "Suodattimet ja korostukset",
"boards_you_moderate": "lautakunnat, joita valvot"
"boards_you_moderate": "lautakunnat, joita valvot",
"more_threads_last_year": "{{count}} ketjua sitten {{currentTimeFilterName}} — [<1>Näytä lisää viime vuoden ketjuja</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "Tulong",
"filter_and_highlights_help": "Tulong sa mga filter at highlight",
"filter_and_highlights": "Mga filter at highlight",
"boards_you_moderate": "mga board na iyong minomoderate"
"boards_you_moderate": "mga board na iyong minomoderate",
"more_threads_last_year": "{{count}} mga thread mula {{currentTimeFilterName}} — [<1>Ipakita ang higit pang mga thread mula noong nakaraang taon</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "Aide",
"filter_and_highlights_help": "Aide sur les filtres et surlignages",
"filter_and_highlights": "Filtres et surlignages",
"boards_you_moderate": "boards que vous modérez"
"boards_you_moderate": "boards que vous modérez",
"more_threads_last_year": "{{count}} fils depuis {{currentTimeFilterName}} — [<1>Afficher plus de fils de l'année dernière</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "עזרה",
"filter_and_highlights_help": "עזרה במסננים והדגשות",
"filter_and_highlights": "מסננים והדגשות",
"boards_you_moderate": "לוחות שאתה מפקח עליהם"
"boards_you_moderate": "לוחות שאתה מפקח עליהם",
"more_threads_last_year": "{{count}} חוטים מאז {{currentTimeFilterName}} — [<1>הצג עוד חוטים מהשנה שעברה</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "मदद",
"filter_and_highlights_help": "फ़िल्टर और हाइलाइट्स सहायता",
"filter_and_highlights": "फ़िल्टर और हाइलाइट्स",
"boards_you_moderate": "बोर्ड जो आप मॉडरेट करते हैं"
"boards_you_moderate": "बोर्ड जो आप मॉडरेट करते हैं",
"more_threads_last_year": "{{count}} थ्रेड्स {{currentTimeFilterName}} से — [<1>पिछले साल के अधिक थ्रेड्स दिखाएं</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"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",
"boards_you_moderate": "boardok, amelyeket moderálsz"
"boards_you_moderate": "boardok, amelyeket moderálsz",
"more_threads_last_year": "{{count}} szál {{currentTimeFilterName}} óta — [<1>Több szál megjelenítése a múlt évből</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "Bantuan",
"filter_and_highlights_help": "Bantuan Filter dan Sorotan",
"filter_and_highlights": "Filter dan Sorotan",
"boards_you_moderate": "board yang Anda moderasi"
"boards_you_moderate": "board yang Anda moderasi",
"more_threads_last_year": "{{count}} thread sejak {{currentTimeFilterName}} — [<1>Tampilkan lebih banyak thread dari tahun lalu</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "Aiuto",
"filter_and_highlights_help": "Aiuto su Filtri e Evidenziazioni",
"filter_and_highlights": "Filtri e Evidenziazioni",
"boards_you_moderate": "board che moderi"
"boards_you_moderate": "board che moderi",
"more_threads_last_year": "{{count}} thread da {{currentTimeFilterName}} — [<1>Mostra altri thread dell'anno scorso</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "ヘルプ",
"filter_and_highlights_help": "フィルターとハイライトのヘルプ",
"filter_and_highlights": "フィルターとハイライト",
"boards_you_moderate": "あなたが管理するボード"
"boards_you_moderate": "あなたが管理するボード",
"more_threads_last_year": "{{count}} スレッド {{currentTimeFilterName}} から — [<1>去年のスレッドをさらに表示</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "도움말",
"filter_and_highlights_help": "필터 및 하이라이트 도움말",
"filter_and_highlights": "필터 및 하이라이트",
"boards_you_moderate": "당신이 관리하는 게시판"
"boards_you_moderate": "당신이 관리하는 게시판",
"more_threads_last_year": "{{count}} 스레드 {{currentTimeFilterName}} 이후 — [<1>작년의 스레드 더 보기</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "मदत",
"filter_and_highlights_help": "फिल्टर आणि हायलाइट्स सहाय्य",
"filter_and_highlights": "फिल्टर आणि हायलाइट्स",
"boards_you_moderate": "तुम्ही ज्यांचे नियंत्रण करता अशा बोर्ड्स"
"boards_you_moderate": "तुम्ही ज्यांचे नियंत्रण करता अशा बोर्ड्स",
"more_threads_last_year": "{{count}} ध糀ं {{currentTimeFilterName}} पासून — [<1>गेल्या वर्षातील आणखी थ्रेड्स दाखवा</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "Help",
"filter_and_highlights_help": "Filters en Markeringen Help",
"filter_and_highlights": "Filters en markeringen",
"boards_you_moderate": "boards die je beheert"
"boards_you_moderate": "boards die je beheert",
"more_threads_last_year": "{{count}} threads sinds {{currentTimeFilterName}} — [<1>Meer threads van afgelopen jaar tonen</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "Hjelp",
"filter_and_highlights_help": "Filtre og fremhevinger Hjelp",
"filter_and_highlights": "Filtre og uthevinger",
"boards_you_moderate": "boards du modererer"
"boards_you_moderate": "boards du modererer",
"more_threads_last_year": "{{count}} tråder siden {{currentTimeFilterName}} — [<1>Vis flere tråder fra forrige år</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "Pomoc",
"filter_and_highlights_help": "Pomoc dotycząca filtrów i wyróżnień",
"filter_and_highlights": "Filtry i wyróżnienia",
"boards_you_moderate": "boards, które moderujesz"
"boards_you_moderate": "boards, które moderujesz",
"more_threads_last_year": "{{count}} wątki od {{currentTimeFilterName}} — [<1>Pokaż więcej wątków z zeszłego roku</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "Ajuda",
"filter_and_highlights_help": "Ajuda de Filtros e Realces",
"filter_and_highlights": "Filtros e Destaques",
"boards_you_moderate": "boards que você modera"
"boards_you_moderate": "boards que você modera",
"more_threads_last_year": "{{count}} tópicos desde {{currentTimeFilterName}} — [<1>Mostrar mais tópicos do ano passado</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "Ajutor",
"filter_and_highlights_help": "Ajutor pentru filtre și evidențieri",
"filter_and_highlights": "Filtre și evidențieri",
"boards_you_moderate": "boards pe care le moderezi"
"boards_you_moderate": "boards pe care le moderezi",
"more_threads_last_year": "{{count}} subiecte din {{currentTimeFilterName}} — [<1>Afișați mai multe subiecte de anul trecut</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "Помощь",
"filter_and_highlights_help": "Помощь с фильтрами и выделениями",
"filter_and_highlights": "Фильтры и выделения",
"boards_you_moderate": "boards, которые вы модерируете"
"boards_you_moderate": "boards, которые вы модерируете",
"more_threads_last_year": "{{count}} темы с {{currentTimeFilterName}} — [<1>Показать больше тем с прошлого года</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "Ndihmë",
"filter_and_highlights_help": "Ndihma për Filtro & Thekso",
"filter_and_highlights": "Filtra dhe thekson",
"boards_you_moderate": "boards që i moderoni"
"boards_you_moderate": "boards që i moderoni",
"more_threads_last_year": "{{count}} tema nga {{currentTimeFilterName}} — [<1>Shfaq më shumë tema nga viti i kaluar</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "Hjälp",
"filter_and_highlights_help": "Filter och markeringar Hjälp",
"filter_and_highlights": "Filter och markeringar",
"boards_you_moderate": "boards du modererar"
"boards_you_moderate": "boards du modererar",
"more_threads_last_year": "{{count}} trådar sedan {{currentTimeFilterName}} — [<1>Visa fler trådar från förra året</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "సహాయం",
"filter_and_highlights_help": "ఫిల్టర్లు మరియు హైలైట్ సహాయం",
"filter_and_highlights": "ఫిల్టర్లు మరియు హైలైట్స్",
"boards_you_moderate": "మీరు మోడరేట్ చేసే బోర్డులు"
"boards_you_moderate": "మీరు మోడరేట్ చేసే బోర్డులు",
"more_threads_last_year": "{{count}} థ్రెడ్‌లు {{currentTimeFilterName}} నుండి — [<1>గత సంవత్సరం నుండి మరిన్ని థ్రెడ్‌లను చూపించండి</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "ช่วยเหลือ",
"filter_and_highlights_help": "ตัวกรองและการเน้นความช่วยเหลือ",
"filter_and_highlights": "ตัวกรองและไฮไลท์",
"boards_you_moderate": "บอร์ดที่คุณดูแล"
"boards_you_moderate": "บอร์ดที่คุณดูแล",
"more_threads_last_year": "{{count}} กระทู้ตั้งแต่ {{currentTimeFilterName}} — [<1>แสดงกระทู้จากปีที่แล้วเพิ่มเติม</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "Yardım",
"filter_and_highlights_help": "Filtreler ve Vurgular Yardımı",
"filter_and_highlights": "Filtreler ve Vurgular",
"boards_you_moderate": "modere ettiğiniz panolar"
"boards_you_moderate": "modere ettiğiniz panolar",
"more_threads_last_year": "{{count}} konu {{currentTimeFilterName}} itibariyle — [<1>Geçen yıldan daha fazla konu göster</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "Допомога",
"filter_and_highlights_help": "Допомога з фільтрами та підсвічуваннями",
"filter_and_highlights": "Фільтри та виділення",
"boards_you_moderate": "boards, які ви модеруватимете"
"boards_you_moderate": "boards, які ви модеруватимете",
"more_threads_last_year": "{{count}} тем з {{currentTimeFilterName}} — [<1>Показати більше тем з минулого року</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "مدد",
"filter_and_highlights_help": "فلٹرز اور ہائی لائٹس کی مدد",
"filter_and_highlights": "فلٹرز اور ہائلाइटس",
"boards_you_moderate": "وہ بورڈز جن کی آپ نگرانی کرتے ہیں"
"boards_you_moderate": "وہ بورڈز جن کی آپ نگرانی کرتے ہیں",
"more_threads_last_year": "{{count}} تھریڈز {{currentTimeFilterName}} سے — [<1>گزشتہ سال کے مزید تھریڈز دکھائیں</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"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",
"boards_you_moderate": "boards bạn điều hành"
"boards_you_moderate": "boards bạn điều hành",
"more_threads_last_year": "{{count}} chủ đề từ {{currentTimeFilterName}} — [<1>Hiển thị thêm các chủ đề từ năm ngoái</1>]"
}
+2 -1
View File
@@ -295,5 +295,6 @@
"help": "帮助",
"filter_and_highlights_help": "过滤器和高亮帮助",
"filter_and_highlights": "筛选器与高亮",
"boards_you_moderate": "您管理的板块"
"boards_you_moderate": "您管理的板块",
"more_threads_last_year": "{{count}} 主题自 {{currentTimeFilterName}} — [<1>显示更多去年的主题</1>]"
}
+4 -4
View File
@@ -148,10 +148,10 @@ const App = () => (
<Route path='/all/catalog/:timeFilterName?' element={<Catalog />} />
<Route path='/all/catalog/:timeFilterName?/settings' element={<Catalog />} />
<Route path='/subscriptions/:timeFilterName?' element={<Board />} />
<Route path='/subscriptions/:timeFilterName?/settings' element={<Board />} />
<Route path='/subscriptions/catalog/:timeFilterName?' element={<Catalog />} />
<Route path='/subscriptions/catalog/:timeFilterName?/settings' element={<Catalog />} />
<Route path='/subs/:timeFilterName?' element={<Board />} />
<Route path='/subs/:timeFilterName?/settings' element={<Board />} />
<Route path='/subs/catalog/:timeFilterName?' element={<Catalog />} />
<Route path='/subs/catalog/:timeFilterName?/settings' element={<Catalog />} />
<Route path='/mod/:timeFilterName?' element={<Board />} />
<Route path='/mod/:timeFilterName?/settings' element={<Board />} />
+49 -9
View File
@@ -5,7 +5,7 @@ import useSubplebbitsStore from '@plebbit/plebbit-react-hooks/dist/stores/subple
import useSubplebbitsPagesStore from '@plebbit/plebbit-react-hooks/dist/stores/subplebbits-pages';
import { isAllView, isCatalogView, isDescriptionView, isModView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils';
import { useDefaultSubplebbits } from '../../hooks/use-default-subplebbits';
import { getBoardPath } from '../../lib/utils/route-utils';
import { getBoardPath, isDirectoryBoard } from '../../lib/utils/route-utils';
import { useResolvedSubplebbitAddress } from '../../hooks/use-resolved-subplebbit-address';
import useCatalogFiltersStore from '../../stores/use-catalog-filters-store';
import useCatalogStyleStore from '../../stores/use-catalog-style-store';
@@ -39,8 +39,8 @@ const CatalogButton = ({ address, isInAllView, isInSubscriptionsView, isInModVie
if (params?.timeFilterName) return `/all/catalog/${params.timeFilterName}`;
return `/all/catalog`;
} else if (isInSubscriptionsView) {
if (params?.timeFilterName) return `/subscriptions/catalog/${params.timeFilterName}`;
return `/subscriptions/catalog`;
if (params?.timeFilterName) return `/subs/catalog/${params.timeFilterName}`;
return `/subs/catalog`;
} else if (isInModView) {
if (params?.timeFilterName) return `/mod/catalog/${params.timeFilterName}`;
return `/mod/catalog`;
@@ -82,8 +82,8 @@ const ReturnButton = ({ address, isInAllView, isInSubscriptionsView, isInModView
if (params?.timeFilterName) return `/all/${params.timeFilterName}`;
return `/all`;
} else if (isInSubscriptionsView) {
if (params?.timeFilterName) return `/subscriptions/${params.timeFilterName}`;
return `/subscriptions`;
if (params?.timeFilterName) return `/subs/${params.timeFilterName}`;
return `/subs`;
} else if (isInModView) {
if (params?.timeFilterName) return `/mod/${params.timeFilterName}`;
return `/mod`;
@@ -104,6 +104,28 @@ const ReturnButton = ({ address, isInAllView, isInSubscriptionsView, isInModView
);
};
const VoteButton = () => {
const { t } = useTranslation();
const params = useParams();
const defaultSubplebbits = useDefaultSubplebbits();
// Get the boardIdentifier from params (try boardIdentifier first, then subplebbitAddress for backward compatibility)
const boardIdentifier = params.boardIdentifier || params.subplebbitAddress;
// Only render the vote button if we're on a directory board route
if (!boardIdentifier || !isDirectoryBoard(boardIdentifier, defaultSubplebbits)) {
return null;
}
const message = `Not available yet. Users will be able to submit and vote for boards competing for directory slots (like /${boardIdentifier}). The highest-voted board becomes the directory board.`;
return (
<button className={`button ${styles.disabledButton}`} title={message} onClick={() => window.alert(message)}>
{t('vote')}
</button>
);
};
const RefreshButton = () => {
const { t } = useTranslation();
const reset = useFeedResetStore((state) => state.reset);
@@ -227,8 +249,8 @@ export const TimeFilter = ({ isInAllView, isInCatalogView, isInSubscriptionsView
: `/all/${timeFilterName}`
: isInSubscriptionsView
? isInCatalogView
? `/subscriptions/catalog/${timeFilterName}`
: `/subscriptions/${timeFilterName}`
? `/subs/catalog/${timeFilterName}`
: `/subs/${timeFilterName}`
: isInModView
? isInCatalogView
? `/mod/catalog/${timeFilterName}`
@@ -278,11 +300,17 @@ export const MobileBoardButtons = () => {
const { filteredCount, searchText } = useCatalogFiltersStore();
// Check if we should show the vote button (only for directory boards)
const defaultSubplebbits = useDefaultSubplebbits();
const boardIdentifier = params.boardIdentifier || params.subplebbitAddress;
const showVoteButton = boardIdentifier && isDirectoryBoard(boardIdentifier, defaultSubplebbits);
return (
<div className={`${styles.mobileBoardButtons} ${!isInCatalogView ? styles.addMargin : ''}`}>
{isInPostView || isInPendingPostPage ? (
<>
<ReturnButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />
{showVoteButton && <VoteButton />}
<CatalogButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />
<SubscribeButton address={subplebbitAddress} />
<div className={styles.secondRow}>
@@ -297,6 +325,7 @@ export const MobileBoardButtons = () => {
) : (
<CatalogButton address={subplebbitAddress} isInAllView={isInAllView} isInSubscriptionsView={isInSubscriptionsView} isInModView={isInModView} />
)}
{showVoteButton && <VoteButton />}
{!(isInAllView || isInSubscriptionsView || isInModView) && <SubscribeButton address={subplebbitAddress} />}
<RefreshButton />
{isInCatalogView && searchText ? (
@@ -374,6 +403,11 @@ export const DesktopBoardButtons = () => {
const { filteredCount, searchText } = useCatalogFiltersStore();
// Check if we should show the vote button (only for directory boards)
const defaultSubplebbits = useDefaultSubplebbits();
const boardIdentifier = params.boardIdentifier || params.subplebbitAddress;
const showVoteButton = boardIdentifier && isDirectoryBoard(boardIdentifier, defaultSubplebbits);
return (
<>
<hr />
@@ -406,6 +440,12 @@ export const DesktopBoardButtons = () => {
</>
)}
[<RefreshButton />]
{showVoteButton && (
<>
{' '}
[<VoteButton />]
</>
)}
{isInCatalogView && searchText ? (
<span className={styles.filteredThreadsCount}>
{' '}
@@ -474,8 +514,8 @@ const SearchOPsBar = () => {
: `/all/catalog?q=${encodeURIComponent(searchQuery)}`;
} else if (isInSubscriptionsView) {
catalogUrl = params?.timeFilterName
? `/subscriptions/catalog/${params.timeFilterName}?q=${encodeURIComponent(searchQuery)}`
: `/subscriptions/catalog?q=${encodeURIComponent(searchQuery)}`;
? `/subs/catalog/${params.timeFilterName}?q=${encodeURIComponent(searchQuery)}`
: `/subs/catalog?q=${encodeURIComponent(searchQuery)}`;
} else if (isInModView) {
catalogUrl = params?.timeFilterName
? `/mod/catalog/${params.timeFilterName}?q=${encodeURIComponent(searchQuery)}`
@@ -24,6 +24,15 @@
color: var(--board-address-text-color);
}
.clickableSubtitle {
cursor: pointer;
display: inline-block;
}
.clickableSubtitle:hover {
text-decoration: underline;
}
.content hr {
width: 90%;
}
+19 -6
View File
@@ -1,7 +1,7 @@
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useLocation, useParams } from 'react-router-dom';
import { useAccountComment } from '@plebbit/plebbit-react-hooks';
import { useLocation, useParams, useNavigate } from 'react-router-dom';
import { useAccount, useAccountComment } from '@plebbit/plebbit-react-hooks';
import Plebbit from '@plebbit/plebbit-js';
import useSubplebbitsStore from '@plebbit/plebbit-react-hooks/dist/stores/subplebbits';
import { isAllView, isSubscriptionsView, isModView } from '../../lib/utils/view-utils';
@@ -25,6 +25,7 @@ const BoardHeader = () => {
const { t } = useTranslation();
const location = useLocation();
const params = useParams();
const navigate = useNavigate();
const isInAllView = isAllView(location.pathname);
const isInSubscriptionsView = isSubscriptionsView(location.pathname, useParams());
const isInModView = isModView(location.pathname);
@@ -42,14 +43,18 @@ const BoardHeader = () => {
// Find matching subplebbit from default list to get its title
const defaultSubplebbit = subplebbitAddress ? defaultSubplebbits.find((s) => s.address === subplebbitAddress) : null;
const account = useAccount() || {};
const subscriptions = account?.subscriptions || [];
const subscriptionsSubtitle = subscriptions?.length === 1 ? `${subscriptions?.length} subscription` : `${subscriptions?.length} subscriptions`;
const title = isInAllView
? multisubMetadata?.title || 'all'
? multisubMetadata?.title || '/all/ - 5chan Directories'
: isInSubscriptionsView
? 'Subscriptions'
? '/subs/ - Subscriptions'
: isInModView
? _.startCase(t('boards_you_moderate'))
: defaultSubplebbit?.title || subplebbit?.title;
const subtitle = isInAllView ? 'p/all' : isInSubscriptionsView ? 'p/subscriptions' : isInModView ? 'p/mod' : `p/${address || subplebbitAddress || ''}`;
const subtitle = isInAllView ? '' : isInSubscriptionsView ? subscriptionsSubtitle : isInModView ? '/mod/' : `${address || subplebbitAddress || ''}`;
const { isOffline, isOnlineStatusLoading, offlineIconClass, offlineTitle } = useIsSubplebbitOffline(subplebbit);
@@ -75,7 +80,15 @@ const BoardHeader = () => {
</span>
)}
</div>
<div className={styles.boardSubtitle}>{subtitle}</div>
<div className={styles.boardSubtitle}>
{isInSubscriptionsView ? (
<span className={styles.clickableSubtitle} onClick={() => navigate('/subs/settings#subscriptions-settings')}>
{subtitle}
</span>
) : (
subtitle
)}
</div>
<hr />
</div>
);
@@ -63,6 +63,18 @@
background-position: center;
background-repeat: no-repeat;
image-rendering: pixelated;
border-radius: 2px;
}
.closeButton:focus {
outline: 2px solid currentColor;
outline-offset: 2px;
}
.closeButton:focus-visible {
outline: 2px solid #0066cc;
outline-offset: 2px;
box-shadow: 0 0 0 2px #0066cc, 0 0 0 4px rgba(0, 102, 204, 0.3);
}
.bd {
@@ -24,16 +24,20 @@
box-shadow: rgba(0, 0, 0, 0.1) 2px 2px 0px 1px;
font-family: arial, helvetica, clean, sans-serif;
font-size: 13px;
line-height: 16.003px;
line-height: 18px;
z-index: 1000;
}
@media (max-width: 600px) {
.directoryDialog {
width: calc(100% - 20px);
margin-left: -50%;
left: 50%;
max-width: 500px;
left: 50%;
top: 50%;
margin-left: 0;
transform: translate(-50%, -50%);
max-height: calc(100vh - 40px);
overflow-y: auto;
}
}
@@ -57,8 +61,8 @@
.closeButton {
all: unset;
cursor: pointer;
width: 16px;
height: 16px;
width: 18px;
height: 18px;
position: absolute;
top: 4px;
right: 4px;
@@ -66,6 +70,14 @@
background-size: 100%;
background-position: center;
background-repeat: no-repeat;
image-rendering: pixelated;
}
.closeButtonHome {
width: 16px;
height: 16px;
background-image: url("/assets/buttons/icon-close-red.png");
image-rendering: auto;
}
.bd {
@@ -22,27 +22,17 @@ const DirectoryModal = () => {
<div className={styles.directoryDialog}>
<div className={styles.hd}>
<h2>Submit a Board to a Directory</h2>
<button className={styles.closeButton} onClick={closeDirectoryModal} title='Close' />
<button className={`${styles.closeButton} ${isHomeView ? styles.closeButtonHome : ''}`} onClick={closeDirectoryModal} title='Close' />
</div>
<div className={styles.bd}>
<p className={styles.introMessage}>
<strong>The board you clicked on doesn&apos;t exist yet, but it can be yours!</strong>
</p>
<div className={styles.section}>
<h3>Directory vs Regular Board</h3>
<p>
A &quot;directory board&quot; is assigned to a category on the homepage (like &quot;Anime & Manga&quot; or &quot;Video Games&quot;). Directory assignments
are temporary and handpicked by devs until DAO curation is implemented.{' '}
<strong>Anyone can create a board and users can access it anytime using its address</strong>—use the search bar, direct links, or the
&quot;[Subscribe]&quot; button to access boards regardless of directory assignment.
</p>
</div>
<div className={styles.section}>
<h3>Creating Your Board</h3>
<p>
Create a board using the{' '}
<strong>Anyone can create a board</strong> using the{' '}
<a href='https://plebbit.github.io/docs/learn/clients/5chan/create-a-board' target='_blank' rel='noopener noreferrer'>
Seedit GUI client
</a>{' '}
@@ -50,27 +40,21 @@ const DirectoryModal = () => {
<a href='https://github.com/plebbit/plebbit-cli' target='_blank' rel='noopener noreferrer'>
plebbit-cli
</a>
. <strong>Build a following:</strong> Users can subscribe to your board via the &quot;[Subscribe]&quot; button, which adds it to their top bar. You can gain
subscribers through direct links, word of mouth, or search—no directory assignment or dev approval needed.
. Users can access it anytime via the search bar, direct links, or by subscribing with the &quot;[Subscribe]&quot; button—
<strong>no directory assignment or dev approval needed</strong>. Directory boards are simply featured in homepage categories (like &quot;Anime &
Manga&quot;) and are handpicked by devs until DAO curation is implemented.
</p>
</div>
<div className={styles.section}>
<h3>Directory Requirements</h3>
<h3>Directory Submission</h3>
<p>
Boards need 99% uptime (they&apos;re P2P nodes), plus be active, well-moderated, and relevant to the category. Devs review and approve/reject based on these
criteria.
</p>
</div>
<div className={styles.section}>
<h3>Submitting to a Directory</h3>
<p>
Open a PR editing{' '}
To submit your board, open a PR editing{' '}
<a href='https://github.com/plebbit/lists/blob/master/5chan-multisub.json' target='_blank' rel='noopener noreferrer'>
5chan-multisub.json
</a>{' '}
with your board&apos;s title, address, and NSFW status. Devs will review and merge if approved. 99% uptime is required.
with your board&apos;s title, address, and NSFW status. Requirements: 99% uptime (they&apos;re P2P nodes), active, well-moderated, and relevant to the
category. Devs will review and approve/reject based on these criteria.
</p>
</div>
@@ -85,14 +69,6 @@ const DirectoryModal = () => {
.
</p>
</div>
<div className={styles.section}>
<h3>Decentralization</h3>
<p>
Devs can change directories via commits in the open-source repo. No centralized servers—anyone can fork, modify, and redeploy to their own domain. 5chan is
adminless with no central authority.
</p>
</div>
</div>
<div className={styles.directoryFooter}>
<button onClick={closeDirectoryModal}>Close</button>
@@ -55,6 +55,10 @@
background-repeat: no-repeat;
}
.closeButtonHome {
background-image: url("/assets/buttons/icon-close-red.png");
}
.bd {
padding: 10px;
}
@@ -1,10 +1,12 @@
import { useNavigate } from 'react-router-dom';
import { useLocation, useNavigate } from 'react-router-dom';
import useDisclaimerModalStore from '../../stores/use-disclaimer-modal-store';
import useDirectoryModalStore from '../../stores/use-directory-modal-store';
import styles from './disclaimer-modal.module.css';
const DisclaimerModal = () => {
const navigate = useNavigate();
const location = useLocation();
const isHomeView = location.pathname === '/';
const { showModal, closeDisclaimerModal, acceptDisclaimer, targetBoardPath } = useDisclaimerModalStore();
const { openDirectoryModal } = useDirectoryModalStore();
@@ -33,7 +35,7 @@ const DisclaimerModal = () => {
<div className={styles.disclaimerDialog}>
<div className={styles.hd}>
<h2>Disclaimer</h2>
<button className={styles.closeButton} onClick={closeDisclaimerModal} title='Close' />
<button className={`${styles.closeButton} ${isHomeView ? styles.closeButtonHome : ''}`} onClick={closeDisclaimerModal} title='Close' />
</div>
<div className={styles.bd}>
<p>To access boards on 5chan (the &quot;website&quot;), you understand and agree to the following:</p>
+39 -10
View File
@@ -71,7 +71,6 @@ const PostInfo = ({ post, postReplyCount = 0, roles, isHidden }: PostProps) => {
const location = useLocation();
const isInAllView = isAllView(location.pathname);
const isInPostPageView = isPostPageView(location.pathname, params);
const isInSubscriptionsView = isSubscriptionsView(location.pathname, params);
const userID = address && Plebbit.getShortAddress(address);
const userIDBackgroundColor = hashStringToColor(userID);
@@ -166,12 +165,6 @@ const PostInfo = ({ post, postReplyCount = 0, roles, isHidden }: PostProps) => {
{isDescription || isRules ? '' : ' '}
</span>
<span className={styles.postNum}>
{subplebbitAddress && (isInAllView || isInSubscriptionsView) && !isReply && boardPath && (
<span className={styles.postNumLink}>
{' '}
<Link to={`/${boardPath}`}>p/{subplebbitAddress && Plebbit.getShortAddress(subplebbitAddress)}</Link>{' '}
</span>
)}
{!(isDescription || isRules) &&
(cid ? (
<span className={styles.postNumLink}>
@@ -181,7 +174,7 @@ const PostInfo = ({ post, postReplyCount = 0, roles, isHidden }: PostProps) => {
title={t('link_to_post')}
onClick={(e) => !cid && e.preventDefault()}
>
c/
CID:
</Link>
<span className={styles.replyToPost} title={t('reply_to_post')} onMouseDown={onReplyModalClick}>
{shortCid}
@@ -189,7 +182,7 @@ const PostInfo = ({ post, postReplyCount = 0, roles, isHidden }: PostProps) => {
</span>
) : (
<>
<span>c/</span>
<span>CID:</span>
<span className={styles.pendingCid}>
{state === 'failed' || stateString === 'Failed' ? _.capitalize(t('failed')) : state === 'pending' ? _.capitalize(t('pending')) : ''}
</span>
@@ -250,13 +243,31 @@ interface PostMediaProps {
linkHeight: number;
linkWidth: number;
parentCid: string;
subplebbitAddress: string;
isInAllView: boolean;
isInSubscriptionsView: boolean;
}
const PostMedia = ({ commentMediaInfo, hasThumbnail, isDescription, isRules, spoiler, deleted, removed, linkHeight, linkWidth, parentCid }: PostMediaProps) => {
const PostMedia = ({
commentMediaInfo,
hasThumbnail,
isDescription,
isRules,
spoiler,
deleted,
removed,
linkHeight,
linkWidth,
parentCid,
subplebbitAddress,
isInAllView,
isInSubscriptionsView,
}: PostMediaProps) => {
const { t } = useTranslation();
const { url } = commentMediaInfo || {};
let type = commentMediaInfo?.type;
const gifFrameUrl = useFetchGifFirstFrame(url);
const defaultSubplebbits = useDefaultSubplebbits();
if (type === 'gif' && gifFrameUrl !== null) {
type = 'animated gif';
@@ -268,10 +279,16 @@ const PostMedia = ({ commentMediaInfo, hasThumbnail, isDescription, isRules, spo
const [showThumbnail, setShowThumbnail] = useState(true);
const mediaDimensions = getMediaDimensions(commentMediaInfo);
const boardPath = getBoardPath(subplebbitAddress, defaultSubplebbits);
return (
<div className={styles.file}>
<div className={styles.fileText}>
{subplebbitAddress && (isInAllView || isInSubscriptionsView) && boardPath && !parentCid && (
<>
{t('board')}: <Link to={`/${boardPath}`}>{boardPath}</Link>{' '}
</>
)}
{t('link')}:{' '}
<a href={url} target='_blank' rel='noopener noreferrer'>
{spoiler ? _.capitalize(t('spoiler')) : url && url.length > 30 ? url.slice(0, 30) + '...' : url}
@@ -337,6 +354,10 @@ const Reply = ({ postReplyCount, reply, roles }: PostProps) => {
const isRouteLinkToReply = cid ? location.pathname.startsWith(route) : false;
const { hidden } = useHide({ cid });
const isInAllView = isAllView(location.pathname);
const params = useParams();
const isInSubscriptionsView = isSubscriptionsView(location.pathname, params);
const commentMediaInfo = useCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight);
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
@@ -357,6 +378,9 @@ const Reply = ({ postReplyCount, reply, roles }: PostProps) => {
linkHeight={linkHeight}
linkWidth={linkWidth}
parentCid={parentCid}
subplebbitAddress={subplebbitAddress}
isInAllView={isInAllView}
isInSubscriptionsView={isInSubscriptionsView}
/>
)}
{!hidden && (!(removed || deleted) || ((removed || deleted) && reason)) && <CommentContent comment={post} />}
@@ -373,6 +397,8 @@ const PostDesktop = ({ post, roles, showAllReplies, showReplies = true }: PostPr
const location = useLocation();
const isInPendingPostView = isPendingPostView(location.pathname, params);
const isInPostPageView = isPostPageView(location.pathname, params);
const isInAllView = isAllView(location.pathname);
const isInSubscriptionsView = isSubscriptionsView(location.pathname, params);
const defaultSubplebbits = useDefaultSubplebbits();
const boardPath = subplebbitAddress ? getBoardPath(subplebbitAddress, defaultSubplebbits) : undefined;
@@ -433,6 +459,9 @@ const PostDesktop = ({ post, roles, showAllReplies, showReplies = true }: PostPr
linkHeight={linkHeight}
linkWidth={linkWidth}
parentCid={parentCid}
subplebbitAddress={subplebbitAddress}
isInAllView={isInAllView}
isInSubscriptionsView={isInSubscriptionsView}
/>
)}
<PostInfo isHidden={hidden} post={post} postReplyCount={replyCount} roles={roles} />
+9 -7
View File
@@ -10,7 +10,7 @@ import { formatMarkdown } from '../../lib/utils/post-utils';
import { isValidURL } from '../../lib/utils/url-utils';
import { isAllView, isDescriptionView, isModView, isPostPageView, isRulesView, isSubscriptionsView } from '../../lib/utils/view-utils';
import useAnonMode from '../../hooks/use-anon-mode';
import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits';
import { useDefaultSubplebbits } from '../../hooks/use-default-subplebbits';
import { useResolvedSubplebbitAddress } from '../../hooks/use-resolved-subplebbit-address';
import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame';
import useIsSubplebbitOffline from '../../hooks/use-is-subplebbit-offline';
@@ -59,7 +59,7 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
const isInModView = isModView(location.pathname);
const isInSubscriptionsView = isSubscriptionsView(location.pathname, useParams());
const subscriptions = account?.subscriptions || [];
const defaultSubplebbitAddresses = useDefaultSubplebbitAddresses();
const defaultSubplebbits = useDefaultSubplebbits();
const { accountSubplebbits } = useAccountSubplebbits();
const accountSubplebbitAddresses = Object.keys(accountSubplebbits);
@@ -408,11 +408,13 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
<select onChange={(e) => setPublishPostOptions({ subplebbitAddress: e.target.value })} value={subplebbitAddress}>
<option value=''>{t('choose_one')}</option>
{isInAllView &&
defaultSubplebbitAddresses.map((address: string) => (
<option key={address} value={address}>
{address && Plebbit.getShortAddress(address)}
</option>
))}
defaultSubplebbits
.filter((subplebbit) => subplebbit.title && subplebbit.address)
.map((subplebbit) => (
<option key={subplebbit.address} value={subplebbit.address}>
{subplebbit.title}
</option>
))}
{isInModView &&
accountSubplebbitAddresses.map((address: string) => (
<option key={address} value={address}>
+3 -3
View File
@@ -178,7 +178,7 @@ const PostInfoAndMedia = ({ post, postReplyCount = 0, roles }: PostProps) => {
{subplebbitAddress && (isInAllView || isInSubscriptionsView) && !isReply && boardPath && (
<div className={styles.postNumLink}>
{' '}
<Link to={`/${boardPath}`}>p/{subplebbitAddress && Plebbit.getShortAddress(subplebbitAddress)}</Link>
<Link to={`/${boardPath}`}>Board: {boardPath}</Link>
</div>
)}
<Tooltip children={<span>{getFormattedDate(timestamp)}</span>} content={getFormattedTimeAgo(timestamp)} />{' '}
@@ -191,7 +191,7 @@ const PostInfoAndMedia = ({ post, postReplyCount = 0, roles }: PostProps) => {
title={t('link_to_post')}
onClick={(e) => !cid && e.preventDefault()}
>
c/
CID:
</Link>
<span className={styles.replyToPost} title={t('reply_to_post')} onMouseDown={onReplyModalClick}>
{shortCid.slice(0, -4)}
@@ -199,7 +199,7 @@ const PostInfoAndMedia = ({ post, postReplyCount = 0, roles }: PostProps) => {
</span>
) : (
<>
<span>c/</span>
<span>CID:</span>
<span className={styles.pendingCid}>
{state === 'failed' || stateString === 'Failed' ? _.capitalize(t('failed')) : state === 'pending' ? _.capitalize(t('pending')) : ''}
</span>
+2 -2
View File
@@ -230,7 +230,7 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, postCid, scrollY, s
}
}, []);
const contentPrefix = `c/${parentCid && Plebbit.getShortCid(parentCid)}\n`;
const contentPrefix = `>>${parentCid && Plebbit.getShortCid(parentCid)}\n`;
// enable spellcheck after the prefix is set
useEffect(() => {
@@ -311,7 +311,7 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, postCid, scrollY, s
}}
>
<div className={`replyModalHandle ${styles.title}`} {...(!isMobile ? bind() : {})}>
{t('reply_to_cid', { cid: `c/${parentCid && Plebbit.getShortCid(parentCid)}`, interpolation: { escapeValue: false } })}
{t('reply_to_cid', { cid: `CID:${parentCid && Plebbit.getShortCid(parentCid)}`, interpolation: { escapeValue: false } })}
<button
className={styles.closeIcon}
onClick={(e) => {
@@ -10,7 +10,7 @@ const TopbarEditModal = () => {
const { showModal, closeTopbarEditModal } = useTopbarEditModalStore();
const { visibleDirectories, visibleSubscriptions, setDirectoryVisibility, setSubscriptionVisibility } = useTopbarVisibilityStore();
const account = useAccount();
const subscriptions = account?.subscriptions || [];
const subscriptions = useMemo(() => account?.subscriptions || [], [account?.subscriptions]);
const location = useLocation();
// Convert visible directories set to space-separated string for input
+18 -17
View File
@@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next';
import Plebbit from '@plebbit/plebbit-js';
import { useAccount, useAccountComment, useAccountSubplebbits } from '@plebbit/plebbit-react-hooks';
import { isAllView, isCatalogView, isSubscriptionsView } from '../../lib/utils/view-utils';
import { useDefaultSubplebbitAddresses, useDefaultSubplebbits, MultisubSubplebbit } from '../../hooks/use-default-subplebbits';
import { useDefaultSubplebbits, MultisubSubplebbit } from '../../hooks/use-default-subplebbits';
import { useBoardPath, useResolvedSubplebbitAddress } from '../../hooks/use-resolved-subplebbit-address';
import { getBoardPath, extractDirectoryFromTitle } from '../../lib/utils/route-utils';
import { TimeFilter } from '../board-buttons';
@@ -179,7 +179,7 @@ const TopBarDesktop = () => {
return (
<div className={styles.boardNavDesktop}>
<span className={styles.boardList}>
[<Link to='/all'>all</Link> / <Link to='/subscriptions'>subscriptions</Link>
[<Link to='/all'>all</Link> / <Link to='/subs'>subs</Link>
{accountSubplebbitAddresses.length > 0 && (
<>
{' '}
@@ -228,12 +228,12 @@ const TopBarDesktop = () => {
const TopBarMobile = ({ subplebbitAddress }: { subplebbitAddress: string }) => {
const { t } = useTranslation();
const navigate = useNavigate();
const subplebbitAddresses = useDefaultSubplebbitAddresses();
const defaultSubplebbits = useDefaultSubplebbits();
const displaySubplebbitAddress = subplebbitAddress && subplebbitAddress.length > 30 ? subplebbitAddress.slice(0, 30).concat('...') : subplebbitAddress;
const [showSearchBar, setShowSearchBar] = useState(false);
const currentSubplebbitIsInList = subplebbitAddresses.some((address: string) => address === subplebbitAddress);
// Filter to only show directory boards (those with titles)
const directoryBoards = useMemo(() => defaultSubplebbits.filter((sub) => sub.title && extractDirectoryFromTitle(sub.title)), [defaultSubplebbits]);
const location = useLocation();
const params = useParams();
@@ -241,36 +241,37 @@ const TopBarMobile = ({ subplebbitAddress }: { subplebbitAddress: string }) => {
const isInCatalogView = isCatalogView(location.pathname, params);
const isInSubscriptionsView = isSubscriptionsView(location.pathname, params);
const boardPath = useBoardPath(subplebbitAddress);
const selectValue = isInAllView ? 'all' : isInSubscriptionsView ? 'subscriptions' : boardPath || subplebbitAddress;
const selectValue = isInAllView ? 'all' : isInSubscriptionsView ? 'subs' : boardPath || subplebbitAddress;
const { accountSubplebbits } = useAccountSubplebbits();
const accountSubplebbitAddresses = Object.keys(accountSubplebbits);
// Check if current subplebbit is a directory board
const currentIsDirectoryBoard = directoryBoards.some((board) => board.address === subplebbitAddress);
const boardSelect = (
<select
value={selectValue}
onChange={(e) => {
const value = e.target.value;
// If it's a directory code or special route, use it directly
if (value === 'all' || value === 'subscriptions' || value === 'mod') {
// If it's a special route, use it directly
if (value === 'all' || value === 'subs' || value === 'mod') {
navigate(`/${value}${isInCatalogView ? '/catalog' : ''}`);
} else {
// Otherwise, resolve to board path
const path = getBoardPath(value, defaultSubplebbits);
navigate(`/${path}${isInCatalogView ? '/catalog' : ''}`);
// Otherwise, it's a directory code, use it directly
navigate(`/${value}${isInCatalogView ? '/catalog' : ''}`);
}
}}
>
{!currentSubplebbitIsInList && subplebbitAddress && <option value={subplebbitAddress}>{displaySubplebbitAddress}</option>}
{!currentIsDirectoryBoard && subplebbitAddress && <option value={subplebbitAddress}>{displaySubplebbitAddress}</option>}
<option value='all'>all</option>
<option value='subscriptions'>subscriptions</option>
<option value='subs'>subs</option>
{accountSubplebbitAddresses.length > 0 && <option value='mod'>mod</option>}
{subplebbitAddresses.map((address: any, index: number) => {
const subplebbitAddress = address?.includes('.') ? address : Plebbit.getShortAddress(address);
const boardPath = getBoardPath(address, defaultSubplebbits);
{directoryBoards.map((board, index) => {
const directoryCode = extractDirectoryFromTitle(board.title!);
return (
<option key={index} value={boardPath}>
{Plebbit.getShortAddress(subplebbitAddress)}
<option key={index} value={directoryCode!}>
{board.title}
</option>
);
})}
+2 -2
View File
@@ -62,7 +62,7 @@ const useTheme = (): [string, (theme: string) => void] => {
let storedTheme = null;
if (isInAllView || isInSubscriptionsView || isInModView) {
storedTheme = themes.sfw;
storedTheme = themes.nsfw;
} else if (subplebbitAddress) {
const subplebbit = subplebbits.find((s) => s.address === subplebbitAddress);
if (subplebbit?.nsfw) {
@@ -83,7 +83,7 @@ const useTheme = (): [string, (theme: string) => void] => {
const setSubplebbitTheme = useCallback(
async (newTheme: string) => {
if (isInAllView || isInSubscriptionsView || isInModView) {
await setThemeStore('sfw', newTheme);
await setThemeStore('nsfw', newTheme);
} else if (subplebbitAddress) {
const subplebbit = subplebbits.find((s) => s.address === subplebbitAddress);
if (subplebbit?.nsfw) {
+1 -1
View File
@@ -59,7 +59,7 @@ export const is5chanLink = (url: string): boolean => {
// - /{boardIdentifier}/catalog
// - /{boardIdentifier}/description
// - /{boardIdentifier}/rules
// - /all, /subscriptions, /mod, /pending/{index}
// - /all, /subs, /mod, /pending/{index}
return (
/^\/p\/[^/]+(\/c\/[^/]+)?$/.test(routePath) ||
/^\/[^/]+(\/thread\/[^/]+|\/catalog|\/description|\/rules)?$/.test(routePath) ||
+13 -13
View File
@@ -16,7 +16,7 @@ export const isBoardView = (pathname: string, params: ParamsType): boolean => {
// Check if it's a board view (not all, subscriptions, mod, pending, or special routes)
if (
pathname.startsWith('/all') ||
pathname.startsWith('/subscriptions') ||
pathname.startsWith('/subs') ||
pathname.startsWith('/mod') ||
pathname.startsWith('/pending') ||
pathname === '/' ||
@@ -40,10 +40,10 @@ export const isCatalogView = (pathname: string, params: ParamsType): boolean =>
decodedPathname === `/all/catalog/settings` ||
decodedPathname === `/all/catalog/${timeFilterName}` ||
decodedPathname === `/all/catalog/${timeFilterName}/settings` ||
decodedPathname === `/subscriptions/catalog` ||
decodedPathname === `/subscriptions/catalog/settings` ||
decodedPathname === `/subscriptions/catalog/${timeFilterName}` ||
decodedPathname === `/subscriptions/catalog/${timeFilterName}/settings` ||
decodedPathname === `/subs/catalog` ||
decodedPathname === `/subs/catalog/settings` ||
decodedPathname === `/subs/catalog/${timeFilterName}` ||
decodedPathname === `/subs/catalog/${timeFilterName}/settings` ||
decodedPathname === `/mod/catalog` ||
decodedPathname === `/mod/catalog/settings` ||
decodedPathname === `/mod/catalog/${timeFilterName}` ||
@@ -99,14 +99,14 @@ export const isSettingsView = (pathname: string, params: ParamsType): boolean =>
export const isSubscriptionsView = (pathname: string, params: ParamsType): boolean => {
const { timeFilterName } = params;
return (
pathname === '/subscriptions' ||
pathname === '/subscriptions/settings' ||
pathname === `/subscriptions/${timeFilterName}` ||
pathname === `/subscriptions/${timeFilterName}/settings` ||
pathname === '/subscriptions/catalog' ||
pathname === '/subscriptions/catalog/settings' ||
pathname === `/subscriptions/catalog/${timeFilterName}` ||
pathname === `/subscriptions/catalog/${timeFilterName}/settings`
pathname === '/subs' ||
pathname === '/subs/settings' ||
pathname === `/subs/${timeFilterName}` ||
pathname === `/subs/${timeFilterName}/settings` ||
pathname === '/subs/catalog' ||
pathname === '/subs/catalog/settings' ||
pathname === `/subs/catalog/${timeFilterName}` ||
pathname === `/subs/catalog/${timeFilterName}/settings`
);
};
+6 -5
View File
@@ -28,6 +28,12 @@ const useReplyModalStore = create<ReplyModalState>((set, get) => ({
},
openReplyModal: (parentCid, postCid, subplebbitAddress) => {
// Don't update if already open with different parent
if (get().activeCid && get().activeCid !== parentCid) {
window.alert('Multiple quotes are not possible on 5chan for the time being, because of a protocol limitation. Please reply to one post at a time.');
return;
}
// Get selected text
const text = document.getSelection()?.toString();
if (text) {
@@ -38,11 +44,6 @@ const useReplyModalStore = create<ReplyModalState>((set, get) => ({
const isMobile = window.innerWidth <= 768; // Simple check, adjust as needed
const scrollY = isMobile ? window.scrollY : 0;
// Don't update if already open with different parent
if (get().activeCid && get().activeCid !== parentCid) {
return;
}
set({
activeCid: parentCid,
threadCid: postCid,
+6
View File
@@ -78,6 +78,7 @@
--directory-modal-header-background-color: rgb(136, 0, 0);
--directory-modal-header-text-color: rgb(255, 255, 255);
--directory-modal-close-button-background-image: url("/assets/buttons/cross-red.png");
--disclaimer-modal-close-button-background-image: url("/assets/buttons/cross-red.png");
/* footer */
@@ -297,6 +298,7 @@
--directory-modal-header-background-color: #98e;
--directory-modal-header-text-color: #000;
--directory-modal-close-button-background-image: url("/assets/buttons/cross-blue.png");
--disclaimer-modal-close-button-background-image: url("/assets/buttons/cross-blue.png");
/* horizontal rule */
--hr-border: none;
@@ -503,6 +505,7 @@
--directory-modal-header-background-color: #ea8;
--directory-modal-header-text-color: #800;
--directory-modal-close-button-background-image: url("/assets/buttons/cross-red.png");
--disclaimer-modal-close-button-background-image: url("/assets/buttons/cross-red.png");
/* footer */
--footer-item-background-color-desktop: #fed;
@@ -700,6 +703,7 @@
--directory-modal-header-background-color: #98e;
--directory-modal-header-text-color: #000;
--directory-modal-close-button-background-image: url("/assets/buttons/cross-blue.png");
--disclaimer-modal-close-button-background-image: url("/assets/buttons/cross-blue.png");
/* media */
--media-thumbnail-background-color: rgba(0, 0, 0, 0.05);
@@ -897,6 +901,7 @@
--directory-modal-header-background-color: #282a2e;
--directory-modal-header-text-color: #c5c8c6;
--directory-modal-close-button-background-image: url("/assets/buttons/cross-dark.png");
--disclaimer-modal-close-button-background-image: url("/assets/buttons/cross-dark.png");
/* horizontal rule */
--hr-border: none;
@@ -1243,6 +1248,7 @@
--directory-modal-header-background-color: #ddd;
--directory-modal-header-text-color: #333;
--directory-modal-close-button-background-image: url("/assets/buttons/cross-photon.png");
--disclaimer-modal-close-button-background-image: url("/assets/buttons/cross-photon.png");
/* stats */
--stats-font-size: 11px;
+23 -4
View File
@@ -148,14 +148,23 @@ const Board = () => {
newerThan: 60 * 60 * 24 * 30,
filter: hideThreadsWithoutImages ? createThreadsWithoutImagesFilter() : undefined,
});
const { feed: yearlyFeed } = useFeed({
subplebbitAddresses,
sortType,
newerThan: 60 * 60 * 24 * 365,
filter: hideThreadsWithoutImages ? createThreadsWithoutImagesFilter() : undefined,
});
const feedLength = feed.length;
const weeklyFeedLength = weeklyFeed.length;
const monthlyFeedLength = monthlyFeed.length;
const yearlyFeedLength = yearlyFeed.length;
const hasFeedLoaded = !!feed;
const loadingStateString =
useFeedStateString(subplebbitAddresses) ||
(!hasFeedLoaded || (feedLength === 0 && !(weeklyFeedLength > feedLength || monthlyFeedLength > feedLength)) ? t('loading_feed') : t('looking_for_more_posts'));
(!hasFeedLoaded || (feedLength === 0 && !(weeklyFeedLength > feedLength || monthlyFeedLength > feedLength || yearlyFeedLength > monthlyFeedLength))
? t('loading_feed')
: t('looking_for_more_posts'));
const [showMorePostsSuggestion, setShowMorePostsSuggestion] = useState(false);
useEffect(() => {
@@ -189,9 +198,9 @@ const Board = () => {
) : (
(isInAllView || isInSubscriptionsView || isInModView) &&
showMorePostsSuggestion &&
monthlyFeed.length > feed.length &&
(monthlyFeed.length > feed.length || yearlyFeed.length > monthlyFeed.length) &&
(() => {
const basePath = isInAllView ? '/all' : isInSubscriptionsView ? '/subscriptions' : isInModView ? '/mod' : boardPath ? `/${boardPath}` : '';
const basePath = isInAllView ? '/all' : isInSubscriptionsView ? '/subs' : isInModView ? '/mod' : boardPath ? `/${boardPath}` : '';
return weeklyFeed.length > feed.length ? (
<div className={styles.morePostsSuggestion}>
<Trans
@@ -202,7 +211,7 @@ const Board = () => {
}}
/>
</div>
) : (
) : monthlyFeed.length > feed.length ? (
<div className={styles.morePostsSuggestion}>
<Trans
i18nKey='more_threads_last_month'
@@ -212,6 +221,16 @@ const Board = () => {
}}
/>
</div>
) : (
<div className={styles.morePostsSuggestion}>
<Trans
i18nKey='more_threads_last_year'
values={{ currentTimeFilterName, count: feed.length }}
components={{
1: <Link to={`${basePath}/1y`} />,
}}
/>
</div>
);
})()
)}
+26 -6
View File
@@ -285,6 +285,13 @@ const Catalog = () => {
filter: createCombinedFilter(showTextOnlyThreads, filterItems, searchText, subplebbitAddress || 'all', handleFilterMatch),
});
const { feed: yearlyFeed } = useFeed({
subplebbitAddresses,
sortType,
newerThan: 60 * 60 * 24 * 365,
filter: createCombinedFilter(showTextOnlyThreads, filterItems, searchText, subplebbitAddress || 'all', handleFilterMatch),
});
const [showMorePostsSuggestion, setShowMorePostsSuggestion] = useState(false);
useEffect(() => {
const timer = setTimeout(() => {
@@ -306,9 +313,12 @@ const Catalog = () => {
const feedLength = feed.length;
const weeklyFeedLength = weeklyFeed.length;
const monthlyFeedLength = monthlyFeed.length;
const yearlyFeedLength = yearlyFeed.length;
const hasFeedLoaded = !!feed;
const loadingStateString =
useFeedStateString(subplebbitAddresses) || !hasFeedLoaded || (feedLength === 0 && !(weeklyFeedLength > feedLength || monthlyFeedLength > feedLength))
useFeedStateString(subplebbitAddresses) ||
!hasFeedLoaded ||
(feedLength === 0 && !(weeklyFeedLength > feedLength || monthlyFeedLength > feedLength || yearlyFeedLength > monthlyFeedLength))
? t('loading_feed')
: t('looking_for_more_posts');
@@ -317,7 +327,7 @@ const Catalog = () => {
{state === 'failed' ? (
<span className='red'>{state}</span>
) : isInSubscriptionsView && subscriptions?.length === 0 ? (
t('not_subscribed_to_any_board')
<span className='red'>{t('not_subscribed_to_any_board')}</span>
) : blocked ? (
t('you_have_blocked_this_board')
) : !hasMore && combinedFeed.length === 0 ? (
@@ -361,24 +371,34 @@ const Catalog = () => {
) : (
(isInAllView || isInSubscriptionsView) &&
showMorePostsSuggestion &&
monthlyFeed.length > feed.length &&
(monthlyFeed.length > feed.length || yearlyFeed.length > monthlyFeed.length) &&
(weeklyFeed.length > feed.length ? (
<div className={styles.stateString}>
<Trans
i18nKey='more_threads_last_week'
values={{ currentTimeFilterName, count: feed.length }}
components={{
1: <Link to={(isInAllView ? '/all/catalog' : isInSubscriptionsView ? '/subscriptions/catalog' : `/${boardPath}/catalog`) + '/1w'} />,
1: <Link to={(isInAllView ? '/all/catalog' : isInSubscriptionsView ? '/subs/catalog' : `/${boardPath}/catalog`) + '/1w'} />,
}}
/>
</div>
) : monthlyFeed.length > feed.length ? (
<div className={styles.stateString}>
<Trans
i18nKey='more_threads_last_month'
values={{ currentTimeFilterName, count: feed.length }}
components={{
1: <Link to={(isInAllView ? '/all/catalog' : isInSubscriptionsView ? '/subs/catalog' : `/${boardPath}/catalog`) + '/1m'} />,
}}
/>
</div>
) : (
<div className={styles.stateString}>
<Trans
i18nKey='more_threads_last_month'
i18nKey='more_threads_last_year'
values={{ currentTimeFilterName, count: feed.length }}
components={{
1: <Link to={(isInAllView ? '/all/catalog' : isInSubscriptionsView ? '/subscriptions/catalog' : `/${boardPath}/catalog`) + '/1m'} />,
1: <Link to={(isInAllView ? '/all/catalog' : isInSubscriptionsView ? '/subs/catalog' : `/${boardPath}/catalog`) + '/1y'} />,
}}
/>
</div>
@@ -101,6 +101,7 @@ const BoardsList = ({ multisub }: { multisub: MultisubSubplebbit[] }) => {
const showOther = showAll || showWorksafeOnly;
const showMisc = showAll || showNsfwOnly;
const showAdult = showAll || showNsfwOnly;
const showMultiboards = showAll || showNsfwOnly;
return (
<div className={styles.box}>
@@ -603,6 +604,22 @@ const BoardsList = ({ multisub }: { multisub: MultisubSubplebbit[] }) => {
</ul>
</div>
)}
{/* Column 6: Meta */}
{showMultiboards && (
<div className={styles.boardsColumn}>
<h3>Multiboards</h3>
<NSFWBadge />
<ul>
<li>
<Link to='/all'>All 5chan Directories</Link>
</li>
<li>
<Link to='/subs'>Subscriptions</Link>
</li>
</ul>
</div>
)}
</div>
</div>
);
+8
View File
@@ -343,6 +343,14 @@
color: var(--homepage-infobox-link-text-color);
text-decoration: underline;
}
.boardsColumn li {
padding: 5px 10px 5px 0;
}
.boardsColumn a {
font-size: 1.2em;
}
}
@media (min-width: 640px) {
+1
View File
@@ -4,3 +4,4 @@
]
}