Merge pull request #561 from plebbit/development

Development
This commit is contained in:
Tom (plebeius.eth)
2024-10-12 18:31:43 +02:00
committed by GitHub
48 changed files with 533 additions and 150 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ startIpfs.onError = (error) => {
// only show error once or it spams the user
const alreadyShownIpfsError = !!startIpfsError;
startIpfsError = error;
if (!alreadyShownIpfsError) {
if (!alreadyShownIpfsError && error.message) {
dialog.showErrorBox('IPFS warning', error.message);
}
};
+1
View File
@@ -7,6 +7,7 @@ const defaultPlebbitOptions = {
plebbitRpcClientsOptions: ['ws://localhost:9138'],
};
contextBridge.exposeInMainWorld('isElectron', true);
contextBridge.exposeInMainWorld('defaultPlebbitOptions', defaultPlebbitOptions);
contextBridge.exposeInMainWorld('defaultMediaIpfsGatewayUrl', 'http://localhost:6473');
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "مشاركتك تفتقر إلى رابط يحتوي على صورة، سيتم تصفيتها افتراضيًا على Plebchan. يمكنك تعطيل هذا الفلتر في الإعدادات، أو عرض مشاركتك على عميل Plebbit آخر، مثل Seedit. هل تريد النشر على أي حال؟",
"no_board_selected_warning": "يرجى تحديد لوحة قبل النشر.",
"invalid_url_alert": "الرابط المقدم ليس عنوان URL صالحًا.",
"empty_comment_alert": "لا يمكن نشر تعليق فارغ."
"empty_comment_alert": "لا يمكن نشر تعليق فارغ.",
"more_posts_last_week": "تظهر المشاركات منذ {{currentTimeFilterName}} ، <1>عرض المزيد من المشاركات من الأسبوع الماضي</1>",
"more_posts_last_month": "تظهر المشاركات منذ {{currentTimeFilterName}} ، <1>عرض المزيد من المشاركات من الشهر الماضي</1>",
"newer_posts_available": "مشاركات جديدة متاحة، <1>قم بتحديث الخلاصة</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "আপনার পোস্টে একটি চিত্র সহ একটি লিঙ্ক নেই, এটি ডিফল্টভাবে Plebchan এ ফিল্টার করা হবে। আপনি সেটিংসে এই ফিল্টারটি অক্ষম করতে পারেন, অথবা Seedit এর মতো অন্য Plebbit ক্লায়েন্টে আপনার পোস্টটি দেখতে পারেন। তবুও পোস্ট করতে চান?",
"no_board_selected_warning": "পোস্ট করার আগে একটি বোর্ড নির্বাচন করুন।",
"invalid_url_alert": "দেওয়া লিঙ্কটি একটি বৈধ URL নয়।",
"empty_comment_alert": "খালি মন্তব্য পোস্ট করা যাবে না।"
"empty_comment_alert": "খালি মন্তব্য পোস্ট করা যাবে না।",
"more_posts_last_week": "আপনি {{currentTimeFilterName}} থেকে পোস্টগুলি দেখছেন, <1>গত সপ্তাহ থেকে আরও পোস্ট দেখান</1>",
"more_posts_last_month": "আপনি {{currentTimeFilterName}} থেকে পোস্টগুলি দেখছেন, <1>গত মাস থেকে আরও পোস্ট দেখান</1>",
"newer_posts_available": "নতুন পোস্ট উপলব্ধ, <1>ফিডটি রিফ্রেশ করুন</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "Váš příspěvek postrádá odkaz s obrázkem, ve výchozím nastavení bude na Plebchanu filtrován. Tento filtr můžete vypnout v nastavení nebo si můžete svůj příspěvek prohlédnout na jiném klientovi Plebbit, jako je Seedit. Přesto zveřejnit?",
"no_board_selected_warning": "Vyberte desku před odesláním.",
"invalid_url_alert": "Poskytnutý odkaz není platná adresa URL.",
"empty_comment_alert": "Nelze publikovat prázdný komentář."
"empty_comment_alert": "Nelze publikovat prázdný komentář.",
"more_posts_last_week": "Zobrazujeme příspěvky od {{currentTimeFilterName}}, <1>zobrazit další příspěvky z minulého týdne</1>",
"more_posts_last_month": "Zobrazujeme příspěvky od {{currentTimeFilterName}}, <1>zobrazit další příspěvky z minulého měsíce</1>",
"newer_posts_available": "Novější příspěvky jsou k dispozici, <1>aktualizujte kanál</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "Dit indlæg mangler et link med et billede, det vil som standard blive filtreret på Plebchan. Du kan deaktivere dette filter i indstillingerne eller se dit indlæg på en anden Plebbit-klient, som Seedit. Vil du alligevel poste?",
"no_board_selected_warning": "Vælg en tavle, før du poster.",
"invalid_url_alert": "Det angivne link er ikke en gyldig URL.",
"empty_comment_alert": "Kan ikke poste en tom kommentar."
"empty_comment_alert": "Kan ikke poste en tom kommentar.",
"more_posts_last_week": "Viser indlæg siden {{currentTimeFilterName}}, <1>vis flere indlæg fra sidste uge</1>",
"more_posts_last_month": "Viser indlæg siden {{currentTimeFilterName}}, <1>vis flere indlæg fra sidste måned</1>",
"newer_posts_available": "Nyere indlæg tilgængelige, <1>genindlæs feed</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "Ihr Beitrag fehlt ein Link mit einem Bild, er wird standardmäßig auf Plebchan herausgefiltert. Sie können diesen Filter in den Einstellungen deaktivieren oder Ihren Beitrag in einem anderen Plebbit-Client wie Seedit anzeigen. Trotzdem posten?",
"no_board_selected_warning": "Bitte wählen Sie ein Board, bevor Sie posten.",
"invalid_url_alert": "Der bereitgestellte Link ist keine gültige URL.",
"empty_comment_alert": "Kann keinen leeren Kommentar posten."
"empty_comment_alert": "Kann keinen leeren Kommentar posten.",
"more_posts_last_week": "Zeigt Beiträge seit {{currentTimeFilterName}}, <1>zeige mehr Beiträge aus der letzten Woche</1>",
"more_posts_last_month": "Zeigt Beiträge seit {{currentTimeFilterName}}, <1>zeige mehr Beiträge aus dem letzten Monat</1>",
"newer_posts_available": "Neuere Beiträge verfügbar, <1>Feed neu laden</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "Η ανάρτησή σας λείπει από έναν σύνδεσμο με μια εικόνα, θα φιλτραριστεί από προεπιλογή στο Plebchan. Μπορείτε να απενεργοποιήσετε αυτό το φίλτρο στις ρυθμίσεις ή να δείτε την ανάρτησή σας σε έναν άλλο πελάτη Plebbit, όπως το Seedit. Θέλετε να δημοσιεύσετε ούτως ή άλλως;",
"no_board_selected_warning": "Παρακαλώ επιλέξτε μια κατηγορία πριν δημοσιεύσετε.",
"invalid_url_alert": "Ο παρεχόμενος σύνδεσμος δεν είναι έγκυρη διεύθυνση URL.",
"empty_comment_alert": "Δεν μπορεί να δημοσιευτεί κενό σχόλιο."
"empty_comment_alert": "Δεν μπορεί να δημοσιευτεί κενό σχόλιο.",
"more_posts_last_week": "Εμφανίζονται αναρτήσεις από {{currentTimeFilterName}}, <1>εμφάνιση περισσότερων αναρτήσεων από την περασμένη εβδομάδα</1>",
"more_posts_last_month": "Διατίθενται αναρτήσεις από {{currentTimeFilterName}}, <1>εμφάνιση περισσότερων αναρτήσεων από τον περασμένο μήνα</1>",
"newer_posts_available": "Διατίθενται νεότερες δημοσιεύσεις, <1>επανεκκινήστε τη ροή</1>"
}
+5 -2
View File
@@ -135,7 +135,7 @@
"expand_all_settings": "Expand All Settings",
"collapse_all_settings": "Collapse All Settings",
"refresh": "Refresh",
"no_board_selected": "no boards selected",
"no_board_selected": "no board selected",
"none": "none",
"not_subscribed_to_any_board": "You have not subscribed to any boards yet.",
"board_mods": "Board Mods",
@@ -236,5 +236,8 @@
"missing_link_confirm": "Your post is missing a link with an image, it will be filtered out by default on Plebchan. You can disable this filter in the settings, or view your post on another Plebbit client, like Seedit. Post anyway?",
"no_board_selected_warning": "Please select a board before posting.",
"invalid_url_alert": "The provided link is not a valid URL.",
"empty_comment_alert": "Cannot post empty comment."
"empty_comment_alert": "Cannot post empty comment.",
"more_posts_last_week": "Showing posts since {{currentTimeFilterName}}, <1>show more posts from last week</1>",
"more_posts_last_month": "Showing posts since {{currentTimeFilterName}}, <1>show more posts from last month</1>",
"newer_posts_available": "Newer posts available, <1>reload feed</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "Tu publicación falta un enlace con una imagen, se filtrará de forma predeterminada en Plebchan. Puedes desactivar este filtro en la configuración, o ver tu publicación en otro cliente de Plebbit, como Seedit. ¿Publicar de todos modos?",
"no_board_selected_warning": "Por favor, selecciona una tabla antes de publicar.",
"invalid_url_alert": "El enlace proporcionado no es una URL válida.",
"empty_comment_alert": "No se puede publicar un comentario vacío."
"empty_comment_alert": "No se puede publicar un comentario vacío.",
"more_posts_last_week": "Mostrando publicaciones desde {{currentTimeFilterName}}, <1>mostrar más publicaciones de la semana pasada</1>",
"more_posts_last_month": "Mostrando publicaciones desde {{currentTimeFilterName}}, <1>mostrar más publicaciones del mes pasado</1>",
"newer_posts_available": "Nuevas publicaciones disponibles, <1>recargar el feed</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "پست شما دارای یک لینک با تصویر نیست، به‌طور پیش‌فرض در Plebchan فیلتر خواهد شد. شما می‌توانید این فیلتر را در تنظیمات غیرفعال کنید یا پست خود را در یک کلاینت Plebbit دیگر، مانند Seedit مشاهده کنید. با این حال، پست کنید؟",
"no_board_selected_warning": "لطفاً قبل از ارسال یک تابلو انتخاب کنید.",
"invalid_url_alert": "لینک ارائه شده یک URL معتبر نیست.",
"empty_comment_alert": "نمی‌توان نظر خالی ارسال کرد."
"empty_comment_alert": "نمی‌توان نظر خالی ارسال کرد.",
"more_posts_last_week": "نمایش پست‌ها از {{currentTimeFilterName}}، <1>نمایش پست‌های بیشتر از هفته گذشته</1>",
"more_posts_last_month": "نمایش پست‌ها از {{currentTimeFilterName}}، <1>نمایش پست‌های بیشتر از ماه گذشته</1>",
"newer_posts_available": "پست‌های جدیدتر در دسترس هستند، <1>بارگذاری مجدد خوراک</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "Viestistäsi puuttuu kuvaan liittyvä linkki, se suodatetaan oletusarvoisesti Plebchanissa. Voit poistaa tämän suodattimen käytöstä asetuksissa tai katsoa viestiäsi toisella Plebbit-asiakkaalla, kuten Seedit. Haluaako silti julkaista?",
"no_board_selected_warning": "Valitse taulu ennen julkaisemista.",
"invalid_url_alert": "Annettu linkki ei ole voimassa oleva URL.",
"empty_comment_alert": "Ei voi julkaista tyhjää kommenttia."
"empty_comment_alert": "Ei voi julkaista tyhjää kommenttia.",
"more_posts_last_week": "Näytetään viestejä ajasta {{currentTimeFilterName}}, <1>näytä lisää viestejä viime viikolta</1>",
"more_posts_last_month": "Näytetään viestejä ajasta {{currentTimeFilterName}}, <1>näytä lisää viestejä viime kuulta</1>",
"newer_posts_available": "Uudemmat viestit saatavilla, <1>lataa syöte uudelleen</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "Ang iyong post ay walang link na may larawan, ito ay awtomatikong magiging filter sa Plebchan. Maaari mong huwag paganahin ang filter na ito sa mga setting, o tingnan ang iyong post sa ibang Plebbit client, tulad ng Seedit. Mag-post pa rin?",
"no_board_selected_warning": "Mangyaring pumili ng board bago mag-post.",
"invalid_url_alert": "Ang ibinigay na link ay hindi isang wastong URL.",
"empty_comment_alert": "Hindi makapag-post ng walang laman na komento."
"empty_comment_alert": "Hindi makapag-post ng walang laman na komento.",
"more_posts_last_week": "Ipinapakita ang mga post mula {{currentTimeFilterName}}, <1>ipakita ang higit pang mga post mula sa nakaraang linggo</1>",
"more_posts_last_month": "Ipinapakita ang mga post mula {{currentTimeFilterName}}, <1>ipakita ang higit pang mga post mula sa nakaraang buwan</1>",
"newer_posts_available": "May mga bagong post na magagamit, <1>i-reload ang feed</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "Votre publication est manquante d'un lien avec une image, elle sera filtrée par défaut sur Plebchan. Vous pouvez désactiver ce filtre dans les paramètres, ou voir votre publication sur un autre client Plebbit, comme Seedit. Publier quand même ?",
"no_board_selected_warning": "Veuillez sélectionner un tableau avant de publier.",
"invalid_url_alert": "Le lien fourni n'est pas une URL valide.",
"empty_comment_alert": "Impossible de publier un commentaire vide."
"empty_comment_alert": "Impossible de publier un commentaire vide.",
"more_posts_last_week": "Affichage des publications depuis {{currentTimeFilterName}}, <1>afficher plus de publications de la semaine dernière</1>",
"more_posts_last_month": "Affichage des publications depuis {{currentTimeFilterName}}, <1>recharger plus de publications du mois dernier</1>",
"newer_posts_available": "Nouveaux messages disponibles, <1>recharger le fil</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "הפוסט שלך חסר קישור עם תמונה, הוא ית过滤 ברירת מחדל ב-Plebchan. אתה יכול לבטל את הפילטר הזה בהגדרות, או לצפות בפוסט שלך בלקוח Plebbit אחר, כמו Seedit. לפרסם בכל מקרה?",
"no_board_selected_warning": "אנא בחר לוח לפני פרסום.",
"invalid_url_alert": "הלינק שסופק אינו כתובת URL חוקית.",
"empty_comment_alert": "לא ניתן לפרסם תגובה ריקה."
"empty_comment_alert": "לא ניתן לפרסם תגובה ריקה.",
"more_posts_last_week": "מראה פוסטים מאז {{currentTimeFilterName}}, <1>הצג פוסטים נוספים מהשבוע שעבר</1>",
"more_posts_last_month": "מראה פוסטים מאז {{currentTimeFilterName}}, <1>הצג פוסטים נוספים מהחודש שעבר</1>",
"newer_posts_available": "ישנם פוסטים חדשים זמינים, <1>טען מחדש את הפיד</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "आपके पोस्ट में एक छवि के साथ लिंक नहीं है, इसे डिफ़ॉल्ट रूप से Plebchan पर फ़िल्टर किया जाएगा। आप सेटिंग्स में इस फ़िल्टर को अक्षम कर सकते हैं, या अपने पोस्ट को किसी अन्य Plebbit क्लाइंट, जैसे Seedit पर देख सकते हैं। फिर भी पोस्ट करें?",
"no_board_selected_warning": "पोस्ट करने से पहले कृपया एक बोर्ड चुनें।",
"invalid_url_alert": "प्रदत्त लिंक एक वैध URL नहीं है।",
"empty_comment_alert": "खाली टिप्पणी पोस्ट नहीं कर सकते।"
"empty_comment_alert": "खाली टिप्पणी पोस्ट नहीं कर सकते।",
"more_posts_last_week": "आप {{currentTimeFilterName}} से पोस्ट देख रहे हैं, <1>पिछले सप्ताह के और पोस्ट दिखाएं</1>",
"more_posts_last_month": "आप {{currentTimeFilterName}} से पोस्ट देख रहे हैं, <1>पिछले महीने के और पोस्ट दिखाएं</1>",
"newer_posts_available": "नए पोस्ट उपलब्ध हैं, <1>फीड रीलोड करें</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "A bejegyzésedből hiányzik egy képet tartalmazó hivatkozás, alapértelmezés szerint kiszűrésre kerül a Plebchan-on. Ezt a szűrőt letilthatod a beállításokban, vagy megnézheted a bejegyzésedet egy másik Plebbit kliensben, például a Seedit-en. Mégis posztolj?",
"no_board_selected_warning": "Kérlek, válassz egy táblát a közzététel előtt.",
"invalid_url_alert": "A megadott hivatkozás nem érvényes URL.",
"empty_comment_alert": "Üres hozzászólást nem lehet közzétenni."
"empty_comment_alert": "Üres hozzászólást nem lehet közzétenni.",
"more_posts_last_week": "A {{currentTimeFilterName}} óta látható bejegyzések, <1>további bejegyzések megjelenítése az előző héten</1>",
"more_posts_last_month": "A {{currentTimeFilterName}} óta látható bejegyzések, <1>további bejegyzések megjelenítése az előző hónapban</1>",
"newer_posts_available": "Új bejegyzések állnak rendelkezésre, <1>frissítse az adatfolyamot</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "Postingan Anda tidak memiliki tautan dengan gambar, itu akan difilter secara default di Plebchan. Anda dapat menonaktifkan filter ini di pengaturan, atau melihat postingan Anda di klien Plebbit lain, seperti Seedit. Posting saja?",
"no_board_selected_warning": "Silakan pilih papan sebelum memposting.",
"invalid_url_alert": "Tautan yang diberikan bukan URL yang valid.",
"empty_comment_alert": "Tidak dapat memposting komentar kosong."
"empty_comment_alert": "Tidak dapat memposting komentar kosong.",
"more_posts_last_week": "Menampilkan pos sejak {{currentTimeFilterName}}, <1>tampilkan lebih banyak pos dari minggu lalu</1>",
"more_posts_last_month": "Menampilkan pos sejak {{currentTimeFilterName}}, <1>tampilkan lebih banyak pos dari bulan lalu</1>",
"newer_posts_available": "Postingan terbaru tersedia, <1>muat ulang umpan</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "Il tuo post è privo di un link con un'immagine, verrà filtrato per impostazione predefinita su Plebchan. Puoi disabilitare questo filtro nelle impostazioni o visualizzare il tuo post su un altro client Plebbit, come Seedit. Pubblica comunque?",
"no_board_selected_warning": "Per favore, seleziona una board prima di pubblicare.",
"invalid_url_alert": "Il link fornito non è un URL valido.",
"empty_comment_alert": "Impossibile pubblicare un commento vuoto."
"empty_comment_alert": "Impossibile pubblicare un commento vuoto.",
"more_posts_last_week": "Stai visualizzando post da {{currentTimeFilterName}}, <1>visualizza più post della scorsa settimana</1>",
"more_posts_last_month": "Stai visualizzando post da {{currentTimeFilterName}}, <1>visualizza più post del mese scorso</1>",
"newer_posts_available": "Nuovi post disponibili, <1>ricarica il feed</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "あなたの投稿には画像付きのリンクがありません。それはPlebchanでデフォルトでフィルタリングされます。このフィルタを設定で無効にするか、Seeditのような別のPlebbitクライアントで投稿を表示できます。それでも投稿しますか?",
"no_board_selected_warning": "投稿する前にボードを選択してください。",
"invalid_url_alert": "提供されたリンクは有効なURLではありません。",
"empty_comment_alert": "空のコメントは投稿できません。"
"empty_comment_alert": "空のコメントは投稿できません。",
"more_posts_last_week": "{{currentTimeFilterName}} からの投稿を表示しています。<1>先週の投稿をさらに表示</1>",
"more_posts_last_month": "{{currentTimeFilterName}} からの投稿を表示しています。<1>先月の投稿をさらに表示</1>",
"newer_posts_available": "新しい投稿が利用可能です、<1>フィードを再読み込み</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "당신의 게시물에는 이미지가 포함된 링크가 없습니다. 기본적으로 Plebchan에서 필터링됩니다. 설정에서 이 필터를 비활성화하거나 Seedit와 같은 다른 Plebbit 클라이언트에서 게시물을 볼 수 있습니다. 그래도 게시하시겠습니까?",
"no_board_selected_warning": "게시하기 전에 보드를 선택하십시오.",
"invalid_url_alert": "제공된 링크는 유효한 URL이 아닙니다.",
"empty_comment_alert": "빈 댓글을 게시할 수 없습니다."
"empty_comment_alert": "빈 댓글을 게시할 수 없습니다.",
"more_posts_last_week": "{{currentTimeFilterName}} 이후의 게시물이 표시됩니다. <1>지난 주의 게시물 더 보기</1>",
"more_posts_last_month": "{{currentTimeFilterName}} 이후의 게시물이 표시됩니다. <1>지난달의 게시물 더 보기</1>",
"newer_posts_available": "새로운 게시물이 있습니다, <1>피드를 새로 고침</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "तुमच्या पोस्टमध्ये चित्रासह लिंक नाही, हे डिफॉल्टवर Plebchan द्वारे फिल्टर केले जाईल. तुम्ही सेटिंग्जमध्ये हा फिल्टर अक्षम करू शकता किंवा Seedit सारख्या दुसऱ्या Plebbit क्लायंटवर तुमचा पोस्ट पाहू शकता. तरीही पोस्ट करायचे आहे का?",
"no_board_selected_warning": "पोस्ट करण्यापूर्वी कृपया एक बोर्ड निवडा.",
"invalid_url_alert": "दिलेला लिंक वैध URL नाही.",
"empty_comment_alert": "रिक्त टिप्पणी पोस्ट करू शकत नाही."
"empty_comment_alert": "रिक्त टिप्पणी पोस्ट करू शकत नाही.",
"more_posts_last_week": "{{currentTimeFilterName}} पासून पोस्ट दर्शवित आहेत, <1>गेल्या आठवड्यातील अधिक पोस्ट दर्शवा</1>",
"more_posts_last_month": "{{currentTimeFilterName}} पासून पोस्ट दर्शवित आहेत, <1>गेल्या महिन्यातील अधिक पोस्ट दर्शवा</1>",
"newer_posts_available": "नवीनतम पोस्ट उपलब्ध आहेत, <1>फीड रीलोड करा</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "Je bericht mist een link met een afbeelding, deze zal standaard op Plebchan worden gefilterd. Je kunt deze filter in de instellingen uitschakelen of je bericht bekijken op een andere Plebbit-client, zoals Seedit. Toch posten?",
"no_board_selected_warning": "Selecteer een bord voordat je plaatst.",
"invalid_url_alert": "De opgegeven link is geen geldige URL.",
"empty_comment_alert": "Kan geen lege opmerking plaatsen."
"empty_comment_alert": "Kan geen lege opmerking plaatsen.",
"more_posts_last_week": "Berichten sinds {{currentTimeFilterName}}, <1>meer berichten van afgelopen week tonen</1>",
"more_posts_last_month": "Berichten sinds {{currentTimeFilterName}}, <1>meer berichten van afgelopen maand tonen</1>",
"newer_posts_available": "Nieuwere berichten beschikbaar, <1>herlaad feed</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "Innlegget ditt mangler en lenke med et bilde, og vil bli filtrert som standard på Plebchan. Du kan deaktivere dette filteret i innstillingene, eller se innlegget ditt på en annen Plebbit-klient, som Seedit. Vil du legge ut uansett?",
"no_board_selected_warning": "Vennligst velg et brett før du poster.",
"invalid_url_alert": "Den angitte lenken er ikke en gyldig URL.",
"empty_comment_alert": "Kan ikke poste tom kommentar."
"empty_comment_alert": "Kan ikke poste tom kommentar.",
"more_posts_last_week": "Viser innlegg siden {{currentTimeFilterName}}, <1>vis flere innlegg fra forrige uke</1>",
"more_posts_last_month": "Viser innlegg siden {{currentTimeFilterName}}, <1>vis flere innlegg fra forrige måned</1>",
"newer_posts_available": "Nyere innlegg tilgjengelige, <1>oppdater feed</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "Twój post nie zawiera linku z obrazem, domyślnie zostanie przefiltrowany na Plebchan. Możesz wyłączyć ten filtr w ustawieniach lub zobaczyć swój post w innym kliencie Plebbit, takim jak Seedit. Chcesz opublikować mimo to?",
"no_board_selected_warning": "Proszę wybrać tablicę przed opublikowaniem.",
"invalid_url_alert": "Podany link nie jest poprawnym adresem URL.",
"empty_comment_alert": "Nie można opublikować pustego komentarza."
"empty_comment_alert": "Nie można opublikować pustego komentarza.",
"more_posts_last_week": "Pokazuje posty od {{currentTimeFilterName}}, <1>pokaż więcej postów z zeszłego tygodnia</1>",
"more_posts_last_month": "Pokazuje posty od {{currentTimeFilterName}}, <1>pokaż więcej postów z zeszłego miesiąca</1>",
"newer_posts_available": "Dostępne są nowsze posty, <1>przeładuj kanał</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "Sua postagem está faltando um link com uma imagem, ela será filtrada por padrão no Plebchan. Você pode desativar esse filtro nas configurações ou visualizar sua postagem em outro cliente Plebbit, como o Seedit. Postar mesmo assim?",
"no_board_selected_warning": "Por favor, selecione um quadro antes de postar.",
"invalid_url_alert": "O link fornecido não é uma URL válida.",
"empty_comment_alert": "Não é possível postar um comentário vazio."
"empty_comment_alert": "Não é possível postar um comentário vazio.",
"more_posts_last_week": "Exibindo postagens desde {{currentTimeFilterName}}, <1>mostrar mais postagens da semana passada</1>",
"more_posts_last_month": "Exibindo postagens desde {{currentTimeFilterName}}, <1>atualize o feed com mais postagens do mês passado</1>",
"newer_posts_available": "Novos posts disponíveis, <1>atualize o feed</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "Postarea ta nu are un link cu o imagine, va fi filtrată în mod implicit pe Plebchan. Poți dezactiva acest filtru în setări sau vizualiza postarea ta pe un alt client Plebbit, cum ar fi Seedit. Postează oricum?",
"no_board_selected_warning": "Vă rugăm să selectați un tablou înainte de a publica.",
"invalid_url_alert": "Linkul furnizat nu este un URL valid.",
"empty_comment_alert": "Nu se poate publica un comentariu gol."
"empty_comment_alert": "Nu se poate publica un comentariu gol.",
"more_posts_last_week": "Afișând postări din {{currentTimeFilterName}}, <1>afișați mai multe postări din săptămâna trecută</1>",
"more_posts_last_month": "Afișând postări din {{currentTimeFilterName}}, <1>afișați mai multe postări din luna trecută</1>",
"newer_posts_available": "Postări mai recente disponibile, <1>reîncarcă fluxul</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "Ваш пост не содержит ссылки с изображением, он будет отфильтрован по умолчанию на Plebchan. Вы можете отключить этот фильтр в настройках или просмотреть свой пост в другом клиенте Plebbit, таком как Seedit. Опубликовать все равно?",
"no_board_selected_warning": "Пожалуйста, выберите доску перед публикацией.",
"invalid_url_alert": "Предоставленная ссылка не является действительным URL.",
"empty_comment_alert": "Невозможно опубликовать пустой комментарий."
"empty_comment_alert": "Невозможно опубликовать пустой комментарий.",
"more_posts_last_week": "Показаны записи с {{currentTimeFilterName}}, <1>показать больше записей за прошлую неделю</1>",
"more_posts_last_month": "Показаны записи с {{currentTimeFilterName}}, <1>показать больше записей за прошлый месяц</1>",
"newer_posts_available": "Доступны более новые записи, <1>обновите ленту</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "Postimi juaj i mungon një lidhje me një imazh, ai do të filtrohet automatikisht në Plebchan. Mund ta çaktivizoni këtë filtër në cilësimet, ose të shihni postimin tuaj në një klient tjetër Plebbit, si Seedit. A dëshironi të postoni gjithsesi?",
"no_board_selected_warning": "Ju lutemi zgjidhni një bord para se të postoni.",
"invalid_url_alert": "Lidhja e ofruar nuk është një URL e vlefshme.",
"empty_comment_alert": "Nuk mund të postoni një koment bosh."
"empty_comment_alert": "Nuk mund të postoni një koment bosh.",
"more_posts_last_week": "Duke treguar postimet që nga {{currentTimeFilterName}}, <1>tregoni më shumë postime nga java e kaluar</1>",
"more_posts_last_month": "Duke treguar postimet që nga {{currentTimeFilterName}}, <1>tregoni më shumë postime nga java e kaluar</1>",
"newer_posts_available": "Postime më të reja janë të disponueshme, <1>rifreskoje fluxin</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "Ditt inlägg saknar en länk med en bild, det kommer att filtreras som standard på Plebchan. Du kan inaktivera detta filter i inställningarna, eller visa ditt inlägg på en annan Plebbit-klient, som Seedit. Vill du ändå posta?",
"no_board_selected_warning": "Vänligen välj en tavla innan du postar.",
"invalid_url_alert": "Den angivna länken är inte en giltig URL.",
"empty_comment_alert": "Kan inte posta tom kommentar."
"empty_comment_alert": "Kan inte posta tom kommentar.",
"more_posts_last_week": "Visar inlägg sedan {{currentTimeFilterName}}, <1>visa fler inlägg från förra veckan</1>",
"more_posts_last_month": "Visar inlägg sedan {{currentTimeFilterName}}, <1>visa fler inlägg från förra månaden</1>",
"newer_posts_available": "Nyare inlägg tillgängliga, <1>ladda om flödet</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "మీ పోస్టు చిత్రంతో లింక్ మిస్సింగ్ ఉంది, ఇది Plebchan లో డిఫాల్ట్‌గా ఫిల్టర్ అవుతుంది. మీరు ఈ ఫిల్టర్‌ను సెటింగ్స్ లో డిసేబుల్ చేయవచ్చు, లేదా Seedit వంటి మరో Plebbit క్లయింట్ లో మీ పోస్టును చూడవచ్చు. అయినప్పటికీ పోస్టు చేయాలా?",
"no_board_selected_warning": "పోస్ట్ చేయడానికి ముందు దయచేసి ఒక బోర్డు ఎంచుకోండి.",
"invalid_url_alert": "నివేదిక ఇవ్వబడిన లింక్ చెల్లుబాటులో లేని URL.",
"empty_comment_alert": "ఖాళీ వ్యాఖ్యను పోస్ట్ చేయలేరు."
"empty_comment_alert": "ఖాళీ వ్యాఖ్యను పోస్ట్ చేయలేరు.",
"more_posts_last_week": "{{currentTimeFilterName}} నుండి పోస్టులు చూపిస్తున్నాయి, <1>గత వారంనుంచి మరిన్ని పోస్టులను చూపించు</1>",
"more_posts_last_month": "{{currentTimeFilterName}} నుండి పోస్టులు చూపిస్తున్నాయి, <1>గత నెల నుండి మరిన్ని పోస్టులను చూపించండి</1>",
"newer_posts_available": "కొత్త పోస్టులు అందుబాటులో ఉన్నాయి, <1>ఫీడ్‌ను తిరిగి లోడ్ చేయండి</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "โพสต์ของคุณขาดลิงก์ที่มีรูปภาพจะถูกกรองตามค่าเริ่มต้นใน Plebchan คุณสามารถปิดการกรองนี้ในการตั้งค่าหรือดูโพสต์ของคุณในไคลเอนต์ Plebbit อื่น เช่น Seedit คุณจะโพสต์อยู่ดีไหม?",
"no_board_selected_warning": "กรุณาเลือกกระดานก่อนที่จะโพสต์.",
"invalid_url_alert": "ลิงก์ที่ให้ไม่ใช่ URL ที่ถูกต้อง",
"empty_comment_alert": "ไม่สามารถโพสต์ความคิดเห็นว่างเปล่าได้"
"empty_comment_alert": "ไม่สามารถโพสต์ความคิดเห็นว่างเปล่าได้",
"more_posts_last_week": "แสดงโพสต์ตั้งแต่ {{currentTimeFilterName}} <1>แสดงโพสต์เพิ่มเติมจากสัปดาห์ที่แล้ว</1>",
"more_posts_last_month": "โพสต์ตั้งแต่ {{currentTimeFilterName}} <1>แสดงโพสต์เพิ่มเติมจากเดือนที่แล้ว</1>",
"newer_posts_available": "โพสต์ใหม่พร้อมใช้งาน, <1>โหลดฟีดใหม่</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "Gönderinizin bir resmi içeren bağlantısı yok, varsayılan olarak Plebchan'da filtrelenecek. Bu filtreyi ayarlardan devre dışı bırakabilir veya gönderinizi Seedit gibi başka bir Plebbit istemcisinde görüntüleyebilirsiniz. Yine de gönderi paylaşmak ister misiniz?",
"no_board_selected_warning": "Lütfen gönderimden önce bir pano seçin.",
"invalid_url_alert": "Verilen bağlantı geçerli bir URL değildir.",
"empty_comment_alert": "Boş yorum gönderilemez."
"empty_comment_alert": "Boş yorum gönderilemez.",
"more_posts_last_week": "{{currentTimeFilterName}} tarihinden itibaren gönderiler gösteriliyor, <1>geçen haftadan daha fazla gönderi göster</1>",
"more_posts_last_month": "{{currentTimeFilterName}} tarihinden itibaren gönderiler gösteriliyor, <1>geçen aydan daha fazla gönderi göster</1>",
"newer_posts_available": "Daha yeni gönderiler mevcut, <1>beslemeyi yenile</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "Ваш пост не має посилання з зображенням, за замовчуванням він буде відфільтровано на Plebchan. Ви можете вимкнути цей фільтр у налаштуваннях або переглянути свій пост в іншому клієнті Plebbit, такому як Seedit. Все ж таки опублікувати?",
"no_board_selected_warning": "Будь ласка, виберіть дошку перед публікацією.",
"invalid_url_alert": "Надане посилання не є дійсною URL-адресою.",
"empty_comment_alert": "Не можна опублікувати порожній коментар."
"empty_comment_alert": "Не можна опублікувати порожній коментар.",
"more_posts_last_week": "Показані пости з {{currentTimeFilterName}}, <1>показати більше постів з минулого тижня</1>",
"more_posts_last_month": "Показані пости з {{currentTimeFilterName}}, <1>показати більше постів з минулого місяця</1>",
"newer_posts_available": "Доступні нові пости, <1>перезавантажити стрічку</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "آپ کی پوسٹ میں ایک تصویر کے ساتھ ایک لنک غائب ہے، یہ Plebchan پر ڈیفالٹ کے طور پر فلٹر ہوگی۔ آپ اس فلٹر کو سیٹنگز میں غیر فعال کرسکتے ہیں، یا اپنے پوسٹ کو کسی دوسرے Plebbit کلائنٹ، جیسے Seedit پر دیکھ سکتے ہیں۔ پھر بھی پوسٹ کریں؟",
"no_board_selected_warning": "براہ کرم پوسٹ کرنے سے پہلے ایک بورڈ منتخب کریں۔",
"invalid_url_alert": "فراہم کردہ لنک درست URL نہیں ہے۔",
"empty_comment_alert": "خالی تبصرہ پوسٹ نہیں کیا جا سکتا۔"
"empty_comment_alert": "خالی تبصرہ پوسٹ نہیں کیا جا سکتا۔",
"more_posts_last_week": "{{currentTimeFilterName}} سے پوسٹ دکھا رہا ہے، <1>پچھلے ہفتے کے مزید پوسٹس دکھائیں</1>",
"more_posts_last_month": "{{currentTimeFilterName}} سے پوسٹ دکھا رہا ہے، <1>پچھلے مہینے کے مزید پوسٹس دکھائیں</1>",
"newer_posts_available": "نئے پوسٹس دستیاب ہیں، <1>فیڈ کو دوبارہ لوڈ کریں</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "Bài đăng của bạn thiếu một liên kết với hình ảnh, nó sẽ bị lọc theo mặc định trên Plebchan. Bạn có thể tắt bộ lọc này trong cài đặt hoặc xem bài đăng của bạn trên một khách hàng Plebbit khác, như Seedit. Vẫn muốn đăng?",
"no_board_selected_warning": "Vui lòng chọn một bảng trước khi đăng.",
"invalid_url_alert": "Liên kết được cung cấp không phải là một URL hợp lệ.",
"empty_comment_alert": "Không thể đăng bình luận trống."
"empty_comment_alert": "Không thể đăng bình luận trống.",
"more_posts_last_week": "Đang hiển thị bài viết từ {{currentTimeFilterName}}, <1>hiển thị thêm bài viết từ tuần trước</1>",
"more_posts_last_month": "Đang hiển thị bài viết từ {{currentTimeFilterName}}, <1>hiển thị thêm bài viết từ tháng trước</1>",
"newer_posts_available": "Có bài viết mới, <1>tải lại nguồn cấp dữ liệu</1>"
}
+4 -1
View File
@@ -236,5 +236,8 @@
"missing_link_confirm": "您的帖子缺少带图像的链接,默认情况下将在 Plebchan 中被过滤。您可以在设置中禁用此过滤器,或者在另一个 Plebbit 客户端(如 Seedit)中查看您的帖子。还是发布吗?",
"no_board_selected_warning": "请在发布之前选择一个板块。",
"invalid_url_alert": "提供的链接不是有效的 URL。",
"empty_comment_alert": "无法发布空评论。"
"empty_comment_alert": "无法发布空评论。",
"more_posts_last_week": "显示自 {{currentTimeFilterName}} 以来的帖子,<1>显示上周的更多帖子</1>",
"more_posts_last_month": "显示自 {{currentTimeFilterName}} 以来的帖子,<1>显示上个月的更多帖子</1>",
"newer_posts_available": "新帖子可用,<1>重新加载源</1>"
}
+14 -3
View File
@@ -1,10 +1,10 @@
import { useEffect, useRef, useState } from 'react';
import { useEffect, useMemo, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { useTranslation } from 'react-i18next';
import { Link, useLocation, useParams } from 'react-router-dom';
import { Comment, useComment } from '@plebbit/plebbit-react-hooks';
import { useFloating, offset, size, autoUpdate, Placement } from '@floating-ui/react';
import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
import { fetchWebpageThumbnailIfNeeded, getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
import { getFormattedTimeAgo } from '../../lib/utils/time-utils';
import { isAllView } from '../../lib/utils/view-utils';
import useCatalogStyleStore from '../../stores/use-catalog-style-store';
@@ -114,8 +114,19 @@ const CatalogPost = ({ post }: { post: Comment }) => {
title,
} = post || {};
const linkCount = useCountLinksInReplies(post);
const commentMediaInfo = getCommentMediaInfo(post);
// some sites have CORS access, so the thumbnail can be fetched client-side, which is helpful if subplebbit.settings.fetchThumbnailUrls is false
const initialCommentMediaInfo = useMemo(() => getCommentMediaInfo(post), [post]);
const [commentMediaInfo, setCommentMediaInfo] = useState(initialCommentMediaInfo);
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
useEffect(() => {
if (initialCommentMediaInfo?.type === 'webpage' && !initialCommentMediaInfo.thumbnail) {
fetchWebpageThumbnailIfNeeded(initialCommentMediaInfo).then(setCommentMediaInfo);
} else {
setCommentMediaInfo(initialCommentMediaInfo);
}
}, [initialCommentMediaInfo]);
const { hidden } = useHide({ cid });
const location = useLocation();
+13 -3
View File
@@ -1,10 +1,10 @@
import { useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { Link, useLocation, useParams } from 'react-router-dom';
import { Comment, useAuthorAvatar, useComment, useEditedComment, useSubplebbit } from '@plebbit/plebbit-react-hooks';
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
import styles from '../../views/post/post.module.css';
import { getCommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail, getMediaDimensions } from '../../lib/utils/media-utils';
import { fetchWebpageThumbnailIfNeeded, getCommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail, getMediaDimensions } from '../../lib/utils/media-utils';
import { hashStringToColor, getTextColorForBackground } from '../../lib/utils/post-utils';
import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-utils';
import { isValidURL } from '../../lib/utils/url-utils';
@@ -208,7 +208,17 @@ const PostInfo = ({ openReplyModal, post, postReplyCount = 0, roles, isHidden }:
const PostMedia = ({ post }: PostProps) => {
const { t } = useTranslation();
const { link, spoiler } = post || {};
const commentMediaInfo = getCommentMediaInfo(post);
// some sites have CORS access, so the thumbnail can be fetched client-side, which is helpful if subplebbit.settings.fetchThumbnailUrls is false
const initialCommentMediaInfo = useMemo(() => getCommentMediaInfo(post), [post]);
const [commentMediaInfo, setCommentMediaInfo] = useState(initialCommentMediaInfo);
useEffect(() => {
if (initialCommentMediaInfo?.type === 'webpage' && !initialCommentMediaInfo.thumbnail) {
fetchWebpageThumbnailIfNeeded(initialCommentMediaInfo).then(setCommentMediaInfo);
} else {
setCommentMediaInfo(initialCommentMediaInfo);
}
}, [initialCommentMediaInfo]);
const { url } = commentMediaInfo || {};
let type = commentMediaInfo?.type;
+13 -3
View File
@@ -1,10 +1,10 @@
import { useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { Link, useLocation, useParams } from 'react-router-dom';
import { Comment, useAuthorAvatar, useComment, useEditedComment, useSubplebbit } from '@plebbit/plebbit-react-hooks';
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
import styles from '../../views/post/post.module.css';
import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
import { fetchWebpageThumbnailIfNeeded, getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
import { getTextColorForBackground, hashStringToColor } from '../../lib/utils/post-utils';
import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-utils';
import { isAllView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils';
@@ -40,7 +40,17 @@ const PostInfoAndMedia = ({ openReplyModal, post, postReplyCount = 0, roles }: P
const isInPostPageView = isPostPageView(location.pathname, params);
const isInSubscriptionsView = isSubscriptionsView(location.pathname, params);
const commentMediaInfo = getCommentMediaInfo(post);
// some sites have CORS access, so the thumbnail can be fetched client-side, which is helpful if subplebbit.settings.fetchThumbnailUrls is false
const initialCommentMediaInfo = useMemo(() => getCommentMediaInfo(post), [post]);
const [commentMediaInfo, setCommentMediaInfo] = useState(initialCommentMediaInfo);
useEffect(() => {
if (initialCommentMediaInfo?.type === 'webpage' && !initialCommentMediaInfo.thumbnail) {
fetchWebpageThumbnailIfNeeded(initialCommentMediaInfo).then(setCommentMediaInfo);
} else {
setCommentMediaInfo(initialCommentMediaInfo);
}
}, [initialCommentMediaInfo]);
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
const [showThumbnail, setShowThumbnail] = useState(true);
+50 -30
View File
@@ -4,48 +4,68 @@ import { getCommentMediaInfo, getHasThumbnail } from '../lib/utils/media-utils';
const usePopularPosts = (subplebbits: Subplebbit[]) => {
const [popularPosts, setPopularPosts] = useState<Comment[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const uniqueLinks: Set<string> = new Set();
const allPosts: Comment[] = [];
const fetchPopularPosts = () => {
try {
setIsLoading(true);
setError(null);
const uniqueLinks: Set<string> = new Set();
const allPosts: Comment[] = [];
const postsPerSub = [0, 8, 4, 3, 2, 2, 2, 2, 1][Math.min(subplebbits.length, 8)];
const postsPerSub = [0, 8, 4, 3, 2, 2, 2, 2, 1][Math.min(subplebbits.length, 8)];
subplebbits.forEach((subplebbit: any) => {
let subplebbitPosts: Comment[] = [];
subplebbits.forEach((subplebbit: any) => {
let subplebbitPosts: Comment[] = [];
if (subplebbit?.posts?.pages?.hot?.comments) {
for (const post of Object.values(subplebbit.posts.pages.hot.comments as Comment)) {
const { deleted, link, locked, pinned, removed, replyCount, timestamp } = post;
const commentMediaInfo = getCommentMediaInfo(post);
const isMediaShowed = getHasThumbnail(commentMediaInfo, link);
if (subplebbit?.posts?.pages?.hot?.comments) {
for (const post of Object.values(subplebbit.posts.pages.hot.comments as Comment)) {
const { deleted, link, locked, pinned, removed, replyCount, timestamp } = post;
if (
isMediaShowed &&
(replyCount > 0 || postsPerSub > 1) &&
!deleted &&
!removed &&
!locked &&
!pinned &&
timestamp > Date.now() / 1000 - 60 * 60 * 24 * 30 &&
!uniqueLinks.has(link)
) {
subplebbitPosts.push(post);
uniqueLinks.add(link);
try {
const commentMediaInfo = getCommentMediaInfo(post);
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
if (
hasThumbnail &&
(replyCount > 0 || postsPerSub > 1) &&
!deleted &&
!removed &&
!locked &&
!pinned &&
timestamp > Date.now() / 1000 - 60 * 60 * 24 * 30 &&
!uniqueLinks.has(link)
) {
subplebbitPosts.push(post);
uniqueLinks.add(link);
}
} catch (err) {
console.error('Error processing post:', err);
}
}
subplebbitPosts.sort((a: any, b: any) => b.timestamp - a.timestamp);
allPosts.push(...subplebbitPosts.slice(0, postsPerSub));
}
}
});
subplebbitPosts.sort((a: any, b: any) => b.timestamp - a.timestamp);
allPosts.push(...subplebbitPosts.slice(0, postsPerSub));
const sortedPosts = allPosts.sort((a: any, b: any) => b.timestamp - a.timestamp).slice(0, 8);
setPopularPosts(sortedPosts);
} catch (err) {
console.error('Error in usePopularPosts:', err);
setError('Failed to fetch popular posts');
} finally {
setIsLoading(false);
}
});
};
const sortedPosts = allPosts.sort((a: any, b: any) => b.timestamp - a.timestamp).slice(0, 8);
setPopularPosts(sortedPosts);
fetchPopularPosts();
}, [subplebbits]);
return popularPosts;
return { popularPosts, isLoading, error };
};
export default usePopularPosts;
@@ -6,27 +6,27 @@ const lastVisitTimestamp = localStorage.getItem('plebchanLastVisitTimestamp');
// update the last visited timestamp every n seconds
setInterval(() => {
localStorage.setItem('plebchanLastVisitTimestamp', Date.now());
localStorage.setItem('plebchanLastVisitTimestamp', Date.now().toString());
}, 60 * 1000);
const timeFilterNamesToSeconds = {
const timeFilterNamesToSeconds: Record<string, number | undefined> = {
'1h': 60 * 60,
'12h': 60 * 60 * 12,
'24h': 60 * 60 * 24,
'48h': 60 * 60 * 24 * 2,
week: 60 * 60 * 24 * 7,
month: 60 * 60 * 24 * 30,
year: 60 * 60 * 24 * 365,
'1w': 60 * 60 * 24 * 7,
'1m': 60 * 60 * 24 * 30,
'1y': 60 * 60 * 24 * 365,
all: undefined,
};
// calculate the last visit timeFilterNamesToSeconds
const secondsSinceLastVisit = lastVisitTimestamp ? (Date.now() - lastVisitTimestamp) / 1000 : Infinity;
const secondsSinceLastVisit = lastVisitTimestamp ? (Date.now() - parseInt(lastVisitTimestamp, 10)) / 1000 : Infinity;
const day = 24 * 60 * 60;
let lastVisitTimeFilterName;
let lastVisitTimeFilterName: string | undefined;
if (secondsSinceLastVisit > 30 * day) {
lastVisitTimeFilterName = 'month';
timeFilterNamesToSeconds[lastVisitTimeFilterName] = timeFilterNamesToSeconds['month'];
lastVisitTimeFilterName = '1m';
timeFilterNamesToSeconds[lastVisitTimeFilterName] = timeFilterNamesToSeconds['1m'];
} else if (secondsSinceLastVisit > 7 * day) {
const weeks = Math.ceil(secondsSinceLastVisit / day / 7);
lastVisitTimeFilterName = `${weeks}w`;
@@ -40,7 +40,7 @@ if (secondsSinceLastVisit > 30 * day) {
timeFilterNamesToSeconds[lastVisitTimeFilterName] = timeFilterNamesToSeconds['24h'];
}
export const timeFilterNames = [lastVisitTimeFilterName, '1h', '12h', '24h', '48h', 'week', 'month', 'year', 'all'];
export const timeFilterNames = [lastVisitTimeFilterName, '1h', '12h', '24h', '48h', '1w', '1m', '1y', 'all'];
const useTimeFilter = () => {
const params = useParams();
@@ -52,9 +52,9 @@ const useTimeFilter = () => {
}
assert(!timeFilterName || typeof timeFilterName === 'string', `useTimeFilter timeFilterName argument '${timeFilterName}' not a string`);
const timeFilterSeconds = timeFilterNamesToSeconds[timeFilterName];
const timeFilterSeconds = timeFilterNamesToSeconds[timeFilterName as keyof typeof timeFilterNamesToSeconds];
assert(!timeFilterName || timeFilterName === 'all' || timeFilterSeconds !== undefined, `useTimeFilter no filter for timeFilterName '${timeFilterName}'`);
return { timeFilterSeconds, timeFilterNames };
return { timeFilterSeconds, timeFilterNames, timeFilterName };
};
export default useTimeFilter;
+40 -2
View File
@@ -80,8 +80,7 @@ export const getLinkMediaInfo = memoize(
}
try {
const fileName = url.pathname.slice(url.pathname.lastIndexOf('/') + 1).toLowerCase();
mime = extName(fileName)[0]?.mime;
mime = extName(new URL(link).pathname.toLowerCase().replace('/', ''))[0]?.mime;
if (mime) {
if (mime.startsWith('image')) {
type = mime === 'image/gif' ? 'gif' : 'image';
@@ -92,6 +91,10 @@ export const getLinkMediaInfo = memoize(
}
}
if (!url.pathname.includes('.')) {
type = 'webpage';
}
if (canEmbed(url) || url.host.startsWith('yt.')) {
type = 'iframe';
patternThumbnailUrl = getPatternThumbnailUrl(url);
@@ -105,6 +108,41 @@ export const getLinkMediaInfo = memoize(
{ max: 1000 },
);
// some sites have CORS access, so the thumbnail can be fetched client-side, which is helpful if subplebbit.settings.fetchThumbnailUrls is false
const fetchWebpageThumbnail = async (url: string): Promise<string | undefined> => {
try {
const response = await fetch(url);
const html = await response.text();
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
// Try to find Open Graph image
const ogImage = doc.querySelector('meta[property="og:image"]');
if (ogImage && ogImage.getAttribute('content')) {
return ogImage.getAttribute('content')!;
}
// If no Open Graph image, try to find the first image
const firstImage = doc.querySelector('img');
if (firstImage && firstImage.getAttribute('src')) {
return new URL(firstImage.getAttribute('src')!, url).href;
}
return undefined;
} catch (error) {
console.error('Error fetching webpage thumbnail:', error);
return undefined;
}
};
export const fetchWebpageThumbnailIfNeeded = async (commentMediaInfo: CommentMediaInfo): Promise<CommentMediaInfo> => {
if (commentMediaInfo.type === 'webpage' && !commentMediaInfo.thumbnail) {
const thumbnail = await fetchWebpageThumbnail(commentMediaInfo.url);
return { ...commentMediaInfo, thumbnail };
}
return commentMediaInfo;
};
export const getCommentMediaInfo = (comment: Comment): CommentMediaInfo | undefined => {
if (!comment?.thumbnailUrl && !comment?.link) {
return;
+10 -1
View File
@@ -1,5 +1,14 @@
.footer {
padding-top: 10px;
padding: 15px 0 10px 5px;
}
.footer a {
text-decoration: var(--button-text-decoration);
color: var(--button-desktop-text-color);
}
.footer a:hover {
color: var(--button-desktop-text-color-hover);
}
.button {
+87 -6
View File
@@ -1,8 +1,8 @@
import { useEffect, useMemo, useRef } from 'react';
import { useLocation, useParams } from 'react-router-dom';
import { useEffect, useMemo, useRef, useState } from 'react';
import { Link, useLocation, useParams } from 'react-router-dom';
import { Comment, useAccount, useAccountComments, useBlock, useFeed, useSubplebbit } from '@plebbit/plebbit-react-hooks';
import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso';
import { useTranslation } from 'react-i18next';
import { Trans, useTranslation } from 'react-i18next';
import styles from './board.module.css';
import { isAllView, isSubscriptionsView } from '../../lib/utils/view-utils';
import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits';
@@ -53,7 +53,7 @@ const Board = () => {
}, [isInAllView, isInSubscriptionsView, subplebbitAddress, defaultSubplebbitAddresses, subscriptions]);
const { sortType } = useSortingStore();
const { timeFilterSeconds } = useTimeFilter();
const { timeFilterSeconds, timeFilterName } = useTimeFilter();
const feedOptions: any = useMemo(
() => ({
@@ -66,7 +66,7 @@ const Board = () => {
[subplebbitAddresses, sortType, timeFilterSeconds, isInAllView, isInSubscriptionsView, hideThreadsWithoutImages],
);
const { feed, hasMore, loadMore, reset } = useFeed(feedOptions);
const { feed, hasMore, loadMore, reset, subplebbitAddressesWithNewerPosts } = useFeed(feedOptions);
const { accountComments } = useAccountComments();
const resetTriggeredRef = useRef(false);
@@ -142,8 +142,89 @@ const Board = () => {
</div>
);
const handleNewerPostsButtonClick = () => {
window.scrollTo({ top: 0, left: 0 });
setTimeout(() => {
reset();
}, 300);
};
// suggest the user to change time filter if there aren't enough posts
const { feed: weeklyFeed } = useFeed({
subplebbitAddresses,
sortType,
newerThan: 60 * 60 * 24 * 7,
filter: hideThreadsWithoutImages ? threadsWithoutImagesFilter : undefined,
});
const { feed: monthlyFeed } = useFeed({
subplebbitAddresses,
sortType,
newerThan: 60 * 60 * 24 * 30,
filter: hideThreadsWithoutImages ? threadsWithoutImagesFilter : undefined,
});
const [showMorePostsSuggestion, setShowMorePostsSuggestion] = useState(false);
useEffect(() => {
const timer = setTimeout(() => {
setShowMorePostsSuggestion(true);
}, 5000);
return () => clearTimeout(timer);
}, []);
const params = useParams();
const currentTimeFilterName = params?.timeFilterName || timeFilterName;
const Footer = () => {
return <div className={styles.footer}>{loadingString}</div>;
let footerContent;
if (feed.length === 0) {
footerContent = t('no_posts');
}
if (hasMore || (subplebbitAddresses && subplebbitAddresses.length === 0)) {
footerContent = (
<>
{subplebbitAddressesWithNewerPosts.length > 0 ? (
<div className={styles.stateString}>
<Trans
i18nKey='newer_posts_available'
components={{
1: <span onClick={handleNewerPostsButtonClick} />,
}}
/>
</div>
) : (
(isInAllView || isInSubscriptionsView) &&
showMorePostsSuggestion &&
monthlyFeed.length > feed.length &&
(weeklyFeed.length > feed.length ? (
<div className={styles.stateString}>
<Trans
i18nKey='more_posts_last_week'
values={{ currentTimeFilterName }}
components={{
1: <Link to={(isInAllView ? '/p/all' : isInSubscriptionsView ? '/p/subscriptions' : `/p/${subplebbitAddress}`) + '/1w'} />,
}}
/>
</div>
) : (
<div className={styles.stateString}>
<Trans
i18nKey='more_posts_last_month'
values={{ currentTimeFilterName }}
components={{
1: <Link to={(isInAllView ? '/p/all' : isInSubscriptionsView ? '/p/subscriptions' : `/p/${subplebbitAddress}`) + '/1m'} />,
}}
/>
</div>
))
)}
<div className={styles.stateString}>
<LoadingEllipsis string={loadingStateString} />
</div>
</>
);
}
return <div className={styles.footer}>{footerContent}</div>;
};
// save the last Virtuoso state to restore it when navigating back
+10 -1
View File
@@ -1,5 +1,14 @@
.footer {
padding-top: 10px;
padding: 15px 0 10px 5px;
}
.footer a {
text-decoration: var(--button-text-decoration);
color: var(--button-desktop-text-color);
}
.footer a:hover {
color: var(--button-desktop-text-color-hover);
}
@media (max-width: 640px) {
+138 -49
View File
@@ -1,26 +1,33 @@
import { useEffect, useMemo, useRef } from 'react';
import { useLocation, useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useAccount, useFeed, useSubplebbit, useBlock } from '@plebbit/plebbit-react-hooks';
import { useEffect, useMemo, useRef, useState } from 'react';
import { Link, useLocation, useParams } from 'react-router-dom';
import { Trans, useTranslation } from 'react-i18next';
import { Comment, useAccount, useFeed, useSubplebbit, useBlock } from '@plebbit/plebbit-react-hooks';
import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso';
import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
import { isAllView, isSubscriptionsView } from '../../lib/utils/view-utils';
import useCatalogFeedRows from '../../hooks/use-catalog-feed-rows';
import useDefaultSubplebbits from '../../hooks/use-default-subplebbits';
import useFeedStateString from '../../hooks/use-feed-state-string';
import useTimeFilter from '../../hooks/use-time-filter';
import useWindowWidth from '../../hooks/use-window-width';
import useCatalogFiltersStore from '../../stores/use-catalog-filters-store';
import useCatalogStyleStore from '../../stores/use-catalog-style-store';
import useFeedResetStore from '../../stores/use-feed-reset-store';
import useInterfaceSettingsStore from '../../stores/use-interface-settings-store';
import useSortingStore from '../../stores/use-sorting-store';
import CatalogRow from '../../components/catalog-row';
import LoadingEllipsis from '../../components/loading-ellipsis';
import SettingsModal from '../../components/settings-modal';
import styles from './catalog.module.css';
import useInterfaceSettingsStore from '../../stores/use-interface-settings-store';
const lastVirtuosoStates: { [key: string]: StateSnapshot } = {};
const threadsWithoutImagesFilter = (comment: Comment) => {
if (!getHasThumbnail(getCommentMediaInfo(comment), comment?.link)) {
return false;
}
return true;
};
const Catalog = () => {
const { t } = useTranslation();
const location = useLocation();
@@ -29,7 +36,7 @@ const Catalog = () => {
const isInAllView = isAllView(location.pathname, useParams());
const defaultSubplebbits = useDefaultSubplebbits();
const { hideAdultBoards, hideGoreBoards } = useInterfaceSettingsStore();
const { filter } = useCatalogFiltersStore();
const { hideThreadsWithoutImages } = useInterfaceSettingsStore();
const account = useAccount();
const subscriptions = account?.subscriptions;
@@ -64,7 +71,7 @@ const Catalog = () => {
const columnCount = Math.floor(useWindowWidth() / columnWidth);
const postsPerPage = columnCount <= 2 ? 10 : columnCount === 3 ? 15 : columnCount === 4 ? 20 : 25;
const { timeFilterSeconds } = useTimeFilter();
const { timeFilterSeconds, timeFilterName } = useTimeFilter();
const { sortType } = useSortingStore();
const feedOptions = useMemo(() => {
@@ -72,7 +79,7 @@ const Catalog = () => {
subplebbitAddresses,
sortType,
postsPerPage: isInAllView || isInSubscriptionsView ? 10 : postsPerPage,
filter,
filter: hideThreadsWithoutImages ? threadsWithoutImagesFilter : undefined,
};
if (isInAllView || isInSubscriptionsView) {
@@ -80,9 +87,39 @@ const Catalog = () => {
}
return options;
}, [subplebbitAddresses, sortType, isInAllView, isInSubscriptionsView, postsPerPage, timeFilterSeconds, filter]);
}, [subplebbitAddresses, sortType, isInAllView, isInSubscriptionsView, postsPerPage, timeFilterSeconds, hideThreadsWithoutImages]);
const { feed, hasMore, loadMore, reset } = useFeed(feedOptions);
const { feed, hasMore, loadMore, reset, subplebbitAddressesWithNewerPosts } = useFeed(feedOptions);
const handleNewerPostsButtonClick = () => {
window.scrollTo({ top: 0, left: 0 });
setTimeout(() => {
reset();
}, 300);
};
// suggest the user to change time filter if there aren't enough posts
const { feed: weeklyFeed } = useFeed({
subplebbitAddresses,
sortType,
newerThan: 60 * 60 * 24 * 7,
filter: hideThreadsWithoutImages ? threadsWithoutImagesFilter : undefined,
});
const { feed: monthlyFeed } = useFeed({
subplebbitAddresses,
sortType,
newerThan: 60 * 60 * 24 * 30,
filter: hideThreadsWithoutImages ? threadsWithoutImagesFilter : undefined,
});
const [showMorePostsSuggestion, setShowMorePostsSuggestion] = useState(false);
useEffect(() => {
const timer = setTimeout(() => {
setShowMorePostsSuggestion(true);
}, 5000);
return () => clearTimeout(timer);
}, []);
const setResetFunction = useFeedResetStore((state) => state.setResetFunction);
useEffect(() => {
@@ -91,18 +128,24 @@ const Catalog = () => {
const subplebbit = useSubplebbit({ subplebbitAddress });
const { error, shortAddress, state, title } = subplebbit || {};
const { blocked, unblock } = useBlock({ address: subplebbitAddress });
const loadingStateString = useFeedStateString(subplebbitAddresses) || t('loading');
const loadingString = (
<div className={styles.stateString}>
{state === 'failed' ? (
state
<span className='red'>{state}</span>
) : isInSubscriptionsView && subscriptions?.length === 0 ? (
t('not_subscribed_to_any_board')
) : blocked ? (
'you have blocked this board'
) : !hasMore && feed.length === 0 ? (
t('no_posts')
) : (
<LoadingEllipsis string={loadingStateString} />
hasMore && <LoadingEllipsis string={loadingStateString} />
)}
{error && (
<div style={{ color: 'red' }}>
<div className='red'>
<br />
{error.message}
</div>
@@ -110,7 +153,8 @@ const Catalog = () => {
</div>
);
const { blocked, unblock } = useBlock({ address: subplebbitAddress });
const params = useParams();
const currentTimeFilterName = params?.timeFilterName || timeFilterName;
const Footer = () => {
let footerContent;
@@ -121,29 +165,51 @@ const Catalog = () => {
footerContent = t('no_posts');
}
}
if (hasMore || subplebbitAddresses.length === 0) {
footerContent = loadingString;
if (hasMore || (subplebbitAddresses && subplebbitAddresses.length === 0)) {
footerContent = (
<>
{subplebbitAddressesWithNewerPosts.length > 0 ? (
<div className={styles.stateString}>
<Trans
i18nKey='newer_posts_available'
components={{
1: <span onClick={handleNewerPostsButtonClick} />,
}}
/>
</div>
) : (
(isInAllView || isInSubscriptionsView) &&
showMorePostsSuggestion &&
monthlyFeed.length > feed.length &&
(weeklyFeed.length > feed.length ? (
<div className={styles.stateString}>
<Trans
i18nKey='more_posts_last_week'
values={{ currentTimeFilterName }}
components={{
1: <Link to={(isInAllView ? '/p/all/catalog' : isInSubscriptionsView ? '/p/subscriptions/catalog' : `/p/${subplebbitAddress}/catalog`) + '/1w'} />,
}}
/>
</div>
) : (
<div className={styles.stateString}>
<Trans
i18nKey='more_posts_last_month'
values={{ currentTimeFilterName }}
components={{
1: <Link to={(isInAllView ? '/p/all/catalog' : isInSubscriptionsView ? '/p/subscriptions/catalog' : `/p/${subplebbitAddress}/catalog`) + '/1m'} />,
}}
/>
</div>
))
)}
<div className={styles.stateString}>
<LoadingEllipsis string={loadingStateString} />
</div>
</>
);
}
return (
<div className={styles.footer}>
{footerContent}
{blocked && (
<>
&nbsp;&nbsp;[
<span
className={styles.button}
onClick={() => {
unblock();
reset();
}}
>
Unblock
</span>
]
</>
)}
</div>
);
return <div className={styles.footer}>{footerContent}</div>;
};
const isFeedLoaded = feed.length > 0 || state === 'failed';
@@ -177,18 +243,41 @@ const Catalog = () => {
{location.pathname.endsWith('/settings') && <SettingsModal />}
<hr />
<div className={styles.catalog}>
<Virtuoso
increaseViewportBy={{ bottom: 1200, top: 1200 }}
totalCount={rows?.length || 0}
data={rows}
itemContent={(index, row) => <CatalogRow index={index} row={row} />}
useWindowScroll={true}
components={{ Footer }}
endReached={loadMore}
ref={virtuosoRef}
restoreStateFrom={lastVirtuosoState}
initialScrollTop={lastVirtuosoState?.scrollTop}
/>
{feed.length !== 0 ? (
<>
<Virtuoso
increaseViewportBy={{ bottom: 1200, top: 1200 }}
totalCount={rows?.length || 0}
data={rows}
itemContent={(index, row) => <CatalogRow index={index} row={row} />}
useWindowScroll={true}
components={{ Footer }}
endReached={loadMore}
ref={virtuosoRef}
restoreStateFrom={lastVirtuosoState}
initialScrollTop={lastVirtuosoState?.scrollTop}
/>
</>
) : (
<div className={styles.footer}>
{loadingString}
{blocked && (
<>
&nbsp;&nbsp;[
<span
className={styles.button}
onClick={() => {
unblock();
reset();
}}
>
Unblock
</span>
]
</>
)}
</div>
)}
</div>
</div>
);
@@ -71,7 +71,7 @@ const PopularThreadsBox = ({ multisub, subplebbits }: { multisub: Subplebbit[];
};
const filteredSubplebbits = useMemo(getFilteredSubplebbits, [subplebbits, showWorksafeContentOnly, showNsfwContentOnly, multisub]);
const popularPosts = usePopularPosts(filteredSubplebbits);
const { popularPosts } = usePopularPosts(filteredSubplebbits);
return (
<div className={styles.box}>
@@ -79,8 +79,8 @@ const PopularThreadsBox = ({ multisub, subplebbits }: { multisub: Subplebbit[];
<h2 className='capitalize'>{t('popular_threads')}</h2>
<BoxModal isBoardsBoxModal={false} />
</div>
<div className={`${styles.boxContent} ${popularPosts.length === 8 ? styles.popularThreads : ''}`}>
{popularPosts.length < 8 ? (
<div className={`${styles.boxContent} ${popularPosts.length !== 0 ? styles.popularThreads : ''}`}>
{popularPosts.length === 0 ? (
<LoadingEllipsis string={t('loading')} />
) : (
popularPosts.map((post: any) => (