mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
@@ -204,7 +204,7 @@
|
||||
"by": "بواسطة",
|
||||
"last_reply_by": "آخر رد بواسطة",
|
||||
"showing_all_replies": "عرض جميع الردود.",
|
||||
"highlight_posts": "تسليط الضوء على المشاركات من خلال هذا العنوان للمستخدم",
|
||||
"highlight_posts": "تمييز المشاركات بواسطة هذا المعرف",
|
||||
"display_name_too_long": "اسم العرض طويل جداً",
|
||||
"title_too_long": "العنوان طويل جداً",
|
||||
"1_post_by_this_id": "مشاركة واحدة بهذا المعرف",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "إخفاء لوحات الجور",
|
||||
"hide_adult_boards": "إخفاء لوحات الكبار",
|
||||
"static_gif": "GIF ثابت",
|
||||
"animated_gif": "GIF متحرك"
|
||||
"animated_gif": "GIF متحرك",
|
||||
"hide_avatars": "إخفاء الصور الرمزية",
|
||||
"anon_mode": "وضع مجهول",
|
||||
"anon_mode_description": "يتم استخدام معرف مستخدم مختلف تلقائيًا في كل موضوع",
|
||||
"pending": "قيد الانتظار",
|
||||
"failed": "فشل"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "দ্বারা",
|
||||
"last_reply_by": "শেষ উত্তর দেননি",
|
||||
"showing_all_replies": "সব উত্তর দেখাচ্ছি।",
|
||||
"highlight_posts": "এই ব্যবহারকারী ঠিকানা দ্বারা পোস্টগুলি হাইলাইট করুন",
|
||||
"highlight_posts": "এই আইডি দ্বারা পোস্ট হাইলাইট করুন",
|
||||
"display_name_too_long": "প্রদর্শনী নাম অত্যন্ত দীর্ঘ",
|
||||
"title_too_long": "শিরোনাম অত্যন্ত দীর্ঘ",
|
||||
"1_post_by_this_id": "এই আইডি দ্বারা 1 পোস্ট",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "গর বোর্ডস লুকান",
|
||||
"hide_adult_boards": "পুরুষ বোর্ডস লুকান",
|
||||
"static_gif": "স্ট্যাটিক GIF",
|
||||
"animated_gif": "অ্যানিমেটেড GIF"
|
||||
"animated_gif": "অ্যানিমেটেড GIF",
|
||||
"hide_avatars": "অ্যাভাটার লুকান",
|
||||
"anon_mode": "অজ্ঞাত মোড",
|
||||
"anon_mode_description": "প্রতিটি থ্রেডে স্বয়ংক্রিয়ভাবে একটি ভিন্ন ব্যবহারকারীর আইডি ব্যবহার করুন",
|
||||
"pending": "অপেক্ষমাণ",
|
||||
"failed": "ব্যর্থ"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "od",
|
||||
"last_reply_by": "Poslední odpověď od",
|
||||
"showing_all_replies": "Zobrazuji všechny odpovědi.",
|
||||
"highlight_posts": "Zvýraznit příspěvky z této uživatelské adresy",
|
||||
"highlight_posts": "Zdůraznit příspěvky podle tohoto ID",
|
||||
"display_name_too_long": "Zobrazované jméno je příliš dlouhé",
|
||||
"title_too_long": "Název je příliš dlouhý",
|
||||
"1_post_by_this_id": "1 příspěvek s tímto ID",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "Skrýt gore desky",
|
||||
"hide_adult_boards": "Skrýt dospělé desky",
|
||||
"static_gif": "Statický GIF",
|
||||
"animated_gif": "Animovaný GIF"
|
||||
"animated_gif": "Animovaný GIF",
|
||||
"hide_avatars": "Skrýt avatary",
|
||||
"anon_mode": "Anonymní režim",
|
||||
"anon_mode_description": "Automaticky používejte jiný uživatelský ID v každém vlákně",
|
||||
"pending": "Čeká na schválení",
|
||||
"failed": "Nepodařilo se"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "af",
|
||||
"last_reply_by": "Seneste svar fra",
|
||||
"showing_all_replies": "Viser alle svar.",
|
||||
"highlight_posts": "Fremhæv indlæg fra denne brugeradresse",
|
||||
"highlight_posts": "Fremhæv indlæg fra denne ID",
|
||||
"display_name_too_long": "Visningsnavn for langt",
|
||||
"title_too_long": "Titel for lang",
|
||||
"1_post_by_this_id": "1 post med dette ID",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "Skjul Gore Boards",
|
||||
"hide_adult_boards": "Skjul Voksne Boards",
|
||||
"static_gif": "Statisk GIF",
|
||||
"animated_gif": "Animeret GIF"
|
||||
"animated_gif": "Animeret GIF",
|
||||
"hide_avatars": "Skjul avatarer",
|
||||
"anon_mode": "Anonim tilstand",
|
||||
"anon_mode_description": "Brug automatisk en anden bruger-ID i hver tråd",
|
||||
"pending": "Afventer",
|
||||
"failed": "Mislykkedes"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "von",
|
||||
"last_reply_by": "Letzte Antwort von",
|
||||
"showing_all_replies": "Zeige alle Antworten.",
|
||||
"highlight_posts": "Beiträge von dieser Benutzeradresse hervorheben",
|
||||
"highlight_posts": "Beiträge nach dieser ID hervorheben",
|
||||
"display_name_too_long": "Angezeigter Name zu lang",
|
||||
"title_too_long": "Titel zu lang",
|
||||
"1_post_by_this_id": "1 Beitrag mit dieser ID",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "Gore-Boards ausblenden",
|
||||
"hide_adult_boards": "Erwachsenen-Boards ausblenden",
|
||||
"static_gif": "Statisches GIF",
|
||||
"animated_gif": "Animiertes GIF"
|
||||
"animated_gif": "Animiertes GIF",
|
||||
"hide_avatars": "Avatare ausblenden",
|
||||
"anon_mode": "Anonymer Modus",
|
||||
"anon_mode_description": "Verwenden Sie automatisch eine andere Benutzer-ID in jedem Thread",
|
||||
"pending": "Ausstehend",
|
||||
"failed": "Fehlgeschlagen"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "από",
|
||||
"last_reply_by": "Τελευταία απάντηση από",
|
||||
"showing_all_replies": "Εμφανίζονται όλες οι απαντήσεις.",
|
||||
"highlight_posts": "Επισήμανση δημοσιεύσεων από αυτήν τη διεύθυνση χρήστη",
|
||||
"highlight_posts": "Επισήμανση αναρτήσεων με αυτήν την ταυτότητα",
|
||||
"display_name_too_long": "Το όνομα εμφάνισης είναι πολύ μεγάλο",
|
||||
"title_too_long": "Το τίτλος είναι πολύ μεγάλος",
|
||||
"1_post_by_this_id": "1 ανάρτηση με αυτό το ID",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "Απόκρυψη των Gore Boards",
|
||||
"hide_adult_boards": "Απόκρυψη των Adult Boards",
|
||||
"static_gif": "Στατικό GIF",
|
||||
"animated_gif": "Κινούμενο GIF"
|
||||
"animated_gif": "Κινούμενο GIF",
|
||||
"hide_avatars": "Απόκρυψη avatars",
|
||||
"anon_mode": "Λειτουργία ανωνυμίας",
|
||||
"anon_mode_description": "Χρησιμοποιήστε αυτόματα διαφορετικό αναγνωριστικό χρήστη σε κάθε νήμα",
|
||||
"pending": "Σε εκκρεμότητα",
|
||||
"failed": "Απέτυχε"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "by",
|
||||
"last_reply_by": "Last reply by",
|
||||
"showing_all_replies": "Showing all replies.",
|
||||
"highlight_posts": "Highlight posts by this user address",
|
||||
"highlight_posts": "Highlight posts by this ID",
|
||||
"display_name_too_long": "display name too long",
|
||||
"title_too_long": "title too long",
|
||||
"1_post_by_this_id": "1 post by this ID",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "Hide Gore Boards",
|
||||
"hide_adult_boards": "Hide Adult Boards",
|
||||
"static_gif": "Static GIF",
|
||||
"animated_gif": "Animated GIF"
|
||||
"animated_gif": "Animated GIF",
|
||||
"hide_avatars": "Hide avatars",
|
||||
"anon_mode": "Anon mode",
|
||||
"anon_mode_description": "Automatically use a different user ID in each thread",
|
||||
"pending": "Pending",
|
||||
"failed": "Failed"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "por",
|
||||
"last_reply_by": "Última respuesta de",
|
||||
"showing_all_replies": "Mostrando todas las respuestas.",
|
||||
"highlight_posts": "Resaltar publicaciones de esta dirección de usuario",
|
||||
"highlight_posts": "Resaltar publicaciones por este ID",
|
||||
"display_name_too_long": "nombre de visualización demasiado largo",
|
||||
"title_too_long": "título demasiado largo",
|
||||
"1_post_by_this_id": "1 publicación con este ID",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "Ocultar tableros de gore",
|
||||
"hide_adult_boards": "Ocultar tableros para adultos",
|
||||
"static_gif": "GIF Estático",
|
||||
"animated_gif": "GIF Animado"
|
||||
"animated_gif": "GIF Animado",
|
||||
"hide_avatars": "Ocultar avatares",
|
||||
"anon_mode": "Modo anónimo",
|
||||
"anon_mode_description": "Utiliza automáticamente un ID de usuario diferente en cada hilo",
|
||||
"pending": "Pendiente",
|
||||
"failed": "Fallido"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "توسط",
|
||||
"last_reply_by": "آخرین پاسخ توسط",
|
||||
"showing_all_replies": "نمایش تمام پاسخها.",
|
||||
"highlight_posts": "برجسته سازی پست ها از این آدرس کاربر",
|
||||
"highlight_posts": "برجسته کردن پستها با این شناسه",
|
||||
"display_name_too_long": "نام نمایشی خیلی طولانی است",
|
||||
"title_too_long": "عنوان خیلی طولانی است",
|
||||
"1_post_by_this_id": "1 پست با این شناسه",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "پنهان کردن تخته های گور",
|
||||
"hide_adult_boards": "پنهان کردن تخته های بزرگسالان",
|
||||
"static_gif": "GIF ثابت",
|
||||
"animated_gif": "GIF متحرک"
|
||||
"animated_gif": "GIF متحرک",
|
||||
"hide_avatars": "پنهان کردن آواتارها",
|
||||
"anon_mode": "حالت ناشناس",
|
||||
"anon_mode_description": "به طور خودکار از یک شناسه کاربری متفاوت در هر موضوع استفاده کنید",
|
||||
"pending": "در انتظار",
|
||||
"failed": "ناموفق"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "kirjoittanut",
|
||||
"last_reply_by": "Viimeisin vastaus",
|
||||
"showing_all_replies": "Näytetään kaikki vastaukset.",
|
||||
"highlight_posts": "Korosta tämän käyttäjän osoitteen viestit",
|
||||
"highlight_posts": "Korosta postauksia tällä ID:llä",
|
||||
"display_name_too_long": "Näyttönimi liian pitkä",
|
||||
"title_too_long": "Otsikko on liian pitkä",
|
||||
"1_post_by_this_id": "1 viesti tällä tunnuksella",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "Piilota Gore-laudat",
|
||||
"hide_adult_boards": "Piilota Aikuisten lautat",
|
||||
"static_gif": "Staattinen GIF",
|
||||
"animated_gif": "Animoitu GIF"
|
||||
"animated_gif": "Animoitu GIF",
|
||||
"hide_avatars": "Piilota avatarit",
|
||||
"anon_mode": "Anonyymi tila",
|
||||
"anon_mode_description": "Käytä automaattisesti eri käyttäjä-ID:tä jokaisessa keskustelussa",
|
||||
"pending": "Odottaa",
|
||||
"failed": "Epäonnistui"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "ni",
|
||||
"last_reply_by": "Huling sagot ni",
|
||||
"showing_all_replies": "Nagpapakita ng lahat ng mga sagot.",
|
||||
"highlight_posts": "I-highlight ang mga post mula sa user address na ito",
|
||||
"highlight_posts": "I-highlight ang mga post gamit ang ID na ito",
|
||||
"display_name_too_long": "ang display name ay masyadong mahaba",
|
||||
"title_too_long": "ang pamagat ay masyadong mahaba",
|
||||
"1_post_by_this_id": "1 post sa pamamagitan ng ID na ito",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "Itago ang mga Gore Boards",
|
||||
"hide_adult_boards": "Itago ang mga Adult Boards",
|
||||
"static_gif": "Static GIF",
|
||||
"animated_gif": "Animated GIF"
|
||||
"animated_gif": "Animated GIF",
|
||||
"hide_avatars": "Itago ang mga avatar",
|
||||
"anon_mode": "Anong mode",
|
||||
"anon_mode_description": "Awtomatikong gamitin ang ibang user ID sa bawat thread",
|
||||
"pending": "Naghihintay",
|
||||
"failed": "Nabigo"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "par",
|
||||
"last_reply_by": "Dernière réponse par",
|
||||
"showing_all_replies": "Affichage de toutes les réponses.",
|
||||
"highlight_posts": "Mettre en évidence les messages de cette adresse utilisateur",
|
||||
"highlight_posts": "Mettre en évidence les publications par cet ID",
|
||||
"display_name_too_long": "nom d'affichage trop long",
|
||||
"title_too_long": "titre trop long",
|
||||
"1_post_by_this_id": "1 publication avec cet identifiant",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "Masquer les tableaux Gore",
|
||||
"hide_adult_boards": "Masquer les tableaux pour adultes",
|
||||
"static_gif": "GIF Statique",
|
||||
"animated_gif": "GIF Animé"
|
||||
"animated_gif": "GIF Animé",
|
||||
"hide_avatars": "Masquer les avatars",
|
||||
"anon_mode": "Mode anonyme",
|
||||
"anon_mode_description": "Utilisez automatiquement un identifiant utilisateur différent dans chaque fil",
|
||||
"pending": "En attente",
|
||||
"failed": "Échoué"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "על ידי",
|
||||
"last_reply_by": "תגובה אחרונה מאת",
|
||||
"showing_all_replies": "מציג את כל התגובות.",
|
||||
"highlight_posts": "הדגשת הודעות מכתובת המשתמש הזו",
|
||||
"highlight_posts": "הדגש פוסטים לפי מזהה זה",
|
||||
"display_name_too_long": "שם התצוגה ארוך מדי",
|
||||
"title_too_long": "הכותרת ארוכה מדי",
|
||||
"1_post_by_this_id": "פוסט אחד עם המזהה הזה",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "הסתר לוחות Gore",
|
||||
"hide_adult_boards": "הסתר לוחות המבוגרים",
|
||||
"static_gif": "GIF סטטי",
|
||||
"animated_gif": "GIF מונפש"
|
||||
"animated_gif": "GIF מונפש",
|
||||
"hide_avatars": "הסתר את התמונות האישיות",
|
||||
"anon_mode": "מצב אנונימי",
|
||||
"anon_mode_description": "השתמש אוטומטית במזהה משתמש שונה בכל שרשור",
|
||||
"pending": "ממתין",
|
||||
"failed": "נכשל"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "द्वारा",
|
||||
"last_reply_by": "अंतिम उत्तर दिया गया है",
|
||||
"showing_all_replies": "सभी उत्तर दिखा रहा है।",
|
||||
"highlight_posts": "इस उपयोगकर्ता पते से पोस्ट्स को हाइलाइट करें",
|
||||
"highlight_posts": "इस ID द्वारा पोस्ट को हाइलाइट करें",
|
||||
"display_name_too_long": "प्रदर्शन नाम बहुत लंबा है",
|
||||
"title_too_long": "शीर्षक बहुत लंबा है",
|
||||
"1_post_by_this_id": "इस आईडी द्वारा 1 पोस्ट",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "गोर बोर्ड्स छिपाएं",
|
||||
"hide_adult_boards": "वयस्क बोर्ड्स छिपाएं",
|
||||
"static_gif": "स्थिर GIF",
|
||||
"animated_gif": "एनिमेटेड GIF"
|
||||
"animated_gif": "एनिमेटेड GIF",
|
||||
"hide_avatars": "अवतार छिपाएँ",
|
||||
"anon_mode": "अनाम मोड",
|
||||
"anon_mode_description": "प्रत्येक थ्रेड में स्वचालित रूप से एक अलग उपयोगकर्ता आईडी का उपयोग करें",
|
||||
"pending": "लंबित",
|
||||
"failed": "असफल"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "által",
|
||||
"last_reply_by": "Utolsó válasz írta",
|
||||
"showing_all_replies": "Az összes válasz megjelenítése.",
|
||||
"highlight_posts": "Kiemelt bejegyzések ebből a felhasználói címből",
|
||||
"highlight_posts": "Kiemelés ezen ID által",
|
||||
"display_name_too_long": "a megjelenített név túl hosszú",
|
||||
"title_too_long": "a cím túl hosszú",
|
||||
"1_post_by_this_id": "1 bejegyzés ezzel az azonosítóval",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "Gore táblák elrejtése",
|
||||
"hide_adult_boards": "Felnőtt táblák elrejtése",
|
||||
"static_gif": "Statikus GIF",
|
||||
"animated_gif": "Animált GIF"
|
||||
"animated_gif": "Animált GIF",
|
||||
"hide_avatars": "Avatarok elrejtése",
|
||||
"anon_mode": "Anonim mód",
|
||||
"anon_mode_description": "Automatikusan használjon más felhasználói azonosítót minden szálban",
|
||||
"pending": "Függőben",
|
||||
"failed": "Sikertelen"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "oleh",
|
||||
"last_reply_by": "Balasan terakhir oleh",
|
||||
"showing_all_replies": "Menampilkan semua balasan.",
|
||||
"highlight_posts": "Sorot posting dari alamat pengguna ini",
|
||||
"highlight_posts": "Sorot pos dengan ID ini",
|
||||
"display_name_too_long": "nama tampilan terlalu panjang",
|
||||
"title_too_long": "judul terlalu panjang",
|
||||
"1_post_by_this_id": "1 pos dengan ID ini",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "Sembunyikan Papan Gore",
|
||||
"hide_adult_boards": "Sembunyikan Papan Dewasa",
|
||||
"static_gif": "GIF Statis",
|
||||
"animated_gif": "GIF Animasi"
|
||||
"animated_gif": "GIF Animasi",
|
||||
"hide_avatars": "Sembunyikan avatar",
|
||||
"anon_mode": "Mode anonim",
|
||||
"anon_mode_description": "Secara otomatis gunakan ID pengguna yang berbeda di setiap utas",
|
||||
"pending": "Tertunda",
|
||||
"failed": "Gagal"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "da",
|
||||
"last_reply_by": "Ultima risposta da",
|
||||
"showing_all_replies": "Mostrando tutte le risposte.",
|
||||
"highlight_posts": "Evidenziare i post da questo indirizzo di utente",
|
||||
"highlight_posts": "Evidenzia i post con questo ID",
|
||||
"display_name_too_long": "nome visualizzato troppo lungo",
|
||||
"title_too_long": "titolo troppo lungo",
|
||||
"1_post_by_this_id": "1 post con questo ID",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "Nascondi tavole Gore",
|
||||
"hide_adult_boards": "Nascondi tavole per adulti",
|
||||
"static_gif": "GIF Statica",
|
||||
"animated_gif": "GIF Animata"
|
||||
"animated_gif": "GIF Animata",
|
||||
"hide_avatars": "Nascondi avatar",
|
||||
"anon_mode": "Modalità anonima",
|
||||
"anon_mode_description": "Usa automaticamente un ID utente diverso in ogni thread",
|
||||
"pending": "In attesa",
|
||||
"failed": "Fallito"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "によって",
|
||||
"last_reply_by": "最後の返信者",
|
||||
"showing_all_replies": "すべての返信を表示しています。",
|
||||
"highlight_posts": "このユーザーアドレスによる投稿を強調表示する",
|
||||
"highlight_posts": "このIDによる投稿を強調表示",
|
||||
"display_name_too_long": "表示名が長すぎます",
|
||||
"title_too_long": "タイトルが長すぎます",
|
||||
"1_post_by_this_id": "このIDで1件の投稿",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "ゴアボードを非表示にする",
|
||||
"hide_adult_boards": "アダルトボードを非表示にする",
|
||||
"static_gif": "静的GIF",
|
||||
"animated_gif": "アニメーションGIF"
|
||||
"animated_gif": "アニメーションGIF",
|
||||
"hide_avatars": "アバターを隠す",
|
||||
"anon_mode": "匿名モード",
|
||||
"anon_mode_description": "各スレッドで自動的に異なるユーザーIDを使用します",
|
||||
"pending": "保留中",
|
||||
"failed": "失敗"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "작성자:",
|
||||
"last_reply_by": "마지막 답글 작성자:",
|
||||
"showing_all_replies": "모든 답글을 표시합니다.",
|
||||
"highlight_posts": "이 사용자 주소의 게시물 강조 표시",
|
||||
"highlight_posts": "이 ID로 게시물 강조 표시",
|
||||
"display_name_too_long": "표시 이름이 너무 깁니다",
|
||||
"title_too_long": "제목이 너무 깁니다",
|
||||
"1_post_by_this_id": "이 ID로 게시된 1개의 글",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "고어 게시판 숨기기",
|
||||
"hide_adult_boards": "성인 게시판 숨기기",
|
||||
"static_gif": "정적 GIF",
|
||||
"animated_gif": "애니메이션 GIF"
|
||||
"animated_gif": "애니메이션 GIF",
|
||||
"hide_avatars": "아바타 숨기기",
|
||||
"anon_mode": "익명 모드",
|
||||
"anon_mode_description": "각 스레드에서 자동으로 다른 사용자 ID를 사용합니다",
|
||||
"pending": "대기 중",
|
||||
"failed": "실패"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "व्यक्ती",
|
||||
"last_reply_by": "शेवटचा प्रतिसाद व्यक्ती",
|
||||
"showing_all_replies": "सर्व प्रतिसाद दाखवत आहे.",
|
||||
"highlight_posts": "या वापरकर्त्याच्या पत्त्यापासून पोस्ट्स हायलाइट करा",
|
||||
"highlight_posts": "या ID द्वारे पोस्ट हायलाइट करा",
|
||||
"display_name_too_long": "डिस्प्ले नाम खूप लांब आहे",
|
||||
"title_too_long": "शीर्षक खूप लांब आहे",
|
||||
"1_post_by_this_id": "या ID वर 1 पोस्ट",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "गोर बोर्ड्स लपवा",
|
||||
"hide_adult_boards": "वयस्क बोर्ड्स लपवा",
|
||||
"static_gif": "स्थिर GIF",
|
||||
"animated_gif": "एनिमेटेड GIF"
|
||||
"animated_gif": "एनिमेटेड GIF",
|
||||
"hide_avatars": "अवतार लपवा",
|
||||
"anon_mode": "अनाम मोड",
|
||||
"anon_mode_description": "प्रत्येक थ्रेडमध्ये स्वयंचलितपणे वेगळा वापरकर्ता ID वापरा",
|
||||
"pending": "प्रलंबित",
|
||||
"failed": "असफल"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "door",
|
||||
"last_reply_by": "Laatste reactie door",
|
||||
"showing_all_replies": "Alle antwoorden weergeven.",
|
||||
"highlight_posts": "Berichten van dit gebruikersadres markeren",
|
||||
"highlight_posts": "Markeer berichten op basis van deze ID",
|
||||
"display_name_too_long": "weergavenaam te lang",
|
||||
"title_too_long": "titel te lang",
|
||||
"1_post_by_this_id": "1 bericht met dit ID",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "Gore Boards verbergen",
|
||||
"hide_adult_boards": "Volwassenen Boards verbergen",
|
||||
"static_gif": "Statische GIF",
|
||||
"animated_gif": "Geanimeerde GIF"
|
||||
"animated_gif": "Geanimeerde GIF",
|
||||
"hide_avatars": "Avatars verbergen",
|
||||
"anon_mode": "Anoniem modus",
|
||||
"anon_mode_description": "Gebruik automatisch een ander gebruikers-ID in elk thread",
|
||||
"pending": "In afwachting",
|
||||
"failed": "Mislukt"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "av",
|
||||
"last_reply_by": "Siste svar fra",
|
||||
"showing_all_replies": "Viser alle svar.",
|
||||
"highlight_posts": "Fremhev innlegg fra denne brukeradressen",
|
||||
"highlight_posts": "Uthev innlegg med denne ID-en",
|
||||
"display_name_too_long": "visningsnavn for langt",
|
||||
"title_too_long": "tittel for lang",
|
||||
"1_post_by_this_id": "1 post med denne ID-en",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "Skjul Gore Boards",
|
||||
"hide_adult_boards": "Skjul voksne Boards",
|
||||
"static_gif": "Statisk GIF",
|
||||
"animated_gif": "Animasjons GIF"
|
||||
"animated_gif": "Animasjons GIF",
|
||||
"hide_avatars": "Skjul avatarer",
|
||||
"anon_mode": "Anonim modus",
|
||||
"anon_mode_description": "Bruk automatisk en annen bruker-ID i hver tråd",
|
||||
"pending": "Venter",
|
||||
"failed": "Feilet"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "przez",
|
||||
"last_reply_by": "Ostatnia odpowiedź od",
|
||||
"showing_all_replies": "Wyświetlanie wszystkich odpowiedzi.",
|
||||
"highlight_posts": "Wyróżnij posty z tego adresu użytkownika",
|
||||
"highlight_posts": "Wyróżnij posty według tego ID",
|
||||
"display_name_too_long": "nazwa wyświetlana jest zbyt długa",
|
||||
"title_too_long": "tytuł jest zbyt długi",
|
||||
"1_post_by_this_id": "1 post z tym identyfikatorem",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "Ukryj Gore Boards",
|
||||
"hide_adult_boards": "Ukryj Adult Boards",
|
||||
"static_gif": "Statyczny GIF",
|
||||
"animated_gif": "Animowany GIF"
|
||||
"animated_gif": "Animowany GIF",
|
||||
"hide_avatars": "Ukryj awatary",
|
||||
"anon_mode": "Tryb anonimowy",
|
||||
"anon_mode_description": "Automatycznie używaj innego identyfikatora użytkownika w każdym wątku",
|
||||
"pending": "Oczekujące",
|
||||
"failed": "Nieudane"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "por",
|
||||
"last_reply_by": "Última resposta por",
|
||||
"showing_all_replies": "Mostrando todas as respostas.",
|
||||
"highlight_posts": "Destacar postagens deste endereço de usuário",
|
||||
"highlight_posts": "Destaque posts por este ID",
|
||||
"display_name_too_long": "nome de exibição muito longo",
|
||||
"title_too_long": "título muito longo",
|
||||
"1_post_by_this_id": "1 post com este ID",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "Ocultar Quadros Gore",
|
||||
"hide_adult_boards": "Ocultar Quadros para Adultos",
|
||||
"static_gif": "GIF Estático",
|
||||
"animated_gif": "GIF Animado"
|
||||
"animated_gif": "GIF Animado",
|
||||
"hide_avatars": "Ocultar avatares",
|
||||
"anon_mode": "Modo anônimo",
|
||||
"anon_mode_description": "Use automaticamente um ID de usuário diferente em cada thread",
|
||||
"pending": "Pendente",
|
||||
"failed": "Falhou"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "de către",
|
||||
"last_reply_by": "Ultima răspuns de la",
|
||||
"showing_all_replies": "Afișare toate răspunsurile.",
|
||||
"highlight_posts": "Evidențiați mesajele de la această adresă de utilizator",
|
||||
"highlight_posts": "Subliniați postările după acest ID",
|
||||
"display_name_too_long": "numele afișat este prea lung",
|
||||
"title_too_long": "titlul este prea lung",
|
||||
"1_post_by_this_id": "1 post cu acest ID",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "Ascundeți tablourile Gore",
|
||||
"hide_adult_boards": "Ascundeți tablourile pentru adulți",
|
||||
"static_gif": "GIF Static",
|
||||
"animated_gif": "GIF Animat"
|
||||
"animated_gif": "GIF Animat",
|
||||
"hide_avatars": "Ascunde avatarele",
|
||||
"anon_mode": "Mod anonim",
|
||||
"anon_mode_description": "Utilizați automat un ID de utilizator diferit în fiecare fir",
|
||||
"pending": "În așteptare",
|
||||
"failed": "Eșuat"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "от",
|
||||
"last_reply_by": "Последний ответ от",
|
||||
"showing_all_replies": "Показываются все ответы.",
|
||||
"highlight_posts": "Выделить сообщения с этого адреса пользователя",
|
||||
"highlight_posts": "Выделить посты по этому ID",
|
||||
"display_name_too_long": "отображаемое имя слишком длинное",
|
||||
"title_too_long": "заголовок слишком длинный",
|
||||
"1_post_by_this_id": "1 сообщение с этим идентификатором",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "Скрыть доски Gore",
|
||||
"hide_adult_boards": "Скрыть доски для взрослых",
|
||||
"static_gif": "Статичный GIF",
|
||||
"animated_gif": "Анимированный GIF"
|
||||
"animated_gif": "Анимированный GIF",
|
||||
"hide_avatars": "Скрыть аватары",
|
||||
"anon_mode": "Анонимный режим",
|
||||
"anon_mode_description": "Автоматически используйте другой идентификатор пользователя в каждом потоке",
|
||||
"pending": "В ожидании",
|
||||
"failed": "Не удалось"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "nga",
|
||||
"last_reply_by": "Përgjigje e fundit nga",
|
||||
"showing_all_replies": "Duke treguar të gjitha përgjigjet.",
|
||||
"highlight_posts": "Shëno postime nga ky adresa e përdoruesit",
|
||||
"highlight_posts": "Dallo postimet sipas këtij ID",
|
||||
"display_name_too_long": "emri i shfaqur është shumë i gjatë",
|
||||
"title_too_long": "titulli shumë i gjatë",
|
||||
"1_post_by_this_id": "1 postim me këtë ID",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "Fshihet Gore Boards",
|
||||
"hide_adult_boards": "Fshihet Adult Boards",
|
||||
"static_gif": "GIF Statik",
|
||||
"animated_gif": "GIF i Animuar"
|
||||
"animated_gif": "GIF i Animuar",
|
||||
"hide_avatars": "Fsheh avatarët",
|
||||
"anon_mode": "Mënyra anonime",
|
||||
"anon_mode_description": "Përdorni automatikisht një ID përdoruesi të ndryshëm në çdo temë",
|
||||
"pending": "Në pritje",
|
||||
"failed": "Dështuar"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "av",
|
||||
"last_reply_by": "Senaste svar av",
|
||||
"showing_all_replies": "Visar alla svar.",
|
||||
"highlight_posts": "Markera inlägg från denna användaradress",
|
||||
"highlight_posts": "Markera inlägg med detta ID",
|
||||
"display_name_too_long": "visningsnamn för långt",
|
||||
"title_too_long": "titel för långt",
|
||||
"1_post_by_this_id": "1 inlägg med detta ID",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "Dölj Gore Boards",
|
||||
"hide_adult_boards": "Dölj vuxna Boards",
|
||||
"static_gif": "Statisk GIF",
|
||||
"animated_gif": "Animerad GIF"
|
||||
"animated_gif": "Animerad GIF",
|
||||
"hide_avatars": "Dölj avatarer",
|
||||
"anon_mode": "Anonymt läge",
|
||||
"anon_mode_description": "Använd automatiskt ett annat användar-ID i varje tråd",
|
||||
"pending": "Väntande",
|
||||
"failed": "Misslyckades"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "ద్వారా",
|
||||
"last_reply_by": "చివరి సమాధానం ఇచ్చింది",
|
||||
"showing_all_replies": "అన్ని సమాధానాలను చూపుతుంది.",
|
||||
"highlight_posts": "ఈ వాడుకరి చిరునామ నుండి పోస్టులను హైలైట్ చేయండి",
|
||||
"highlight_posts": "ఈ ID ద్వారా పోస్ట్లను హైలైట్ చేయండి",
|
||||
"display_name_too_long": "ప్రదర్శన పేరు చాలా నీటిగా ఉంది",
|
||||
"title_too_long": "శీర్షిక చాలా పొడవుగా ఉంది",
|
||||
"1_post_by_this_id": "ఈ ID ద్వారా 1 పోస్టు",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "గోర్ బోర్డ్స్ దాచు",
|
||||
"hide_adult_boards": "పురుషులు బోర్డ్లు దాచు",
|
||||
"static_gif": "స్టాటిక్ GIF",
|
||||
"animated_gif": "అనిమేటెడ్ GIF"
|
||||
"animated_gif": "అనిమేటెడ్ GIF",
|
||||
"hide_avatars": "అవతార్లను దాచు",
|
||||
"anon_mode": "అనానిమస్ మోడ్",
|
||||
"anon_mode_description": "ప్రతి థ్రెడ్లో ఆటోమేటిక్గా ఒక వేరు వినియోగదారుడి ID ఉపయోగించండి",
|
||||
"pending": "అనుమతించబడాలి",
|
||||
"failed": "విజయవంతం కాలేదు"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "โดย",
|
||||
"last_reply_by": "การตอบคำถามล่าสุดโดย",
|
||||
"showing_all_replies": "กำลังแสดงคำตอบทั้งหมด",
|
||||
"highlight_posts": "เน้นโพสต์โดยที่อยู่ผู้ใช้นี้",
|
||||
"highlight_posts": "เน้นโพสต์โดย ID นี้",
|
||||
"display_name_too_long": "ชื่อที่แสดงนามสกุลยาวเกินไป",
|
||||
"title_too_long": "ชื่อเรื่องยาวเกินไป",
|
||||
"1_post_by_this_id": "1 โพสต์โดย ID นี้",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "ซ่อนกระดาน Gore",
|
||||
"hide_adult_boards": "ซ่อนกระดานสำหรับผู้ใหญ่",
|
||||
"static_gif": "GIF แบบคงที่",
|
||||
"animated_gif": "GIF ที่เคลื่อนไหว"
|
||||
"animated_gif": "GIF ที่เคลื่อนไหว",
|
||||
"hide_avatars": "ซ่อนอวาตาร์",
|
||||
"anon_mode": "โหมดนิรนาม",
|
||||
"anon_mode_description": "ใช้ ID ผู้ใช้ที่แตกต่างกันโดยอัตโนมัติในแต่ละเธรด",
|
||||
"pending": "รอดำเนินการ",
|
||||
"failed": "ล้มเหลว"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "tarafından",
|
||||
"last_reply_by": "Son cevap",
|
||||
"showing_all_replies": "Tüm yanıtlar gösteriliyor.",
|
||||
"highlight_posts": "Bu kullanıcı adresinden gelen gönderileri vurgula",
|
||||
"highlight_posts": "Bu ID ile gönderileri vurgula",
|
||||
"display_name_too_long": "görünen adı çok uzun",
|
||||
"title_too_long": "başlık çok uzun",
|
||||
"1_post_by_this_id": "Bu ID ile 1 gönderi",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "Gore Panolarını Gizle",
|
||||
"hide_adult_boards": "Yetişkin Panolarını Gizle",
|
||||
"static_gif": "Statik GIF",
|
||||
"animated_gif": "Animasyonlu GIF"
|
||||
"animated_gif": "Animasyonlu GIF",
|
||||
"hide_avatars": "Avatarları gizle",
|
||||
"anon_mode": "Gizli mod",
|
||||
"anon_mode_description": "Her iplikte otomatik olarak farklı bir kullanıcı ID'si kullanın",
|
||||
"pending": "Beklemede",
|
||||
"failed": "Başarısız"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "від",
|
||||
"last_reply_by": "Остання відповідь від",
|
||||
"showing_all_replies": "Показуються всі відповіді.",
|
||||
"highlight_posts": "Виділити повідомлення з цієї адреси користувача",
|
||||
"highlight_posts": "Виділити повідомлення за цим ID",
|
||||
"display_name_too_long": "ім'я відображається занадто довге",
|
||||
"title_too_long": "заголовок відображається занадто довгий",
|
||||
"1_post_by_this_id": "1 пост з цим ідентифікатором",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "Сховати дошки Gore",
|
||||
"hide_adult_boards": "Сховати дошки для дорослих",
|
||||
"static_gif": "Статичний GIF",
|
||||
"animated_gif": "Анімований GIF"
|
||||
"animated_gif": "Анімований GIF",
|
||||
"hide_avatars": "Сховати аватари",
|
||||
"anon_mode": "Режим анонімності",
|
||||
"anon_mode_description": "Автоматично використовуйте інший ідентифікатор користувача в кожній гілці",
|
||||
"pending": "Очікує",
|
||||
"failed": "Не вдалося"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "کے ذریعے",
|
||||
"last_reply_by": "آخری جواب دیا گیا",
|
||||
"showing_all_replies": "تمام جوابات دکھا رہا ہے۔",
|
||||
"highlight_posts": "اس صارف ایڈریس کے ذریعے پوسٹس کو ہائی لائٹ کریں",
|
||||
"highlight_posts": "اس ID کے ذریعہ پوسٹس کو نمایاں کریں",
|
||||
"display_name_too_long": "ڈسپلے نام بہت لمبا ہے",
|
||||
"title_too_long": "عنوان بہت لمبا ہے",
|
||||
"1_post_by_this_id": "اس آئی ڈی پر 1 پوسٹ",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "گور بورڈز چھپائیں",
|
||||
"hide_adult_boards": "بالغ بورڈز چھپائیں",
|
||||
"static_gif": "اسٹیٹک GIF",
|
||||
"animated_gif": "اینیمیٹڈ GIF"
|
||||
"animated_gif": "اینیمیٹڈ GIF",
|
||||
"hide_avatars": "اوٹار چھپائیں",
|
||||
"anon_mode": "خفیہ موڈ",
|
||||
"anon_mode_description": "ہر تھریڈ میں خود بخود مختلف صارف آئی ڈی کا استعمال کریں",
|
||||
"pending": "منتظر",
|
||||
"failed": "ناکام"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "bởi",
|
||||
"last_reply_by": "Trả lời cuối cùng bởi",
|
||||
"showing_all_replies": "Đang hiển thị tất cả các phản hồi.",
|
||||
"highlight_posts": "Đánh dấu bài đăng từ địa chỉ người dùng này",
|
||||
"highlight_posts": "Nổi bật các bài viết bằng ID này",
|
||||
"display_name_too_long": "tên hiển thị quá dài",
|
||||
"title_too_long": "tiêu đề quá dài",
|
||||
"1_post_by_this_id": "1 bài đăng bởi ID này",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "Ẩn Gore Boards",
|
||||
"hide_adult_boards": "Ẩn Adult Boards",
|
||||
"static_gif": "GIF Tĩnh",
|
||||
"animated_gif": "GIF Hoạt Hình"
|
||||
"animated_gif": "GIF Hoạt Hình",
|
||||
"hide_avatars": "Ẩn avatar",
|
||||
"anon_mode": "Chế độ ẩn danh",
|
||||
"anon_mode_description": "Tự động sử dụng ID người dùng khác nhau trong mỗi chủ đề",
|
||||
"pending": "Đang chờ",
|
||||
"failed": "Thất bại"
|
||||
}
|
||||
@@ -204,7 +204,7 @@
|
||||
"by": "由...发布",
|
||||
"last_reply_by": "最后回复者",
|
||||
"showing_all_replies": "显示所有回复。",
|
||||
"highlight_posts": "突出显示此用户地址的帖子",
|
||||
"highlight_posts": "高亮显示此 ID 的帖子",
|
||||
"display_name_too_long": "显示名称太长",
|
||||
"title_too_long": "标题太长",
|
||||
"1_post_by_this_id": "此ID的1篇帖子",
|
||||
@@ -221,5 +221,10 @@
|
||||
"hide_gore_boards": "隐藏Gore板块",
|
||||
"hide_adult_boards": "隐藏成人板块",
|
||||
"static_gif": "静态GIF",
|
||||
"animated_gif": "动画GIF"
|
||||
"animated_gif": "动画GIF",
|
||||
"hide_avatars": "隐藏头像",
|
||||
"anon_mode": "匿名模式",
|
||||
"anon_mode_description": "在每个线程中自动使用不同的用户 ID",
|
||||
"pending": "待处理",
|
||||
"failed": "失败"
|
||||
}
|
||||
@@ -62,6 +62,7 @@
|
||||
|
||||
.editTextarea {
|
||||
width: calc(100% - 12px);
|
||||
height: 100px;
|
||||
margin-left: 3px;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import useIsMobile from '../../hooks/use-is-mobile';
|
||||
import CommentMedia from '../comment-media';
|
||||
import styles from './markdown.module.css';
|
||||
import { useLocation, useParams } from 'react-router-dom';
|
||||
import { canEmbed } from '../embed';
|
||||
|
||||
interface ContentLinkEmbedProps {
|
||||
children: any;
|
||||
@@ -86,6 +87,7 @@ const ContentLinkEmbed = ({ children, href, linkMediaInfo }: ContentLinkEmbedPro
|
||||
<CommentMedia isReply={false} setShowThumbnail={setShowMedia} commentMediaInfo={linkMediaInfo} showThumbnail={false} />
|
||||
</>
|
||||
)}
|
||||
{getHasThumbnail(linkMediaInfo, href) && (
|
||||
<FloatingPortal>
|
||||
{isOpen && !isMobile && (
|
||||
<div className={styles.floatingEmbed} ref={refs.setFloating} style={floatingStyles} {...getFloatingProps()}>
|
||||
@@ -93,6 +95,7 @@ const ContentLinkEmbed = ({ children, href, linkMediaInfo }: ContentLinkEmbedPro
|
||||
</div>
|
||||
)}
|
||||
</FloatingPortal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -172,7 +175,8 @@ const Markdown = ({ content, title }: MarkdownProps) => {
|
||||
a: ({ href, children }) => {
|
||||
if (href && !isInCatalogView) {
|
||||
const linkMediaInfo = getLinkMediaInfo(href);
|
||||
if (getHasThumbnail(linkMediaInfo, href)) {
|
||||
const embedUrl = new URL(href);
|
||||
if (canEmbed(embedUrl) || getHasThumbnail(linkMediaInfo, href)) {
|
||||
return <ContentLinkEmbed children={children} href={href} linkMediaInfo={linkMediaInfo} />;
|
||||
} else {
|
||||
return (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { Link, useLocation, useParams } from 'react-router-dom';
|
||||
import { Comment, useAccount, useAuthorAvatar, useComment, useEditedComment } from '@plebbit/plebbit-react-hooks';
|
||||
@@ -9,9 +9,13 @@ import { hashStringToColor, getTextColorForBackground } from '../../lib/utils/po
|
||||
import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-utils';
|
||||
import { isValidURL } from '../../lib/utils/url-utils';
|
||||
import { isAllView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils';
|
||||
import useAnonModeStore from '../../stores/use-anon-mode-store';
|
||||
import useAvatarVisibilityStore from '../../stores/use-avatar-visibility-store';
|
||||
import useAnonMode from '../../hooks/use-anon-mode';
|
||||
import useAuthorAddressClick from '../../hooks/use-author-address-click';
|
||||
import useEditCommentPrivileges from '../../hooks/use-author-privileges';
|
||||
import useCountLinksInReplies from '../../hooks/use-count-links-in-replies';
|
||||
import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame';
|
||||
import useHide from '../../hooks/use-hide';
|
||||
import useReplies from '../../hooks/use-replies';
|
||||
import useStateString from '../../hooks/use-state-string';
|
||||
@@ -26,7 +30,6 @@ import Tooltip from '../tooltip';
|
||||
import { PostProps } from '../../views/post/post';
|
||||
import { create } from 'zustand';
|
||||
import _ from 'lodash';
|
||||
import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame';
|
||||
|
||||
interface ShowOmittedRepliesState {
|
||||
showOmittedReplies: Record<string, boolean>;
|
||||
@@ -46,7 +49,7 @@ const useShowOmittedReplies = create<ShowOmittedRepliesState>((set) => ({
|
||||
|
||||
const PostInfo = ({ openReplyModal, post, postReplyCount = 0, roles, isHidden }: PostProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { author, cid, deleted, locked, pinned, parentCid, postCid, removed, replyCount, shortCid, state, subplebbitAddress, timestamp } = post || {};
|
||||
const { author, cid, deleted, locked, pinned, parentCid, postCid, removed, shortCid, state, subplebbitAddress, timestamp } = post || {};
|
||||
const title = post?.title?.trim();
|
||||
const replies = useReplies(post);
|
||||
const { address, shortAddress } = author || {};
|
||||
@@ -57,6 +60,7 @@ const PostInfo = ({ openReplyModal, post, postReplyCount = 0, roles, isHidden }:
|
||||
const isReply = parentCid;
|
||||
const { showOmittedReplies } = useShowOmittedReplies();
|
||||
const { imageUrl: avatarImageUrl } = useAuthorAvatar({ author });
|
||||
const { hideAvatars } = useAvatarVisibilityStore();
|
||||
|
||||
const params = useParams();
|
||||
const location = useLocation();
|
||||
@@ -64,15 +68,19 @@ const PostInfo = ({ openReplyModal, post, postReplyCount = 0, roles, isHidden }:
|
||||
const isInPostPageView = isPostPageView(location.pathname, params);
|
||||
const isInSubscriptionsView = isSubscriptionsView(location.pathname, params);
|
||||
|
||||
// comment.author.shortAddress is undefined while the comment publishing state is pending, use account instead
|
||||
// in anon mode, use the newly generated signer.address instead, which will be comment.author.address
|
||||
const { anonMode } = useAnonMode();
|
||||
const { currentAnonSignerAddress } = useAnonModeStore();
|
||||
const account = useAccount();
|
||||
const accountShortAddress = account?.author?.shortAddress; // if reply by account is pending, it doesn't have an author yet
|
||||
const pendingShortAddress = anonMode ? currentAnonSignerAddress && Plebbit.getShortAddress(currentAnonSignerAddress) : account?.author?.shortAddress;
|
||||
|
||||
const { isCommentAuthorMod, isAccountMod, isAccountCommentAuthor } = useEditCommentPrivileges({ commentAuthorAddress: address, subplebbitAddress });
|
||||
|
||||
const handleUserAddressClick = useAuthorAddressClick();
|
||||
const numberOfPostsByAuthor = document.querySelectorAll(`[data-author-address="${shortAddress}"][data-post-cid="${postCid}"]`).length;
|
||||
|
||||
const userID = shortAddress || accountShortAddress;
|
||||
const userID = shortAddress || pendingShortAddress;
|
||||
const userIDBackgroundColor = hashStringToColor(userID);
|
||||
const userIDTextColor = getTextColorForBackground(userIDBackgroundColor);
|
||||
|
||||
@@ -110,7 +118,7 @@ const PostInfo = ({ openReplyModal, post, postReplyCount = 0, roles, isHidden }:
|
||||
</span>
|
||||
{!(isDescription || isRules) && (
|
||||
<>
|
||||
{author?.avatar && !(deleted || removed) ? (
|
||||
{author?.avatar && !(deleted || removed) && !hideAvatars ? (
|
||||
<span className={styles.authorAvatar}>
|
||||
<img src={avatarImageUrl} alt='' />
|
||||
</span>
|
||||
@@ -166,7 +174,9 @@ const PostInfo = ({ openReplyModal, post, postReplyCount = 0, roles, isHidden }:
|
||||
) : (
|
||||
<>
|
||||
<span>c/</span>
|
||||
<span className={styles.pendingCid}>{state === 'failed' || stateString === 'Failed' ? 'Failed' : 'Pending'}</span>
|
||||
<span className={styles.pendingCid}>
|
||||
{state === 'failed' || stateString === 'Failed' ? _.capitalize(t('failed')) : state === 'pending' ? _.capitalize(t('pending')) : ''}
|
||||
</span>
|
||||
</>
|
||||
))}
|
||||
{pinned && (
|
||||
@@ -184,7 +194,7 @@ const PostInfo = ({ openReplyModal, post, postReplyCount = 0, roles, isHidden }:
|
||||
[
|
||||
<Link
|
||||
to={isInAllView && isDescription ? '/p/all/description' : `/p/${subplebbitAddress}/${isDescription ? 'description' : isRules ? 'rules' : `c/${postCid}`}`}
|
||||
onClick={(e) => !cid && e.preventDefault()}
|
||||
onClick={(e) => !cid && !isDescription && !isRules && e.preventDefault()}
|
||||
>
|
||||
{_.capitalize(t('reply'))}
|
||||
</Link>
|
||||
@@ -193,10 +203,12 @@ const PostInfo = ({ openReplyModal, post, postReplyCount = 0, roles, isHidden }:
|
||||
)}
|
||||
</span>
|
||||
<PostMenuDesktop post={post} />
|
||||
{replyCount > 0 &&
|
||||
{cid &&
|
||||
parentCid &&
|
||||
replies &&
|
||||
replies.map((reply: Comment, index: number) => reply?.parentCid === cid && <ReplyQuotePreview key={index} isBacklinkReply={true} backlinkReply={reply} />)}
|
||||
replies.map(
|
||||
(reply: Comment, index: number) => reply?.parentCid === cid && reply?.cid && <ReplyQuotePreview key={index} isBacklinkReply={true} backlinkReply={reply} />,
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -290,7 +302,7 @@ const PostMessage = ({ post }: PostProps) => {
|
||||
<span className={styles.redEditMessage}>({t('this_post_was_removed')})</span>
|
||||
<br />
|
||||
<br />
|
||||
<span className={styles.grayEditMessage}>{`${_.capitalize(t('reason'))}: "${reason}"`}.</span>
|
||||
<span className={styles.grayEditMessage}>{`${_.capitalize(t('reason'))}: "${reason}"`}</span>
|
||||
</>
|
||||
) : (
|
||||
<span className={styles.grayEditMessage}>{_.capitalize(t('this_post_was_removed'))}.</span>
|
||||
@@ -299,7 +311,7 @@ const PostMessage = ({ post }: PostProps) => {
|
||||
reason ? (
|
||||
<>
|
||||
<span className={styles.grayEditMessage}>{t('user_deleted_this_post')}</span>{' '}
|
||||
<span className={styles.grayEditMessage}>{`${_.capitalize(t('reason'))}: "${reason}"`}.</span>
|
||||
<span className={styles.grayEditMessage}>{`${_.capitalize(t('reason'))}: "${reason}"`}</span>
|
||||
</>
|
||||
) : (
|
||||
<span className={styles.grayEditMessage}>{t('user_deleted_this_post')}</span>
|
||||
@@ -416,15 +428,6 @@ const PostDesktop = ({ openReplyModal, post, roles, showAllReplies, showReplies
|
||||
const linksCount = pinned ? totalLinksCount : totalLinksCount - visiblelinksCount;
|
||||
const { showOmittedReplies, setShowOmittedReplies } = useShowOmittedReplies();
|
||||
|
||||
// scroll to reply if pathname is reply permalink (backlink)
|
||||
const replyRefs = useRef<(HTMLDivElement | null)[]>([]);
|
||||
useEffect(() => {
|
||||
const replyIndex = replies.findIndex((reply) => location.pathname === `/p/${subplebbitAddress}/c/${reply?.cid}`);
|
||||
if (replyIndex !== -1 && replyRefs.current[replyIndex]) {
|
||||
replyRefs.current[replyIndex]?.scrollIntoView();
|
||||
}
|
||||
}, [location.pathname, replies, subplebbitAddress]);
|
||||
|
||||
const stateString = useStateString(post);
|
||||
|
||||
return (
|
||||
@@ -476,7 +479,7 @@ const PostDesktop = ({ openReplyModal, post, roles, showAllReplies, showReplies
|
||||
replies &&
|
||||
showReplies &&
|
||||
(showAllReplies || showOmittedReplies[cid] ? replies : replies.slice(-5)).map((reply, index) => (
|
||||
<div key={index} className={styles.replyContainer} ref={(el) => (replyRefs.current[index] = el)}>
|
||||
<div key={index} className={styles.replyContainer}>
|
||||
<Reply openReplyModal={openReplyModal} reply={reply} roles={roles} postReplyCount={replyCount} />
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -180,13 +180,13 @@ const PostMenuDesktop = ({ post }: { post: Comment }) => {
|
||||
className={isInCatalogView ? styles.postMenuBtnCatalog : styles.postMenuBtn}
|
||||
title='Post menu'
|
||||
onClick={handleMenuClick}
|
||||
style={{ transform: menuBtnRotated && cid ? 'rotate(90deg)' : 'rotate(0deg)' }}
|
||||
style={{ transform: menuBtnRotated && (cid || isDescription || isRules) ? 'rotate(90deg)' : 'rotate(0deg)' }}
|
||||
>
|
||||
▶
|
||||
</span>
|
||||
</span>
|
||||
{menuBtnRotated &&
|
||||
cid &&
|
||||
(cid || isDescription || isRules) &&
|
||||
createPortal(
|
||||
<FloatingFocusManager context={context} modal={false}>
|
||||
<div className={styles.postMenu} ref={refs.setFloating} style={floatingStyles} aria-labelledby={headingId} {...getFloatingProps()}>
|
||||
|
||||
@@ -164,7 +164,7 @@ const PostMenuMobile = ({ post }: { post: Comment }) => {
|
||||
...
|
||||
</span>
|
||||
{isMenuOpen &&
|
||||
cid &&
|
||||
(cid || isDescription || isRules) &&
|
||||
createPortal(
|
||||
<FloatingFocusManager context={context} modal={false}>
|
||||
<div className={styles.postMenu} ref={refs.setFloating} style={floatingStyles} aria-labelledby={headingId} {...getFloatingProps()}>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { Link, useLocation, useParams } from 'react-router-dom';
|
||||
import { Comment, useAccount, useAuthorAvatar, useComment, useEditedComment } from '@plebbit/plebbit-react-hooks';
|
||||
@@ -8,6 +8,9 @@ import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-util
|
||||
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';
|
||||
import useAnonModeStore from '../../stores/use-anon-mode-store';
|
||||
import useAvatarVisibilityStore from '../../stores/use-avatar-visibility-store';
|
||||
import useAnonMode from '../../hooks/use-anon-mode';
|
||||
import useAuthorAddressClick from '../../hooks/use-author-address-click';
|
||||
import useCountLinksInReplies from '../../hooks/use-count-links-in-replies';
|
||||
import useHide from '../../hooks/use-hide';
|
||||
@@ -31,6 +34,7 @@ const PostInfoAndMedia = ({ openReplyModal, post, postReplyCount = 0, roles }: P
|
||||
const displayName = author?.displayName?.trim();
|
||||
const authorRole = roles?.[address]?.role;
|
||||
const { imageUrl: avatarImageUrl } = useAuthorAvatar({ author });
|
||||
const { hideAvatars } = useAvatarVisibilityStore();
|
||||
|
||||
const params = useParams();
|
||||
const location = useLocation();
|
||||
@@ -44,16 +48,19 @@ const PostInfoAndMedia = ({ openReplyModal, post, postReplyCount = 0, roles }: P
|
||||
|
||||
const isReply = parentCid;
|
||||
|
||||
// pending reply by account is not yet published
|
||||
// comment.author.shortAddress is undefined while the comment publishing state is pending, use account instead
|
||||
// in anon mode, use the newly generated signer.address instead, which will be comment.author.address
|
||||
const { anonMode } = useAnonMode();
|
||||
const { currentAnonSignerAddress } = useAnonModeStore();
|
||||
const account = useAccount();
|
||||
const accountShortAddress = account?.author?.shortAddress;
|
||||
const pendingShortAddress = anonMode ? currentAnonSignerAddress && Plebbit.getShortAddress(currentAnonSignerAddress) : account?.author?.shortAddress;
|
||||
|
||||
const stateString = useStateString(post);
|
||||
|
||||
const handleUserAddressClick = useAuthorAddressClick();
|
||||
const numberOfPostsByAuthor = document.querySelectorAll(`[data-author-address="${shortAddress}"][data-post-cid="${postCid}"]`).length;
|
||||
|
||||
const userIDBackgroundColor = hashStringToColor(shortAddress || accountShortAddress);
|
||||
const userIDBackgroundColor = hashStringToColor(shortAddress || pendingShortAddress);
|
||||
const userIDTextColor = getTextColorForBackground(userIDBackgroundColor);
|
||||
|
||||
return (
|
||||
@@ -82,7 +89,7 @@ const PostInfoAndMedia = ({ openReplyModal, post, postReplyCount = 0, roles }: P
|
||||
</span>
|
||||
{!(isDescription || isRules) && (
|
||||
<>
|
||||
{author?.avatar && !(deleted || removed) ? (
|
||||
{author?.avatar && !(deleted || removed) && !hideAvatars ? (
|
||||
<span className={styles.authorAvatar}>
|
||||
<img src={avatarImageUrl} alt='' />
|
||||
</span>
|
||||
@@ -100,10 +107,10 @@ const PostInfoAndMedia = ({ openReplyModal, post, postReplyCount = 0, roles }: P
|
||||
<span
|
||||
title={t('highlight_posts')}
|
||||
className={styles.userAddress}
|
||||
onClick={() => handleUserAddressClick(shortAddress || accountShortAddress, postCid)}
|
||||
onClick={() => handleUserAddressClick(shortAddress || pendingShortAddress, postCid)}
|
||||
style={{ backgroundColor: userIDBackgroundColor, color: userIDTextColor }}
|
||||
>
|
||||
{shortAddress || accountShortAddress}
|
||||
{shortAddress || pendingShortAddress}
|
||||
</span>
|
||||
}
|
||||
content={`${numberOfPostsByAuthor === 1 ? t('1_post_by_this_id') : t('x_posts_by_this_id', { number: numberOfPostsByAuthor })}`}
|
||||
@@ -154,7 +161,9 @@ const PostInfoAndMedia = ({ openReplyModal, post, postReplyCount = 0, roles }: P
|
||||
) : (
|
||||
<>
|
||||
<span>c/</span>
|
||||
<span className={styles.pendingCid}>{state === 'failed' || stateString === 'Failed' ? 'Failed' : 'Pending'}</span>
|
||||
<span className={styles.pendingCid}>
|
||||
{state === 'failed' || stateString === 'Failed' ? _.capitalize(t('failed')) : state === 'pending' ? _.capitalize(t('pending')) : ''}
|
||||
</span>
|
||||
</>
|
||||
))}
|
||||
</span>
|
||||
@@ -167,15 +176,17 @@ const PostInfoAndMedia = ({ openReplyModal, post, postReplyCount = 0, roles }: P
|
||||
};
|
||||
|
||||
const ReplyBacklinks = ({ post }: PostProps) => {
|
||||
const { cid, parentCid, replyCount } = post || {};
|
||||
const { cid, parentCid } = post || {};
|
||||
const replies = useReplies(post);
|
||||
|
||||
return (
|
||||
replyCount > 0 &&
|
||||
cid &&
|
||||
parentCid &&
|
||||
replies && (
|
||||
replies.length > 0 && (
|
||||
<div className={styles.mobileReplyBacklinks}>
|
||||
{replies.map((reply: Comment, index: number) => reply?.parentCid === cid && <ReplyQuotePreview key={index} isBacklinkReply={true} backlinkReply={reply} />)}
|
||||
{replies.map(
|
||||
(reply: Comment, index: number) => reply?.parentCid === cid && reply?.cid && <ReplyQuotePreview key={index} isBacklinkReply={true} backlinkReply={reply} />,
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
@@ -214,7 +225,7 @@ const PostMessageMobile = ({ post }: PostProps) => {
|
||||
<span className={styles.redEditMessage}>({t('this_post_was_removed')})</span>
|
||||
<br />
|
||||
<br />
|
||||
<span className={styles.grayEditMessage}>{`${_.capitalize(t('reason'))}: "${reason}"`}.</span>
|
||||
<span className={styles.grayEditMessage}>{`${_.capitalize(t('reason'))}: "${reason}"`}</span>
|
||||
</>
|
||||
) : (
|
||||
<span className={styles.grayEditMessage}>{_.capitalize(t('this_post_was_removed'))}.</span>
|
||||
@@ -223,7 +234,7 @@ const PostMessageMobile = ({ post }: PostProps) => {
|
||||
reason ? (
|
||||
<>
|
||||
<span className={styles.grayEditMessage}>{t('user_deleted_this_post')}</span>{' '}
|
||||
<span className={styles.grayEditMessage}>{`${_.capitalize(t('reason'))}: "${reason}"`}.</span>
|
||||
<span className={styles.grayEditMessage}>{`${_.capitalize(t('reason'))}: "${reason}"`}</span>
|
||||
</>
|
||||
) : (
|
||||
<span className={styles.grayEditMessage}>{t('user_deleted_this_post')}</span>
|
||||
@@ -334,15 +345,6 @@ const PostMobile = ({ openReplyModal, post, roles, showAllReplies, showReplies =
|
||||
const isInPostPageView = isPostPageView(location.pathname, params);
|
||||
const { hidden, unhide } = useHide({ cid });
|
||||
|
||||
// scroll to reply if pathname is reply permalink (backlink)
|
||||
const replyRefs = useRef<(HTMLDivElement | null)[]>([]);
|
||||
useEffect(() => {
|
||||
const replyIndex = replies.findIndex((reply) => location.pathname === `/p/${subplebbitAddress}/c/${reply?.cid}`);
|
||||
if (replyIndex !== -1 && replyRefs.current[replyIndex]) {
|
||||
replyRefs.current[replyIndex]?.scrollIntoView();
|
||||
}
|
||||
}, [location.pathname, replies, subplebbitAddress]);
|
||||
|
||||
const stateString = useStateString(post);
|
||||
|
||||
return (
|
||||
@@ -391,7 +393,7 @@ const PostMobile = ({ openReplyModal, post, roles, showAllReplies, showReplies =
|
||||
replies &&
|
||||
showReplies &&
|
||||
(showAllReplies ? replies : replies.slice(-5)).map((reply, index) => (
|
||||
<div key={index} className={styles.replyContainer} ref={(el) => (replyRefs.current[index] = el)}>
|
||||
<div key={index} className={styles.replyContainer}>
|
||||
<Reply openReplyModal={openReplyModal} postReplyCount={replyCount} reply={reply} roles={roles} />
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -15,6 +15,7 @@ import styles from './reply-modal.module.css';
|
||||
import { LinkTypePreviewer } from '../post-form';
|
||||
import _ from 'lodash';
|
||||
import useAnonMode from '../../hooks/use-anon-mode';
|
||||
import useAnonModeStore from '../../stores/use-anon-mode-store';
|
||||
|
||||
interface ReplyModalProps {
|
||||
closeModal: () => void;
|
||||
@@ -40,6 +41,8 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, postCid, scrollY, s
|
||||
const address = comment?.author?.address;
|
||||
|
||||
const hasCalledAnonAddressRef = useRef(false);
|
||||
const { setCurrentAnonSignerAddress } = useAnonModeStore();
|
||||
|
||||
const getAnonAddressForReply = useCallback(async () => {
|
||||
if (anonMode && !hasCalledAnonAddressRef.current) {
|
||||
hasCalledAnonAddressRef.current = true;
|
||||
@@ -52,8 +55,10 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, postCid, scrollY, s
|
||||
displayName: displayName || undefined,
|
||||
},
|
||||
});
|
||||
setCurrentAnonSignerAddress(existingSigner.address);
|
||||
} else {
|
||||
const newSigner = await getNewSigner();
|
||||
if (newSigner) {
|
||||
setPublishReplyOptions({
|
||||
signer: newSigner,
|
||||
author: {
|
||||
@@ -61,9 +66,11 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, postCid, scrollY, s
|
||||
displayName: displayName || undefined,
|
||||
},
|
||||
});
|
||||
setCurrentAnonSignerAddress(newSigner.address);
|
||||
}
|
||||
}
|
||||
}, [address, getExistingSigner, getNewSigner, setPublishReplyOptions, anonMode, displayName]);
|
||||
}
|
||||
}, [address, getExistingSigner, getNewSigner, setPublishReplyOptions, anonMode, displayName, setCurrentAnonSignerAddress]);
|
||||
|
||||
const onPublishReply = () => {
|
||||
const currentContent = textRef.current?.value.slice(contentPrefix.length).trim() || '';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { Comment, useAccount } from '@plebbit/plebbit-react-hooks';
|
||||
import { useFloating, offset, shift, size, autoUpdate, Placement } from '@floating-ui/react';
|
||||
import useIsMobile from '../../hooks/use-is-mobile';
|
||||
@@ -88,6 +88,19 @@ const DesktopQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, i
|
||||
};
|
||||
}, [update]);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleClick = (e: React.MouseEvent, cid: string | undefined, subplebbitAddress: string | undefined) => {
|
||||
e.preventDefault();
|
||||
if (cid && subplebbitAddress) {
|
||||
navigate(`/p/${subplebbitAddress}/c/${cid}`);
|
||||
setTimeout(() => {
|
||||
const element = document.querySelector(`[data-cid="${cid}"]`);
|
||||
element?.scrollIntoView();
|
||||
}, 100);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseOver = (cid: string | undefined) => {
|
||||
if (!cid) return;
|
||||
|
||||
@@ -114,6 +127,7 @@ const DesktopQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, i
|
||||
ref={refs.setReference}
|
||||
onMouseOver={() => handleMouseOver(backlinkReply?.cid)}
|
||||
onMouseLeave={() => handleMouseLeave(backlinkReply?.cid)}
|
||||
onClick={(e) => handleClick(e, backlinkReply?.cid, backlinkReply?.subplebbitAddress)}
|
||||
>
|
||||
c/{backlinkReply?.shortCid}
|
||||
</Link>
|
||||
@@ -138,8 +152,9 @@ const DesktopQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, i
|
||||
className={styles.quoteLink}
|
||||
onMouseOver={() => handleMouseOver(quotelinkReply?.cid)}
|
||||
onMouseLeave={() => handleMouseLeave(quotelinkReply?.cid)}
|
||||
onClick={(e) => handleClick(e, quotelinkReply?.cid, quotelinkReply?.subplebbitAddress)}
|
||||
>
|
||||
{`c/${quotelinkReply?.shortCid}`}
|
||||
{quotelinkReply?.shortCid && `c/${quotelinkReply?.shortCid}`}
|
||||
{quotelinkReply?.author?.address === account?.author?.address && ' (You)'}
|
||||
</Link>
|
||||
<br />
|
||||
@@ -174,6 +189,19 @@ const MobileQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, is
|
||||
};
|
||||
}, [update]);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleClick = (e: React.MouseEvent, cid: string | undefined, subplebbitAddress: string | undefined) => {
|
||||
e.preventDefault();
|
||||
if (cid && subplebbitAddress) {
|
||||
navigate(`/p/${subplebbitAddress}/c/${cid}`);
|
||||
setTimeout(() => {
|
||||
const element = document.querySelector(`[data-cid="${cid}"]`);
|
||||
element?.scrollIntoView();
|
||||
}, 100);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseOver = (cid: string | undefined) => {
|
||||
if (!cid) return;
|
||||
|
||||
@@ -200,11 +228,18 @@ const MobileQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, is
|
||||
onMouseOver={() => handleMouseOver(backlinkReply?.cid)}
|
||||
onMouseLeave={() => handleMouseLeave(backlinkReply?.cid)}
|
||||
>
|
||||
c/{backlinkReply?.shortCid}{' '}
|
||||
{backlinkReply?.shortCid && `c/${backlinkReply?.shortCid}`}
|
||||
</span>
|
||||
<Link to={`/p/${backlinkReply?.subplebbitAddress}/c/${backlinkReply?.cid}`} className={styles.backlinkHash}>
|
||||
{backlinkReply?.shortCid && (
|
||||
<Link
|
||||
to={`/p/${backlinkReply?.subplebbitAddress}/c/${backlinkReply?.cid}`}
|
||||
className={styles.backlinkHash}
|
||||
onClick={(e) => handleClick(e, backlinkReply?.cid, backlinkReply?.subplebbitAddress)}
|
||||
>
|
||||
{' '}
|
||||
#
|
||||
</Link>
|
||||
)}
|
||||
{hoveredCid === backlinkReply?.cid &&
|
||||
outOfViewCid === backlinkReply?.cid &&
|
||||
createPortal(
|
||||
@@ -226,14 +261,19 @@ const MobileQuotePreview = ({ backlinkReply, quotelinkReply, isBacklinkReply, is
|
||||
onMouseOver={() => handleMouseOver(quotelinkReply?.cid)}
|
||||
onMouseLeave={() => handleMouseLeave(quotelinkReply?.cid)}
|
||||
>
|
||||
c/{quotelinkReply?.shortCid}
|
||||
{quotelinkReply?.shortCid && `c/${quotelinkReply?.shortCid}`}
|
||||
{quotelinkReply?.author?.address === account?.author?.address && ' (You)'}
|
||||
</span>
|
||||
<Link className={styles.quoteLink} to={`/p/${quotelinkReply?.subplebbitAddress}/c/${quotelinkReply?.cid}`}>
|
||||
{quotelinkReply?.shortCid && (
|
||||
<Link
|
||||
className={styles.quoteLink}
|
||||
to={`/p/${quotelinkReply?.subplebbitAddress}/c/${quotelinkReply?.cid}`}
|
||||
onClick={(e) => handleClick(e, quotelinkReply?.cid, quotelinkReply?.subplebbitAddress)}
|
||||
>
|
||||
{' '}
|
||||
#
|
||||
</Link>
|
||||
<br />
|
||||
)}
|
||||
{hoveredCid === quotelinkReply?.cid &&
|
||||
outOfViewCid === quotelinkReply?.cid &&
|
||||
createPortal(
|
||||
|
||||
@@ -6,15 +6,16 @@ import styles from './account-settings.module.css';
|
||||
import useAnonModeStore from '../../../stores/use-anon-mode-store';
|
||||
|
||||
const AnonMode = () => {
|
||||
const { t } = useTranslation();
|
||||
const anonMode = useAnonModeStore((state) => state.anonMode);
|
||||
const setAnonMode = useAnonModeStore((state) => state.setAnonMode);
|
||||
|
||||
return (
|
||||
<div className={styles.anonMode}>
|
||||
<label>
|
||||
<input type='checkbox' checked={anonMode} onChange={(e) => setAnonMode(e.target.checked)} /> anon mode
|
||||
<input type='checkbox' checked={anonMode} onChange={(e) => setAnonMode(e.target.checked)} /> {t('anon_mode')}
|
||||
</label>
|
||||
<span className={styles.settingTip}>Automatically use a different user ID in each thread</span>
|
||||
<span className={styles.settingTip}>{t('anon_mode_description')}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -117,7 +117,7 @@ const CryptoAddressSetting = () => {
|
||||
</button>
|
||||
{showCryptoAddressInfo && (
|
||||
<div className={styles.cryptoAddressInfo}>
|
||||
steps to set a .eth user address:
|
||||
steps to set a .eth address as your ID:
|
||||
<br />
|
||||
<ol>
|
||||
<li>
|
||||
@@ -130,7 +130,7 @@ const CryptoAddressSetting = () => {
|
||||
<li>once you own the address, go to its page, click on "records", then "edit records"</li>
|
||||
<li>add a new text record with name "plebbit-author-address" and value: {account?.signer?.address}</li>
|
||||
</ol>
|
||||
steps to set a .sol user address:
|
||||
steps to set a .sol address as your ID:
|
||||
<br />
|
||||
<ol>
|
||||
<li>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
}
|
||||
|
||||
.setting {
|
||||
text-transform: capitalize;
|
||||
margin-left: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
@@ -25,3 +24,7 @@
|
||||
.interfaceSettings {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.setting input[type="checkbox"] {
|
||||
margin-right: 2px;
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import useAvatarVisibilityStore from '../../../stores/use-avatar-visibility-store';
|
||||
import useTheme from '../../../hooks/use-theme';
|
||||
import styles from './interface-settings.module.css';
|
||||
import packageJson from '../../../../package.json';
|
||||
import styles from './interface-settings.module.css';
|
||||
import _ from 'lodash';
|
||||
|
||||
const commitRef = process.env.REACT_APP_COMMIT_REF;
|
||||
const isElectron = window.isElectron === true;
|
||||
@@ -107,11 +109,16 @@ const InterfaceLanguage = () => {
|
||||
|
||||
const InterfaceSettings = () => {
|
||||
const { t } = useTranslation();
|
||||
const { hideAvatars, setHideAvatars } = useAvatarVisibilityStore();
|
||||
|
||||
const handleHideAvatarsChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setHideAvatars(e.target.checked);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.interfaceSettings}>
|
||||
<div className={styles.version}>
|
||||
{t('version')}:{' '}
|
||||
{_.capitalize(t('version'))}:{' '}
|
||||
<a href={`https://github.com/plebbit/plebchan/releases/tag/v${packageJson.version}`} target='_blank' rel='noopener noreferrer'>
|
||||
{packageJson.version}
|
||||
</a>
|
||||
@@ -122,13 +129,18 @@ const InterfaceSettings = () => {
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.setting}>
|
||||
{t('update')}: <CheckForUpdates />
|
||||
{_.capitalize(t('update'))}: <CheckForUpdates />
|
||||
</div>
|
||||
<div className={styles.setting}>
|
||||
{t('style')}: <Style />
|
||||
{_.capitalize(t('style'))}: <Style />
|
||||
</div>
|
||||
<div className={styles.setting}>
|
||||
{t('interface_language')}: <InterfaceLanguage />
|
||||
{_.capitalize(t('interface_language'))}: <InterfaceLanguage />
|
||||
</div>
|
||||
<div className={styles.setting}>
|
||||
<label>
|
||||
<input type='checkbox' checked={hideAvatars} onChange={handleHideAvatarsChange} /> {_.capitalize(t('hide_avatars'))}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -230,7 +230,7 @@ const PlebbitOptions = () => {
|
||||
</span>
|
||||
</div>
|
||||
<div className={styles.category}>
|
||||
<span className={styles.categoryTitle}>node rpc:</span>
|
||||
<span className={styles.categoryTitle}>node RPC:</span>
|
||||
<span className={styles.categorySettings}>
|
||||
<PlebbitRPCSettings plebbitRpcRef={plebbitRpcRef} />
|
||||
</span>
|
||||
|
||||
@@ -85,7 +85,8 @@ const TopBarDesktop = () => {
|
||||
{renderSubplebbits(projectsSubs)}
|
||||
{renderSubplebbits(interestsSubs)}
|
||||
{renderSubplebbits(randomSubs)}
|
||||
{renderSubplebbits(internationalSubs)}[
|
||||
{renderSubplebbits(internationalSubs)}
|
||||
{/* [
|
||||
<Link
|
||||
to='boards/create'
|
||||
onClick={(e) => {
|
||||
@@ -105,7 +106,7 @@ const TopBarDesktop = () => {
|
||||
>
|
||||
Vote
|
||||
</Link>
|
||||
]
|
||||
] */}
|
||||
</span>
|
||||
<span className={styles.navTopRight}>
|
||||
[<Link to={!location.pathname.endsWith('settings') ? location.pathname.replace(/\/$/, '') + '/settings' : location.pathname}>{t('settings')}</Link>] [
|
||||
|
||||
@@ -2,12 +2,13 @@ import { useAccount } from '@plebbit/plebbit-react-hooks';
|
||||
import useAnonModeStore from '../stores/use-anon-mode-store';
|
||||
|
||||
const useAnonMode = (postCid?: string) => {
|
||||
const { anonMode, threadSigners, setThreadSigner, setAddressSigner, getAddressSigner } = useAnonModeStore((state) => ({
|
||||
const { anonMode, threadSigners, setThreadSigner, setAddressSigner, getAddressSigner, setCurrentAnonSignerAddress } = useAnonModeStore((state) => ({
|
||||
anonMode: state.anonMode,
|
||||
threadSigners: state.threadSigners,
|
||||
setThreadSigner: state.setThreadSigner,
|
||||
setAddressSigner: state.setAddressSigner,
|
||||
getAddressSigner: state.getAddressSigner,
|
||||
setCurrentAnonSignerAddress: state.setCurrentAnonSignerAddress,
|
||||
}));
|
||||
|
||||
const threadSigner = postCid ? threadSigners[postCid] : undefined;
|
||||
@@ -25,6 +26,7 @@ const useAnonMode = (postCid?: string) => {
|
||||
} else {
|
||||
setAddressSigner(signer);
|
||||
}
|
||||
setCurrentAnonSignerAddress(signer.address);
|
||||
}
|
||||
return signer;
|
||||
} catch (error) {
|
||||
@@ -33,6 +35,7 @@ const useAnonMode = (postCid?: string) => {
|
||||
} else {
|
||||
try {
|
||||
const signer = await account?.plebbit.createSigner({ type: 'ed25519', privateKey: threadSigner?.privateKey });
|
||||
setCurrentAnonSignerAddress(signer.address);
|
||||
return signer;
|
||||
} catch (error) {
|
||||
console.error('Failed to retrieve anonymous signer:', error);
|
||||
@@ -43,7 +46,11 @@ const useAnonMode = (postCid?: string) => {
|
||||
};
|
||||
|
||||
const getExistingSigner = (address: string) => {
|
||||
return getAddressSigner(address);
|
||||
const signer = getAddressSigner(address);
|
||||
if (signer) {
|
||||
setCurrentAnonSignerAddress(signer.address);
|
||||
}
|
||||
return signer;
|
||||
};
|
||||
|
||||
return { anonMode, getNewSigner, getExistingSigner };
|
||||
|
||||
+16
-27
@@ -1,4 +1,4 @@
|
||||
import { useMemo, useCallback, useState, useEffect } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { Comment, useAccountComments } from '@plebbit/plebbit-react-hooks';
|
||||
import { flattenCommentsPages } from '@plebbit/plebbit-react-hooks/dist/lib/utils';
|
||||
|
||||
@@ -9,37 +9,26 @@ const useReplies = (comment: Comment) => {
|
||||
// generate a Set of CIDs from flattened replies for quick lookup
|
||||
const replyCids = useMemo(() => new Set(flattenedReplies.map((reply) => reply?.cid)), [flattenedReplies]);
|
||||
|
||||
const [filteredAccountComments, setFilteredAccountComments] = useState<Comment[]>([]);
|
||||
|
||||
const getPostCid = useCallback(
|
||||
(accountComment: Comment, allComments: Comment[]): string | null => {
|
||||
if (accountComment.parentCid === comment?.cid) {
|
||||
return comment?.cid;
|
||||
}
|
||||
const parent = allComments.find((c) => c.cid === accountComment.parentCid);
|
||||
if (!parent) {
|
||||
return null;
|
||||
}
|
||||
return getPostCid(parent, allComments);
|
||||
},
|
||||
[comment?.cid],
|
||||
);
|
||||
|
||||
const { accountComments } = useAccountComments();
|
||||
|
||||
useEffect(() => {
|
||||
const filterComments = (comments: Comment[]) => {
|
||||
return comments.filter((accountComment) => {
|
||||
const parentCid = accountComment.parentCid;
|
||||
if (parentCid === (comment?.cid || 'n/a') || replyCids.has(parentCid)) {
|
||||
const filteredAccountComments = useMemo(() => {
|
||||
const commentMap = new Map(accountComments.map((c) => [c.cid, c]));
|
||||
|
||||
return accountComments.filter((accountComment) => {
|
||||
let currentCid = accountComment.parentCid;
|
||||
while (currentCid && currentCid !== comment?.cid) {
|
||||
if (replyCids.has(currentCid)) {
|
||||
return true;
|
||||
}
|
||||
return getPostCid(accountComment, comments) === comment?.cid;
|
||||
const parent = commentMap.get(currentCid);
|
||||
if (!parent) {
|
||||
return false;
|
||||
}
|
||||
currentCid = parent.parentCid;
|
||||
}
|
||||
return currentCid === comment?.cid;
|
||||
});
|
||||
};
|
||||
|
||||
setFilteredAccountComments(filterComments(accountComments));
|
||||
}, [accountComments, comment?.cid, replyCids, getPostCid]);
|
||||
}, [accountComments, comment?.cid, replyCids]);
|
||||
|
||||
// the account's replies have a delay before getting published, so get them locally from accountComments instead
|
||||
const accountRepliesNotYetPublished = useMemo(() => {
|
||||
|
||||
@@ -10,6 +10,8 @@ interface AnonModeState {
|
||||
getThreadSigner: (postCid: string) => any | undefined;
|
||||
setAddressSigner: (signer: any) => void;
|
||||
getAddressSigner: (address: string) => any | undefined;
|
||||
currentAnonSignerAddress: string | null;
|
||||
setCurrentAnonSignerAddress: (address: string | null) => void;
|
||||
}
|
||||
|
||||
const anonModeStore = localForageLru.createInstance({
|
||||
@@ -39,6 +41,11 @@ const useAnonModeStore = create<AnonModeState>((set, get) => ({
|
||||
anonModeStore.setItem(signer.address, signer);
|
||||
},
|
||||
getAddressSigner: (address: string) => get().addressSigners[address],
|
||||
currentAnonSignerAddress: null,
|
||||
setCurrentAnonSignerAddress: (address: string | null) => {
|
||||
set({ currentAnonSignerAddress: address });
|
||||
anonModeStore.setItem('currentAnonSignerAddress', address);
|
||||
},
|
||||
}));
|
||||
|
||||
const initializeAnonModeStore = async () => {
|
||||
@@ -57,15 +64,16 @@ const initializeAnonModeStore = async () => {
|
||||
}
|
||||
});
|
||||
|
||||
const currentAnonSignerAddress = await anonModeStore.getItem('currentAnonSignerAddress');
|
||||
|
||||
useAnonModeStore.setState((state) => ({
|
||||
anonMode, // Set the retrieved anonMode state
|
||||
threadSigners: { ...threadSigners, ...state.threadSigners },
|
||||
addressSigners: { ...addressSigners, ...state.addressSigners },
|
||||
currentAnonSignerAddress: currentAnonSignerAddress || null,
|
||||
}));
|
||||
};
|
||||
|
||||
initializeAnonModeStore();
|
||||
|
||||
initializeAnonModeStore();
|
||||
|
||||
export default useAnonModeStore;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
|
||||
interface AvatarVisibilityState {
|
||||
hideAvatars: boolean;
|
||||
setHideAvatars: (hide: boolean) => void;
|
||||
}
|
||||
|
||||
const useAvatarVisibilityStore = create<AvatarVisibilityState>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
hideAvatars: false,
|
||||
setHideAvatars: (hide) => set({ hideAvatars: hide }),
|
||||
}),
|
||||
{
|
||||
name: 'avatar-visibility-storage',
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
export default useAvatarVisibilityStore;
|
||||
@@ -7,6 +7,7 @@ const BoxModal = ({ isBoardsBoxModal }: { isBoardsBoxModal: boolean }) => {
|
||||
const { t } = useTranslation();
|
||||
const [showFilterModal, setShowFilterModal] = useState(false);
|
||||
const modalRef = useRef<HTMLDivElement>(null);
|
||||
const buttonRef = useRef<HTMLSpanElement>(null);
|
||||
|
||||
const {
|
||||
showNsfwBoardsOnly,
|
||||
@@ -23,11 +24,11 @@ const BoxModal = ({ isBoardsBoxModal }: { isBoardsBoxModal: boolean }) => {
|
||||
|
||||
const handleClickOutside = useCallback(
|
||||
(event: MouseEvent) => {
|
||||
if (modalRef.current && !modalRef.current.contains(event.target as Node)) {
|
||||
if (modalRef.current && !modalRef.current.contains(event.target as Node) && buttonRef.current && !buttonRef.current.contains(event.target as Node)) {
|
||||
setShowFilterModal(false);
|
||||
}
|
||||
},
|
||||
[modalRef, setShowFilterModal],
|
||||
[modalRef, buttonRef, setShowFilterModal],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -39,7 +40,7 @@ const BoxModal = ({ isBoardsBoxModal }: { isBoardsBoxModal: boolean }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<span ref={modalRef} onClick={() => setShowFilterModal(true)}>
|
||||
<span ref={buttonRef} onClick={() => !showFilterModal && setShowFilterModal(true)}>
|
||||
{isBoardsBoxModal ? t('filter') : t('options')} ▼
|
||||
</span>
|
||||
{showFilterModal && (
|
||||
|
||||
Reference in New Issue
Block a user