diff --git a/electron/main.js b/electron/main.js index 4d0a1899..cb01fa43 100644 --- a/electron/main.js +++ b/electron/main.js @@ -1,5 +1,19 @@ import './log.js'; -import { app, BrowserWindow, Menu, MenuItem, Tray, screen as electronScreen, shell, dialog, nativeTheme, ipcMain, Notification, systemPreferences } from 'electron'; +import { + app, + BrowserWindow, + Menu, + MenuItem, + Tray, + screen as electronScreen, + shell, + dialog, + nativeTheme, + ipcMain, + Notification, + systemPreferences, + clipboard, +} from 'electron'; import isDev from 'electron-is-dev'; import fs from 'fs'; import path from 'path'; @@ -469,3 +483,23 @@ ipcMain.handle('plugin:file-uploader:pickMedia', async (event) => { throw error; } }); + +// Handle request to copy text to clipboard +ipcMain.handle('copy-to-clipboard', async (event, text) => { + try { + clipboard.writeText(text); + return { success: true }; + } catch (error) { + console.error('[Electron Main] Error copying to clipboard:', error); + return { success: false, error: error.message }; + } +}); + +// Handle request for platform info +ipcMain.handle('get-platform', async () => { + return { + platform: process.platform, + arch: process.arch, + version: process.version, + }; +}); diff --git a/electron/preload.mjs b/electron/preload.mjs index 67f520a7..01e61dd6 100644 --- a/electron/preload.mjs +++ b/electron/preload.mjs @@ -19,7 +19,7 @@ ipcRenderer.send('get-plebbit-rpc-auth-key'); // notifications IPC contextBridge.exposeInMainWorld('electron', { invoke: (channel, ...args) => { - const validChannels = ['get-notification-permission-status', 'get-platform', 'test-notification-permission']; + const validChannels = ['get-notification-permission-status', 'get-platform', 'test-notification-permission', 'copy-to-clipboard']; if (validChannels.includes(channel)) { return ipcRenderer.invoke(channel, ...args); } @@ -36,4 +36,5 @@ contextBridge.exposeInMainWorld('electronApi', { getPlatform: () => ipcRenderer.invoke('get-platform'), testNotification: () => ipcRenderer.invoke('test-notification-permission'), showNotification: (notification) => ipcRenderer.send('show-notification', notification), + copyToClipboard: (text) => ipcRenderer.invoke('copy-to-clipboard', text), }); \ No newline at end of file diff --git a/electron/src/preload.ts b/electron/src/preload.ts index b5245be2..36e7a016 100755 --- a/electron/src/preload.ts +++ b/electron/src/preload.ts @@ -25,7 +25,7 @@ contextBridge.exposeInMainWorld('electron', { }, }); -// Expose a dedicated "electronApi" for your UI code +// Expose a dedicated "electronApi" for UI code contextBridge.exposeInMainWorld('electronApi', { isElectron: true, invoke: (channel: string, ...args: any[]) => ipcRenderer.invoke(channel, ...args), diff --git a/package.json b/package.json index bd409da1..3216841e 100755 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "@capacitor/app": "7.0.1", "@capacitor/local-notifications": "7.0.1", "@floating-ui/react": "0.26.1", - "@plebbit/plebbit-react-hooks": "https://github.com/plebbit/plebbit-react-hooks.git#f760ce2f5961307c93517fed0257b425bbf38c30", + "@plebbit/plebbit-react-hooks": "https://github.com/plebbit/plebbit-react-hooks.git#090e70bed5f799fc140a31a6f2123f9125954fde", "@testing-library/jest-dom": "5.14.1", "@testing-library/react": "13.0.0", "@testing-library/user-event": "13.2.1", @@ -26,6 +26,7 @@ "ext-name": "5.0.0", "form-data": "4.0.2", "fs-extra": "11.2.0", + "gifuct-js": "2.1.2", "http-proxy": "1.18.1", "i18next": "23.5.1", "i18next-browser-languagedetector": "7.1.0", diff --git a/public/translations/ar/default.json b/public/translations/ar/default.json index 94428653..d9c5c4b4 100644 --- a/public/translations/ar/default.json +++ b/public/translations/ar/default.json @@ -390,5 +390,24 @@ "drop_here_or": "إسقاط هنا أو", "upload_button_warning": "التحميل التلقائي محظور بواسطة سياسة CORS في متصفحك، ولكنه متاح في تطبيق Seedit لنظام Android وتطبيقات سطح المكتب (Win/Mac/Linux).\n\nاذهب إلى صفحة روابط التنزيل على GitHub؟", "create_community_warning": "إنشاء مجتمع يتطلب تشغيل عقدة كاملة. يقوم تطبيق Seedit لسطح المكتب (Win/Mac/Linux) بتشغيل عقدة كاملة تلقائيًا.\n\nاذهب إلى صفحة روابط التنزيل على GitHub؟", - "avatars": "الصورة الرمزية" + "avatars": "الصورة الرمزية", + "filtering_by_nsfw": "التصفية حسب المجتمعات التي تحتوي على محتوى NSFW", + "notifications": "الإشعارات", + "new_replies_received": "تم استلام ردود جديدة", + "private_key_warning_title": "سيتم عرض مفتاحك الخاص", + "private_key_warning_description": "أنت على وشك عرض بيانات حسابك، والتي تتضمن المفتاح الخاص بك. يجب ألا تشارك مفتاحك الخاص مع أي شخص.", + "go_back": "عد", + "loading_editor": "جارٍ تحميل المحرر", + "show_thumbnails_next_to_links": "عرض الصور المصغرة بجانب الروابط", + "dont_show_thumbnails_next_to_links": "عدم عرض الصور المصغرة بجانب الروابط", + "show_thumbnails_based_on_community_media_preferences": "عرض الصور المصغرة بناءً على تفضيلات وسائط تلك المجتمعة", + "media_previews": "معاينات الوسائط", + "auto_expand_media_previews": "توسيع معاينات الوسائط تلقائيًا", + "dont_auto_expand_media_previews_on_comments_pages": "لا تقم بتوسيع معاينات الوسائط تلقائيًا في صفحات التعليقات", + "video_player": "مشغل الفيديو", + "autoplay_videos_on_comments_page": "تشغيل الفيديوهات تلقائيًا في صفحة التعليقات", + "mute_videos_by_default": "كتم الفيديوهات افتراضياً", + "expand_media_previews_based_on_community_media_preferences": "قم بتوسيع معاينات الوسائط بناءً على تفضيلات الوسائط في تلك المجتمع", + "show_all_nsfw": "عرض كل المحتوى NSFW", + "hide_all_nsfw": "إخفاء كل المحتوى غير الآمن" } \ No newline at end of file diff --git a/public/translations/bn/default.json b/public/translations/bn/default.json index 83a6a2fe..23aa9657 100644 --- a/public/translations/bn/default.json +++ b/public/translations/bn/default.json @@ -390,5 +390,24 @@ "drop_here_or": "এখানে ফেলুন অথবা", "upload_button_warning": "অটো-আপলোড আপনার ব্রাউজারের CORS নীতি দ্বারা ব্লক করা হয়েছে, তবে এটি Seedit অ্যান্ড্রয়েড অ্যাপ এবং ডেস্কটপ অ্যাপ (Win/Mac/Linux)-এ উপলব্ধ।\n\nGitHub এ ডাউনলোড লিঙ্ক পৃষ্ঠায় যান?", "create_community_warning": "একটি কমিউনিটি তৈরি করতে একটি ফুল নোড চালানো প্রয়োজন। Seedit ডেস্কটপ অ্যাপ (Win/Mac/Linux) স্বয়ংক্রিয়ভাবে একটি ফুল নোড চালায়।\n\nGitHub এ ডাউনলোড লিঙ্ক পৃষ্ঠায় যান?", - "avatars": "অ্যাভাটার" + "avatars": "অ্যাভাটার", + "filtering_by_nsfw": "NSFW কমিউনিটি দ্বারা ফিল্টারিং", + "notifications": "নোটিফিকেশন", + "new_replies_received": "নতুন উত্তর পাওয়া গেছে", + "private_key_warning_title": "আপনার ব্যক্তিগত কী প্রদর্শিত হবে", + "private_key_warning_description": "আপনি আপনার অ্যাকাউন্টের ডেটা দেখার পথে আছেন, যার মধ্যে আপনার প্রাইভেট কি রয়েছে। আপনার প্রাইভেট কি কখনই কারো সাথে শেয়ার করা উচিত নয়।", + "go_back": "ফিরে যাও", + "loading_editor": "এডিটর লোড হচ্ছে", + "show_thumbnails_next_to_links": "লিঙ্কের পাশে থাম্বনেইল দেখান", + "dont_show_thumbnails_next_to_links": "লিঙ্কগুলির পাশে থাম্বনেল দেখাবেন না", + "show_thumbnails_based_on_community_media_preferences": "সেই সম্প্রদায়ের মিডিয়া পছন্দের ভিত্তিতে থাম্বনেইল দেখান", + "media_previews": "মিডিয়া প্রিভিউ", + "auto_expand_media_previews": "মিডিয়া প্রিভিউ স্বয়ংক্রিয়ভাবে প্রসারিত করুন", + "dont_auto_expand_media_previews_on_comments_pages": "মন্তব্য পৃষ্ঠাগুলিতে মিডিয়া প্রিভিউ স্বয়ংক্রিয়ভাবে প্রসারিত করবেন না", + "video_player": "ভিডিও প্লেয়ার", + "autoplay_videos_on_comments_page": "মন্তব্য পৃষ্ঠায় ভিডিও স্বয়ংক্রিয়ভাবে চালান", + "mute_videos_by_default": "ডিফল্টভাবে ভিডিও মিউট করুন", + "expand_media_previews_based_on_community_media_preferences": "সেই কমিউনিটির মিডিয়া পছন্দের উপর ভিত্তি করে মিডিয়া প্রিভিউগুলি সম্প্রসারণ করুন", + "show_all_nsfw": "সব NSFW দেখান", + "hide_all_nsfw": "সব NSFW লুকান" } \ No newline at end of file diff --git a/public/translations/cs/default.json b/public/translations/cs/default.json index 9c729ab5..35574d51 100644 --- a/public/translations/cs/default.json +++ b/public/translations/cs/default.json @@ -390,5 +390,24 @@ "drop_here_or": "Přetáhněte sem nebo", "upload_button_warning": "Automatické nahrávání je blokováno politikou CORS vašeho prohlížeče, ale je k dispozici v aplikaci Seedit pro Android a desktopovou aplikaci (Win/Mac/Linux).\n\nPřejděte na stránku odkazů ke stažení na GitHubu?", "create_community_warning": "Vytvoření komunity vyžaduje spuštění plného uzlu. Aplikace Seedit pro desktop (Win/Mac/Linux) automaticky spouští plný uzel.\n\nPřejděte na stránku odkazů ke stažení na GitHubu?", - "avatars": "Avatar" + "avatars": "Avatar", + "filtering_by_nsfw": "Filtrování podle komunit NSFW", + "notifications": "Notifikace", + "new_replies_received": "Přišly nové odpovědi", + "private_key_warning_title": "Váš soukromý klíč bude zobrazen", + "private_key_warning_description": "Chystáte se zobrazit data svého účtu, která zahrnují váš soukromý klíč. Soukromý klíč byste nikdy neměli sdílet s nikým.", + "go_back": "Zpět", + "loading_editor": "Načítání editoru", + "show_thumbnails_next_to_links": "Zobrazit miniatury vedle odkazů", + "dont_show_thumbnails_next_to_links": "Nezobrazovat náhledy vedle odkazů", + "show_thumbnails_based_on_community_media_preferences": "Zobrazit náhledy podle mediálních preferencí dané komunity", + "media_previews": "Náhledy médií", + "auto_expand_media_previews": "Automatické rozbalení náhledů médií", + "dont_auto_expand_media_previews_on_comments_pages": "Neautomaticky rozšiřujte náhledy médií na stránkách komentářů", + "video_player": "Přehrávač videa", + "autoplay_videos_on_comments_page": "Automatické přehrávání videí na stránce komentářů", + "mute_videos_by_default": "Zvuk videí vypnutý ve výchozím nastavení", + "expand_media_previews_based_on_community_media_preferences": "Rozbalte náhledy médií na základě preferencí médií dané komunity", + "show_all_nsfw": "Zobrazit vše NSFW", + "hide_all_nsfw": "Skrýt vše NSFW" } \ No newline at end of file diff --git a/public/translations/da/default.json b/public/translations/da/default.json index 2daef35b..7368a3cd 100644 --- a/public/translations/da/default.json +++ b/public/translations/da/default.json @@ -390,5 +390,24 @@ "drop_here_or": "Drop her eller", "upload_button_warning": "Auto-uploading er blokeret af din browsers CORS-politik, men det er tilgængeligt på Seedit Android-appen og desktopappen (Win/Mac/Linux).\n\nGå til downloadlinks-siden på GitHub?", "create_community_warning": "Oprettelse af et fællesskab kræver, at du kører en fuld node. Seedit desktop-appen (Win/Mac/Linux) kører automatisk en fuld node.\n\nGå til downloadlinks-siden på GitHub?", - "avatars": "Avatarer" + "avatars": "Avatarer", + "filtering_by_nsfw": "Filtrering efter NSFW-fællesskaber", + "notifications": "Notifikationer", + "new_replies_received": "Nye svar modtaget", + "private_key_warning_title": "Din private nøgle vil blive vist", + "private_key_warning_description": "Du er ved at se dine kontodata, som inkluderer din private nøgle. Du bør aldrig dele din private nøgle med nogen.", + "go_back": "Gå tilbage", + "loading_editor": "Indlæser editor", + "show_thumbnails_next_to_links": "Vis miniaturebilleder ved siden af links", + "dont_show_thumbnails_next_to_links": "Vis ikke miniaturer ved siden af links", + "show_thumbnails_based_on_community_media_preferences": "Vis miniaturebilleder baseret på det pågældende fællesskabs mediepræferencer", + "media_previews": "Medieforhåndsvisninger", + "auto_expand_media_previews": "Auto-udvid medie-forhåndsvisninger", + "dont_auto_expand_media_previews_on_comments_pages": "Udvid ikke automatisk medievisninger på kommentarsider", + "video_player": "Videospiller", + "autoplay_videos_on_comments_page": "Autoplay videoer på kommentarsiden", + "mute_videos_by_default": "Slå lyden fra videoer som standard", + "expand_media_previews_based_on_community_media_preferences": "Udvid mediepræsentationer baseret på det fællesskabs mediepræferencer", + "show_all_nsfw": "Vis alle NSFW", + "hide_all_nsfw": "Skjul alle NSFW" } \ No newline at end of file diff --git a/public/translations/de/default.json b/public/translations/de/default.json index f3ad4f44..92eec67e 100644 --- a/public/translations/de/default.json +++ b/public/translations/de/default.json @@ -390,5 +390,24 @@ "drop_here_or": "Hier ablegen oder", "upload_button_warning": "Auto-Upload wird durch die CORS-Richtlinie Ihres Browsers blockiert, ist jedoch in der Seedit Android-App und Desktop-App (Win/Mac/Linux) verfügbar.\n\nGehen Sie zur Download-Link-Seite auf GitHub?", "create_community_warning": "Das Erstellen einer Community erfordert das Ausführen eines vollständigen Knotens. Die Seedit-Desktop-App (Win/Mac/Linux) führt automatisch einen vollständigen Knoten aus.\n\nGehen Sie zur Download-Link-Seite auf GitHub?", - "avatars": "Avatare" + "avatars": "Avatare", + "filtering_by_nsfw": "Filtern nach NSFW-Communities", + "notifications": "Benachrichtigungen", + "new_replies_received": "Neue Antworten erhalten", + "private_key_warning_title": "Ihr privater Schlüssel wird angezeigt", + "private_key_warning_description": "Sie sind dabei, Ihre Kontodaten anzuzeigen, die Ihren privaten Schlüssel enthalten. Sie sollten Ihren privaten Schlüssel niemals mit jemandem teilen.", + "go_back": "Zurück", + "loading_editor": "Editor wird geladen", + "show_thumbnails_next_to_links": "Miniaturansichten neben Links anzeigen", + "dont_show_thumbnails_next_to_links": "Miniaturansichten neben Links nicht anzeigen", + "show_thumbnails_based_on_community_media_preferences": "Vorschaubilder basierend auf den Medienpräferenzen dieser Community anzeigen", + "media_previews": "Medienvorschauen", + "auto_expand_media_previews": "Medienvorschauen automatisch erweitern", + "dont_auto_expand_media_previews_on_comments_pages": "Medienvorschauen auf Kommentarseiten nicht automatisch erweitern", + "video_player": "Videoplayer", + "autoplay_videos_on_comments_page": "Videos auf der Kommentarseite automatisch abspielen", + "mute_videos_by_default": "Videos standardmäßig stummschalten", + "expand_media_previews_based_on_community_media_preferences": "Erweitern Sie Medienvorschauen basierend auf den Medienpräferenzen dieser Community", + "show_all_nsfw": "Alle NSFW anzeigen", + "hide_all_nsfw": "Alle NSFW ausblenden" } \ No newline at end of file diff --git a/public/translations/el/default.json b/public/translations/el/default.json index 68027709..c4d2828e 100644 --- a/public/translations/el/default.json +++ b/public/translations/el/default.json @@ -390,5 +390,24 @@ "drop_here_or": "Μεταφέρετε εδώ ή", "upload_button_warning": "Η αυτόματη αποστολή είναι αποκλεισμένη από την πολιτική CORS του προγράμματος περιήγησής σας, αλλά είναι διαθέσιμη στην εφαρμογή Seedit Android και στην επιτραπέζια εφαρμογή (Win/Mac/Linux).\n\nΜεταβείτε στη σελίδα συνδέσμων λήψης στο GitHub;", "create_community_warning": "Η δημιουργία μιας κοινότητας απαιτεί τη λειτουργία ενός πλήρους κόμβου. Η εφαρμογή Seedit για επιτραπέζιους υπολογιστές (Win/Mac/Linux) εκτελεί αυτόματα έναν πλήρη κόμβο.\n\nΜεταβείτε στη σελίδα συνδέσμων λήψης στο GitHub;", - "avatars": "Εικονίδια" + "avatars": "Εικονίδια", + "filtering_by_nsfw": "Φιλτράρισμα κατά κοινοτήτων NSFW", + "notifications": "Ειδοποιήσεις", + "new_replies_received": "Λήφθηκαν νέες απαντήσεις", + "private_key_warning_title": "Το ιδιωτικό σας κλειδί θα εμφανιστεί", + "private_key_warning_description": "Ετοιμάζεστε να δείτε τα δεδομένα του λογαριασμού σας, τα οποία περιλαμβάνουν το ιδιωτικό σας κλειδί. Δεν πρέπει ποτέ να μοιράζεστε το ιδιωτικό σας κλειδί με κανέναν.", + "go_back": "Επιστροφή", + "loading_editor": "Φόρτωση επεξεργαστή", + "show_thumbnails_next_to_links": "Εμφάνιση μικρογραφιών δίπλα σε συνδέσμους", + "dont_show_thumbnails_next_to_links": "Μην εμφανίζετε μικρογραφίες δίπλα σε συνδέσμους", + "show_thumbnails_based_on_community_media_preferences": "Εμφάνιση μικρογραφιών βάσει των προτιμήσεων μέσων της κοινότητας", + "media_previews": "Προεπισκοπήσεις μέσων", + "auto_expand_media_previews": "Αυτόματη επέκταση προεπισκοπήσεων μέσων", + "dont_auto_expand_media_previews_on_comments_pages": "Μην επεκτείνετε αυτόματα τις προεπισκοπήσεις μέσων στις σελίδες σχολίων", + "video_player": "Προβολέας βίντεο", + "autoplay_videos_on_comments_page": "Αυτόματη αναπαραγωγή βίντεο στη σελίδα σχολίων", + "mute_videos_by_default": "Σίγαση βίντεο από προεπιλογή", + "expand_media_previews_based_on_community_media_preferences": "Επεκτείνετε τις προεπισκοπήσεις μέσων βάσει των προτιμήσεων μέσων της κοινότητας", + "show_all_nsfw": "Εμφάνιση όλων NSFW", + "hide_all_nsfw": "Απόκρυψη όλων των NSFW" } \ No newline at end of file diff --git a/public/translations/en/default.json b/public/translations/en/default.json index c2319dda..50a85851 100644 --- a/public/translations/en/default.json +++ b/public/translations/en/default.json @@ -390,5 +390,24 @@ "drop_here_or": "Drop here or", "upload_button_warning": "Auto-uploading is blocked by your browser's CORS policy, but it is available on the Seedit Android app and desktop app (Win/Mac/Linux).\n\nGo to the download links page on GitHub?", "create_community_warning": "Creating a community requires running a full node. The Seedit desktop app (Win/Mac/Linux) runs a full node automatically.\n\nGo to the download links page on GitHub?", - "avatars": "Avatars" + "avatars": "Avatars", + "filtering_by_nsfw": "Filtering by NSFW communities", + "notifications": "Notifications", + "new_replies_received": "new replies received", + "private_key_warning_title": "Your private key will be displayed", + "private_key_warning_description": "You're about to view your account data, which includes your private key. You should never share your private key with anyone.", + "go_back": "Go back", + "loading_editor": "Loading editor", + "show_thumbnails_next_to_links": "Show thumbnails next to links", + "dont_show_thumbnails_next_to_links": "Don't show thumbnails next to links", + "show_thumbnails_based_on_community_media_preferences": "Show thumbnails based on that community's media preferences", + "media_previews": "media previews", + "auto_expand_media_previews": "Auto-expand media previews", + "dont_auto_expand_media_previews_on_comments_pages": "Don't auto-expand media previews on comments pages", + "video_player": "Video Player", + "autoplay_videos_on_comments_page": "Autoplay videos on the comments page", + "mute_videos_by_default": "Mute videos by default", + "expand_media_previews_based_on_community_media_preferences": "Expand media previews based on that community's media preferences", + "show_all_nsfw": "show all nsfw", + "hide_all_nsfw": "hide all nsfw" } \ No newline at end of file diff --git a/public/translations/es/default.json b/public/translations/es/default.json index df09e179..151be89e 100644 --- a/public/translations/es/default.json +++ b/public/translations/es/default.json @@ -390,5 +390,24 @@ "drop_here_or": "Suelta aquí o", "upload_button_warning": "La carga automática está bloqueada por la política CORS de su navegador, pero está disponible en la aplicación Seedit para Android y en la aplicación de escritorio (Win/Mac/Linux).\n\n¿Vaya a la página de enlaces de descarga en GitHub?", "create_community_warning": "Crear una comunidad requiere ejecutar un nodo completo. La aplicación de escritorio Seedit (Win/Mac/Linux) ejecuta automáticamente un nodo completo.\n\n¿Vaya a la página de enlaces de descarga en GitHub?", - "avatars": "Avatares" + "avatars": "Avatares", + "filtering_by_nsfw": "Filtrando por comunidades NSFW", + "notifications": "Notificaciones", + "new_replies_received": "nuevas respuestas recibidas", + "private_key_warning_title": "Su clave privada será mostrada", + "private_key_warning_description": "Estás a punto de ver los datos de tu cuenta, que incluyen tu clave privada. Nunca debes compartir tu clave privada con nadie.", + "go_back": "Volver", + "loading_editor": "Cargando editor", + "show_thumbnails_next_to_links": "Mostrar miniaturas junto a los enlaces", + "dont_show_thumbnails_next_to_links": "No mostrar miniaturas junto a los enlaces", + "show_thumbnails_based_on_community_media_preferences": "Mostrar miniaturas basadas en las preferencias de medios de esa comunidad", + "media_previews": "previsualizaciones de medios", + "auto_expand_media_previews": "Expansión automática de vistas previas de medios", + "dont_auto_expand_media_previews_on_comments_pages": "No expandir automáticamente las vistas previas de medios en las páginas de comentarios", + "video_player": "Reproductor de video", + "autoplay_videos_on_comments_page": "Reproducción automática de videos en la página de comentarios", + "mute_videos_by_default": "Silenciar videos por defecto", + "expand_media_previews_based_on_community_media_preferences": "Expande las vistas previas de medios según las preferencias de medios de esa comunidad", + "show_all_nsfw": "mostrar todo NSFW", + "hide_all_nsfw": "ocultar todo NSFW" } \ No newline at end of file diff --git a/public/translations/fa/default.json b/public/translations/fa/default.json index a8cecfc3..ee727513 100644 --- a/public/translations/fa/default.json +++ b/public/translations/fa/default.json @@ -390,5 +390,24 @@ "drop_here_or": "اینجا رها کنید یا", "upload_button_warning": "بارگذاری خودکار توسط سیاست CORS مرورگر شما مسدود شده است، اما در اپلیکیشن Seedit برای Android و اپلیکیشن دسکتاپ (Win/Mac/Linux) در دسترس است.\n\nبه صفحه لینک‌های دانلود در GitHub بروید؟", "create_community_warning": "ایجاد یک جامعه نیاز به اجرای یک گره کامل دارد. اپلیکیشن دسکتاپ Seedit (Win/Mac/Linux) به‌طور خودکار یک گره کامل اجرا می‌کند.\n\nبه صفحه لینک‌های دانلود در GitHub بروید؟", - "avatars": "آواتار" + "avatars": "آواتار", + "filtering_by_nsfw": "فیلتر کردن توسط جوامع NSFW", + "notifications": "اطلاعیه‌ها", + "new_replies_received": "پاسخ‌های جدید دریافت شد", + "private_key_warning_title": "کلید خصوصی شما نمایش داده خواهد شد", + "private_key_warning_description": "شما در آستانه مشاهده داده‌های حساب خود هستید که شامل کلید خصوصی شما می‌شود. هرگز نباید کلید خصوصی خود را با کسی به اشتراک بگذارید.", + "go_back": "برگشت", + "loading_editor": "در حال بارگذاری ویرایشگر", + "show_thumbnails_next_to_links": "نمایش تصاویر کوچک کنار لینک‌ها", + "dont_show_thumbnails_next_to_links": "نمایش تصاویر کوچک کنار لینک‌ها را غیرفعال کن", + "show_thumbnails_based_on_community_media_preferences": "نمایش تصاویر کوچک بر اساس تنظیمات رسانه‌ای آن جامعه", + "media_previews": "پیش‌نمایش رسانه", + "auto_expand_media_previews": "گسترش خودکار پیش‌نمایش رسانه‌ها", + "dont_auto_expand_media_previews_on_comments_pages": "نمایش پیش‌نمایش رسانه را در صفحات نظرات به‌طور خودکار باز نکنید", + "video_player": "پخش‌کننده ویدیو", + "autoplay_videos_on_comments_page": "پخش خودکار ویدیوها در صفحه نظرات", + "mute_videos_by_default": "بی‌صدا کردن ویدیوها به طور پیش‌فرض", + "expand_media_previews_based_on_community_media_preferences": "پیش‌نمایش‌های رسانه‌ای را بر اساس ترجیحات رسانه‌ای آن جامعه گسترش دهید", + "show_all_nsfw": "نمایش همه NSFW", + "hide_all_nsfw": "مخفی کردن همه محتوای NSFW" } \ No newline at end of file diff --git a/public/translations/fi/default.json b/public/translations/fi/default.json index e77c6419..8d8eb257 100644 --- a/public/translations/fi/default.json +++ b/public/translations/fi/default.json @@ -390,5 +390,24 @@ "drop_here_or": "Pudota tänne tai", "upload_button_warning": "Automaattinen lataus on estetty selaimesi CORS-politiikalla, mutta se on saatavilla Seedit Android -sovelluksessa ja työpöytäsovelluksessa (Win/Mac/Linux).\n\nMene GitHubin latauslinkkisivulle?", "create_community_warning": "Yhteisön luominen vaatii täyden solmun ajamisen. Seeditin työpöytäsovellus (Win/Mac/Linux) ajaa automaattisesti täyden solmun.\n\nMene GitHubin latauslinkkisivulle?", - "avatars": "Avatarit" + "avatars": "Avatarit", + "filtering_by_nsfw": "Suodatus NSFW-yhteisöjen mukaan", + "notifications": "Ilmoitukset", + "new_replies_received": "uusia vastauksia saatu", + "private_key_warning_title": "Yksityinen avaimesi näytetään", + "private_key_warning_description": "Olet katsomassa tilisi tietoja, jotka sisältävät yksityisen avaimesi. Älä koskaan jaa yksityistä avaintasi kenellekään.", + "go_back": "Palaa", + "loading_editor": "Ladataan editoria", + "show_thumbnails_next_to_links": "Näytä pikkukuvat linkkien vieressä", + "dont_show_thumbnails_next_to_links": "Älä näytä pikkukuvia linkkien vieressä", + "show_thumbnails_based_on_community_media_preferences": "Näytä pikkukuvat kyseisen yhteisön median mieltymysten perusteella", + "media_previews": "mediasisällöt", + "auto_expand_media_previews": "Laajenna median esikatselut automaattisesti", + "dont_auto_expand_media_previews_on_comments_pages": "Älä laajenna mediasisältöjen esikatseluja automaattisesti kommenttisivuilla", + "video_player": "Videon toistin", + "autoplay_videos_on_comments_page": "Toista videot automaattisesti kommenttisivulla", + "mute_videos_by_default": "Mykistä videot oletuksena", + "expand_media_previews_based_on_community_media_preferences": "Laajenna mediakatsauksia kyseisen yhteisön mediatoiveiden perusteella", + "show_all_nsfw": "näytä kaikki NSFW", + "hide_all_nsfw": "piilota kaikki NSFW" } \ No newline at end of file diff --git a/public/translations/fil/default.json b/public/translations/fil/default.json index 1289a4e4..dc15937d 100644 --- a/public/translations/fil/default.json +++ b/public/translations/fil/default.json @@ -390,5 +390,24 @@ "drop_here_or": "I-drag dito o", "upload_button_warning": "Ang awtomatikong pag-upload ay naharang ng CORS policy ng iyong browser, ngunit ito ay magagamit sa Seedit Android app at desktop app (Win/Mac/Linux).\n\nPumunta sa page ng download links sa GitHub?", "create_community_warning": "Ang paggawa ng komunidad ay nangangailangan ng pagpapatakbo ng isang buong node. Ang Seedit desktop app (Win/Mac/Linux) ay awtomatikong nagpapatakbo ng isang buong node.\n\nPumunta sa page ng download links sa GitHub?", - "avatars": "Mga Avatar" + "avatars": "Mga Avatar", + "filtering_by_nsfw": "Pag-filter ayon sa mga komunidad ng NSFW", + "notifications": "Mga Notipikasyon", + "new_replies_received": "may bagong natanggap na mga sagot", + "private_key_warning_title": "Ipapakita ang iyong pribadong susi", + "private_key_warning_description": "Malapit mo nang makita ang iyong account data, na kasama ang iyong private key. Hindi mo dapat ibahagi ang iyong private key sa kahit kanino.", + "go_back": "Bumalik", + "loading_editor": "Naglo-load ng editor", + "show_thumbnails_next_to_links": "Ipakita ang mga thumbnail sa tabi ng mga link", + "dont_show_thumbnails_next_to_links": "Huwag ipakita ang mga thumbnail sa tabi ng mga link", + "show_thumbnails_based_on_community_media_preferences": "Ipakita ang mga thumbnail batay sa mga kagustuhan sa media ng komunidad na iyon", + "media_previews": "mga preview ng media", + "auto_expand_media_previews": "Awtomatikong palawakin ang mga preview ng media", + "dont_auto_expand_media_previews_on_comments_pages": "Huwag awtomatikong palawakin ang media preview sa mga pahina ng komento", + "video_player": "Video Player", + "autoplay_videos_on_comments_page": "Awtomatikong pag-play ng mga video sa pahina ng mga komento", + "mute_videos_by_default": "I-mute ang mga video bilang default", + "expand_media_previews_based_on_community_media_preferences": "Palawakin ang mga preview ng media batay sa mga kagustuhan ng media ng komunidad na iyon", + "show_all_nsfw": "ipakita lahat ng NSFW", + "hide_all_nsfw": "Itago lahat ng NSFW" } \ No newline at end of file diff --git a/public/translations/fr/default.json b/public/translations/fr/default.json index db27407c..8b1b436c 100644 --- a/public/translations/fr/default.json +++ b/public/translations/fr/default.json @@ -390,5 +390,24 @@ "drop_here_or": "Déposez ici ou", "upload_button_warning": "Le téléchargement automatique est bloqué par la politique CORS de votre navigateur, mais il est disponible sur l'application Seedit Android et l'application de bureau (Win/Mac/Linux).\n\nAllez à la page des liens de téléchargement sur GitHub?", "create_community_warning": "Créer une communauté nécessite l'exécution d'un nœud complet. L'application Seedit pour bureau (Win/Mac/Linux) exécute automatiquement un nœud complet.\n\nAllez à la page des liens de téléchargement sur GitHub?", - "avatars": "Avatars" + "avatars": "Avatars", + "filtering_by_nsfw": "Filtrage par communautés NSFW", + "notifications": "Notifications", + "new_replies_received": "nouvelles réponses reçues", + "private_key_warning_title": "Votre clé privée sera affichée", + "private_key_warning_description": "Vous êtes sur le point de voir les données de votre compte, qui incluent votre clé privée. Vous ne devez jamais partager votre clé privée avec qui que ce soit.", + "go_back": "Retour", + "loading_editor": "Chargement de l'éditeur", + "show_thumbnails_next_to_links": "Afficher les vignettes à côté des liens", + "dont_show_thumbnails_next_to_links": "Ne pas afficher les miniatures à côté des liens", + "show_thumbnails_based_on_community_media_preferences": "Afficher les vignettes en fonction des préférences médias de cette communauté", + "media_previews": "aperçus des médias", + "auto_expand_media_previews": "Extension automatique des aperçus médias", + "dont_auto_expand_media_previews_on_comments_pages": "Ne pas développer automatiquement les aperçus médias sur les pages de commentaires", + "video_player": "Lecteur vidéo", + "autoplay_videos_on_comments_page": "Lecture automatique des vidéos sur la page des commentaires", + "mute_videos_by_default": "Mettre les vidéos en sourdine par défaut", + "expand_media_previews_based_on_community_media_preferences": "Développez les aperçus des médias en fonction des préférences médias de cette communauté", + "show_all_nsfw": "afficher tout NSFW", + "hide_all_nsfw": "masquer tout NSFW" } \ No newline at end of file diff --git a/public/translations/he/default.json b/public/translations/he/default.json index 105625cc..ea26ea22 100644 --- a/public/translations/he/default.json +++ b/public/translations/he/default.json @@ -390,5 +390,24 @@ "drop_here_or": "הנח כאן או", "upload_button_warning": "העלאה אוטומטית חסומה על ידי מדיניות ה-CORS של הדפדפן שלך, אך היא זמינה באפליקציית Seedit לאנדרואיד ובאפליקציית הדסקטופ (Win/Mac/Linux).\n\nעבור לדף קישורי ההורדה ב-GitHub?", "create_community_warning": "יצירת קהילה דורשת הפעלת צומת מלא. אפליקציית שולחן העבודה של Seedit (Win/Mac/Linux) מפעילה צומת מלא באופן אוטומטי.\n\nעבור לדף קישורי ההורדה ב-GitHub?", - "avatars": "תמונות פרופיל" + "avatars": "תמונות פרופיל", + "filtering_by_nsfw": "סינון לפי קהילות NSFW", + "notifications": "הודעות", + "new_replies_received": "תגובות חדשות התקבלו", + "private_key_warning_title": "המפתח הפרטי שלך יוצג", + "private_key_warning_description": "אתה עומד לצפות בנתוני החשבון שלך, הכוללים את המפתח הפרטי שלך. לעולם לא כדאי לשתף את המפתח הפרטי שלך עם אף אחד.", + "go_back": "חזור", + "loading_editor": "טוען עורך", + "show_thumbnails_next_to_links": "הצג תמונות ממוזערות לצד קישורים", + "dont_show_thumbnails_next_to_links": "אל תציג תמונות ממוזערות ליד קישורים", + "show_thumbnails_based_on_community_media_preferences": "הצג תמונות ממוזערות בהתאם להעדפות המדיה של הקהילה", + "media_previews": "תצוגות מדיה", + "auto_expand_media_previews": "הרחבת תצוגות מקדימות של מדיה אוטומטית", + "dont_auto_expand_media_previews_on_comments_pages": "אל תפתח אוטומטית תצוגות מדיה מקדימות בדפי תגובות", + "video_player": "נגן וידאו", + "autoplay_videos_on_comments_page": "השמעת וידאו אוטומטית בדף ההערות", + "mute_videos_by_default": "כיבוי קול לסרטונים כבררת מחדל", + "expand_media_previews_based_on_community_media_preferences": "הרחב תצוגות מדיה בהתאם להעדפות המדיה של הקהילה", + "show_all_nsfw": "הצג הכל NSFW", + "hide_all_nsfw": "הסתר את כל התוכן הלא בטוח" } \ No newline at end of file diff --git a/public/translations/hi/default.json b/public/translations/hi/default.json index d5dbbf89..f4e63662 100644 --- a/public/translations/hi/default.json +++ b/public/translations/hi/default.json @@ -390,5 +390,24 @@ "drop_here_or": "यहां छोड़ें या", "upload_button_warning": "ऑटो-अपलोड आपके ब्राउज़र की CORS नीति द्वारा अवरुद्ध है, लेकिन यह Seedit Android ऐप और डेस्कटॉप ऐप (Win/Mac/Linux) पर उपलब्ध है।\n\nGitHub पर डाउनलोड लिंक पृष्ठ पर जाएं?", "create_community_warning": "एक समुदाय बनाने के लिए एक पूर्ण नोड चलाना आवश्यक है। Seedit डेस्कटॉप ऐप (Win/Mac/Linux) स्वचालित रूप से एक पूर्ण नोड चलाता है।\n\nGitHub पर डाउनलोड लिंक पृष्ठ पर जाएं?", - "avatars": "अवतार" + "avatars": "अवतार", + "filtering_by_nsfw": "NSFW समुदायों द्वारा फ़िल्टरिंग", + "notifications": "सूचनाएँ", + "new_replies_received": "नई प्रतिक्रियाएँ प्राप्त हुईं", + "private_key_warning_title": "आपकी निजी कुंजी प्रदर्शित होगी", + "private_key_warning_description": "आप अपने खाते का डेटा देखने वाले हैं, जिसमें आपका निजी कुंजी शामिल है। आपको अपनी निजी कुंजी किसी के साथ कभी साझा नहीं करनी चाहिए।", + "go_back": "वापस जाएं", + "loading_editor": "एडिटर लोड हो रहा है", + "show_thumbnails_next_to_links": "लिंक के बगल में थंबनेल दिखाएं", + "dont_show_thumbnails_next_to_links": "लिंक के पास थंबनेल न दिखाएं", + "show_thumbnails_based_on_community_media_preferences": "उस समुदाय की मीडिया प्राथमिकताओं के आधार पर थंबनेल दिखाएं", + "media_previews": "मीडिया पूर्वावलोकन", + "auto_expand_media_previews": "मीडिया प्रीव्यू स्वतः विस्तारित करें", + "dont_auto_expand_media_previews_on_comments_pages": "टिप्पणी पृष्ठों पर मीडिया पूर्वावलोकन को स्वचालित रूप से विस्तार न करें", + "video_player": "वीडियो प्लेयर", + "autoplay_videos_on_comments_page": "टिप्पणियों के पृष्ठ पर वीडियो स्वचालित रूप से चलाएं", + "mute_videos_by_default": "वीडियो को डिफ़ॉल्ट रूप से म्यूट करें", + "expand_media_previews_based_on_community_media_preferences": "उस समुदाय की मीडिया प्राथमिकताओं के आधार पर मीडिया पूर्वावलोकन का विस्तार करें", + "show_all_nsfw": "सभी NSFW दिखाएं", + "hide_all_nsfw": "सभी NSFW छुपाएं" } \ No newline at end of file diff --git a/public/translations/hu/default.json b/public/translations/hu/default.json index 8466f4ea..22bb5c2b 100644 --- a/public/translations/hu/default.json +++ b/public/translations/hu/default.json @@ -390,5 +390,24 @@ "drop_here_or": "Húzza ide, vagy", "upload_button_warning": "Az automatikus feltöltés a böngészője CORS irányelvei által blokkolva van, de elérhető a Seedit Android alkalmazásban és asztali alkalmazásban (Win/Mac/Linux).\n\nMenjen a letöltési linkek oldalára a GitHub-on?", "create_community_warning": "Közösség létrehozása teljes csomópont futtatását igényli. A Seedit asztali alkalmazás (Win/Mac/Linux) automatikusan futtat egy teljes csomópontot.\n\nMenjen a letöltési linkek oldalára a GitHub-on?", - "avatars": "Avatarok" + "avatars": "Avatarok", + "filtering_by_nsfw": "Szűrés NSFW közösségek szerint", + "notifications": "Értesítések", + "new_replies_received": "új válaszok érkeztek", + "private_key_warning_title": "A privát kulcsod meg lesz jelenítve", + "private_key_warning_description": "Meg fogod tekinteni a fiókod adatait, amelyek tartalmazzák a privát kulcsodat. Soha ne oszd meg a privát kulcsodat senkivel.", + "go_back": "Vissza", + "loading_editor": "Szerkesztő betöltése", + "show_thumbnails_next_to_links": "Miniatűrök megjelenítése a linkek mellett", + "dont_show_thumbnails_next_to_links": "Ne jelenjenek meg bélyegképek a linkek mellett", + "show_thumbnails_based_on_community_media_preferences": "Közösség média preferenciái alapján jelenítse meg a bélyegképeket", + "media_previews": "médiabelnézetek", + "auto_expand_media_previews": "Média előnézetek automatikus kibontása", + "dont_auto_expand_media_previews_on_comments_pages": "Ne bontsa automatikusan a média előnézeteket a hozzászólásoldalakon", + "video_player": "Videolejátszó", + "autoplay_videos_on_comments_page": "Videók automatikus lejátszása a hozzászólások oldalán", + "mute_videos_by_default": "Videók alapértelmezett némítása", + "expand_media_previews_based_on_community_media_preferences": "Bővítse a média előnézeteket a közösség média preferenciái alapján", + "show_all_nsfw": "mutasd az összes NSFW-t", + "hide_all_nsfw": "Minden NSFW elrejtése" } \ No newline at end of file diff --git a/public/translations/id/default.json b/public/translations/id/default.json index 01708683..927b3aea 100644 --- a/public/translations/id/default.json +++ b/public/translations/id/default.json @@ -390,5 +390,24 @@ "drop_here_or": "Seretkan di sini atau", "upload_button_warning": "Pengunggahan otomatis diblokir oleh kebijakan CORS browser Anda, tetapi tersedia di aplikasi Seedit Android dan aplikasi desktop (Win/Mac/Linux).\n\nPergi ke halaman tautan unduhan di GitHub?", "create_community_warning": "Membuat komunitas memerlukan menjalankan node penuh. Aplikasi desktop Seedit (Win/Mac/Linux) secara otomatis menjalankan node penuh.\n\nPergi ke halaman tautan unduhan di GitHub?", - "avatars": "Avatar" + "avatars": "Avatar", + "filtering_by_nsfw": "Penyaringan berdasarkan komunitas NSFW", + "notifications": "Notifikasi", + "new_replies_received": "balasan baru diterima", + "private_key_warning_title": "Kunci pribadi Anda akan ditampilkan", + "private_key_warning_description": "Anda akan melihat data akun Anda, yang mencakup kunci pribadi Anda. Anda tidak boleh pernah membagikan kunci pribadi Anda dengan siapa pun.", + "go_back": "Kembali", + "loading_editor": "Memuat editor", + "show_thumbnails_next_to_links": "Tampilkan thumbnail di samping tautan", + "dont_show_thumbnails_next_to_links": "Jangan tampilkan thumbnail di samping tautan", + "show_thumbnails_based_on_community_media_preferences": "Tampilkan thumbnail berdasarkan preferensi media komunitas tersebut", + "media_previews": "pratinjau media", + "auto_expand_media_previews": "Perluas pratinjau media secara otomatis", + "dont_auto_expand_media_previews_on_comments_pages": "Jangan otomatis memperluas pratinjau media di halaman komentar", + "video_player": "Pemutar Video", + "autoplay_videos_on_comments_page": "Putar otomatis video di halaman komentar", + "mute_videos_by_default": "Diamkan video secara default", + "expand_media_previews_based_on_community_media_preferences": "Perluas pratinjau media berdasarkan preferensi media komunitas tersebut", + "show_all_nsfw": "tampilkan semua NSFW", + "hide_all_nsfw": "sembunyikan semua NSFW" } \ No newline at end of file diff --git a/public/translations/it/default.json b/public/translations/it/default.json index e19d4201..5c606122 100644 --- a/public/translations/it/default.json +++ b/public/translations/it/default.json @@ -390,5 +390,24 @@ "drop_here_or": "Trascina qui o", "upload_button_warning": "Il caricamento automatico è bloccato dalla politica CORS del tuo browser, ma è disponibile nell'app Seedit per Android e nell'app desktop (Win/Mac/Linux).\n\nVai alla pagina dei link per il download su GitHub?", "create_community_warning": "Creare una comunità richiede l'esecuzione di un nodo completo. L'app Seedit per desktop (Win/Mac/Linux) esegue automaticamente un nodo completo.\n\nVai alla pagina dei link per il download su GitHub?", - "avatars": "Avatar" + "avatars": "Avatar", + "filtering_by_nsfw": "Filtraggio per comunità NSFW", + "notifications": "Notifiche", + "new_replies_received": "nuove risposte ricevute", + "private_key_warning_title": "La tua chiave privata verrà mostrata", + "private_key_warning_description": "Stai per visualizzare i dati del tuo account, che includono la tua chiave privata. Non dovresti mai condividere la tua chiave privata con nessuno.", + "go_back": "Torna indietro", + "loading_editor": "Caricamento editor", + "show_thumbnails_next_to_links": "Mostra le anteprime accanto ai link", + "dont_show_thumbnails_next_to_links": "Non mostrare le miniature accanto ai link", + "show_thumbnails_based_on_community_media_preferences": "Mostra le miniature basate sulle preferenze dei media di quella comunità", + "media_previews": "anteprime media", + "auto_expand_media_previews": "Espansione automatica delle anteprime multimediali", + "dont_auto_expand_media_previews_on_comments_pages": "Non espandere automaticamente le anteprime multimediali nelle pagine dei commenti", + "video_player": "Lettore video", + "autoplay_videos_on_comments_page": "Riproduci automaticamente i video nella pagina dei commenti", + "mute_videos_by_default": "Disattiva l’audio dei video per impostazione predefinita", + "expand_media_previews_based_on_community_media_preferences": "Espandi le anteprime multimediali in base alle preferenze multimediali di quella comunità", + "show_all_nsfw": "mostra tutto NSFW", + "hide_all_nsfw": "nascondi tutto NSFW" } \ No newline at end of file diff --git a/public/translations/ja/default.json b/public/translations/ja/default.json index ee248dd3..7fd0f107 100644 --- a/public/translations/ja/default.json +++ b/public/translations/ja/default.json @@ -390,5 +390,24 @@ "drop_here_or": "ここにドロップまたは", "upload_button_warning": "自動アップロードはブラウザのCORSポリシーでブロックされていますが、SeeditのAndroidアプリおよびデスクトップアプリ(Win/Mac/Linux)では利用可能です。\n\nGitHubのダウンロードリンクページに移動しますか?", "create_community_warning": "コミュニティを作成するには、フルノードを実行する必要があります。Seeditのデスクトップアプリ(Win/Mac/Linux)は自動的にフルノードを実行します。\n\nGitHubのダウンロードリンクページに移動しますか?", - "avatars": "アバター" + "avatars": "アバター", + "filtering_by_nsfw": "NSFW コミュニティによるフィルタリング", + "notifications": "通知", + "new_replies_received": "新しい返信を受信しました", + "private_key_warning_title": "あなたの秘密鍵が表示されます", + "private_key_warning_description": "あなたは自分のアカウントデータ(秘密鍵を含む)を表示しようとしています。秘密鍵を誰とも共有してはいけません。", + "go_back": "戻る", + "loading_editor": "エディターを読み込み中", + "show_thumbnails_next_to_links": "リンクの横にサムネイルを表示する", + "dont_show_thumbnails_next_to_links": "リンクの横にサムネイルを表示しない", + "show_thumbnails_based_on_community_media_preferences": "そのコミュニティのメディア設定に基づいてサムネイルを表示する", + "media_previews": "メディアプレビュー", + "auto_expand_media_previews": "メディアプレビューを自動で展開", + "dont_auto_expand_media_previews_on_comments_pages": "コメントページでメディアプレビューを自動展開しない", + "video_player": "ビデオプレーヤー", + "autoplay_videos_on_comments_page": "コメントページで動画を自動再生", + "mute_videos_by_default": "動画をデフォルトでミュートにする", + "expand_media_previews_based_on_community_media_preferences": "そのコミュニティのメディアの好みに基づいてメディアプレビューを展開する", + "show_all_nsfw": "すべてのNSFWを表示", + "hide_all_nsfw": "すべてのNSFWを非表示" } \ No newline at end of file diff --git a/public/translations/ko/default.json b/public/translations/ko/default.json index 65bf28be..e2702ef2 100644 --- a/public/translations/ko/default.json +++ b/public/translations/ko/default.json @@ -390,5 +390,24 @@ "drop_here_or": "여기에 드롭 또는", "upload_button_warning": "자동 업로드는 브라우저의 CORS 정책에 의해 차단되었지만 Seedit Android 앱과 데스크탑 앱(Win/Mac/Linux)에서 사용할 수 있습니다.\n\nGitHub의 다운로드 링크 페이지로 이동하시겠습니까?", "create_community_warning": "커뮤니티를 만들려면 전체 노드를 실행해야 합니다. Seedit 데스크탑 앱 (Win/Mac/Linux)은 자동으로 전체 노드를 실행합니다.\n\nGitHub의 다운로드 링크 페이지로 이동하시겠습니까?", - "avatars": "아바타" + "avatars": "아바타", + "filtering_by_nsfw": "NSFW 커뮤니티별 필터링", + "notifications": "알림", + "new_replies_received": "새로운 답글이 도착했습니다", + "private_key_warning_title": "개인 키가 표시됩니다", + "private_key_warning_description": "계정 데이터를 보려고 합니다. 여기에는 개인 키가 포함되어 있습니다. 개인 키를 누구와도 공유해서는 안 됩니다.", + "go_back": "뒤로 가기", + "loading_editor": "에디터 로딩 중", + "show_thumbnails_next_to_links": "링크 옆에 썸네일 표시", + "dont_show_thumbnails_next_to_links": "링크 옆에 썸네일 표시 안 함", + "show_thumbnails_based_on_community_media_preferences": "해당 커뮤니티의 미디어 선호도에 따라 썸네일 표시", + "media_previews": "미디어 미리보기", + "auto_expand_media_previews": "미디어 미리보기를 자동 확장", + "dont_auto_expand_media_previews_on_comments_pages": "댓글 페이지에서 미디어 미리보기를 자동 확장하지 않음", + "video_player": "비디오 플레이어", + "autoplay_videos_on_comments_page": "댓글 페이지에서 동영상 자동 재생", + "mute_videos_by_default": "기본적으로 동영상 음소거", + "expand_media_previews_based_on_community_media_preferences": "해당 커뮤니티의 미디어 선호도에 따라 미디어 미리보기를 확장합니다", + "show_all_nsfw": "모든 NSFW 보기", + "hide_all_nsfw": "모든 NSFW 숨기기" } \ No newline at end of file diff --git a/public/translations/mr/default.json b/public/translations/mr/default.json index 71cfaf12..e1cc38d5 100644 --- a/public/translations/mr/default.json +++ b/public/translations/mr/default.json @@ -390,5 +390,24 @@ "drop_here_or": "इथे ड्रॉप करा किंवा", "upload_button_warning": "ऑटो-अपलोड आपल्या ब्राउझरच्या CORS धोरणामुळे अवरुद्ध आहे, परंतु ते Seedit Android अॅप आणि डेस्कटॉप अॅप (Win/Mac/Linux) वर उपलब्ध आहे.\n\nGitHub वर डाउनलोड लिंक पृष्ठावर जा?", "create_community_warning": "एक समुदाय तयार करण्यासाठी एक पूर्ण नोड चालवणे आवश्यक आहे. Seedit डेस्कटॉप अॅप (Win/Mac/Linux) स्वयंचलितपणे एक पूर्ण नोड चालवते.\n\nGitHub वर डाउनलोड लिंक पृष्ठावर जा?", - "avatars": "अवतार" + "avatars": "अवतार", + "filtering_by_nsfw": "NSFW समुदायांद्वारे फिल्टरिंग", + "notifications": "सूचनाएँ", + "new_replies_received": "नवीन प्रतिक्रिया प्राप्त", + "private_key_warning_title": "तुमची खासगी कळी दर्शविली जाईल", + "private_key_warning_description": "तुम्ही तुमच्या खात्याचा डेटा पाहणार आहात, ज्यामध्ये तुमची खाजगी कळी समाविष्ट आहे. तुम्ही कधीही तुमची खाजगी कळी कोणासही शेअर करू नये.", + "go_back": "परत जा", + "loading_editor": "एडिटर लोड करत आहे", + "show_thumbnails_next_to_links": "दुव्यांबाजूला थंबनेल दर्शवा", + "dont_show_thumbnails_next_to_links": "दुव्यांच्या बाजूला थंबनेल दाखवू नका", + "show_thumbnails_based_on_community_media_preferences": "त्या समुदायाच्या मीडिया प्राधान्यांवर आधारित थंबनेल दर्शवा", + "media_previews": "मिडिया पूर्वावलोकने", + "auto_expand_media_previews": "मीडिया प्रिव्ह्यू स्वयंचलितपणे वाढवा", + "dont_auto_expand_media_previews_on_comments_pages": "टिप्पणी पृष्ठांवर मीडिया पूर्वावलोकन आपोआप विस्तृत करू नका", + "video_player": "व्हिडिओ प्लेयर", + "autoplay_videos_on_comments_page": "टिप्पण्यांच्या पृष्ठावर व्हिडिओ ऑटोप्ले करा", + "mute_videos_by_default": "व्हिडिओ डिफॉल्टने म्यूट करा", + "expand_media_previews_based_on_community_media_preferences": "त्या समुदायाच्या मीडिया प्राधान्यांनुसार मीडिया प्रीव्ह्यू विस्तारा", + "show_all_nsfw": "सर्व NSFW दाखवा", + "hide_all_nsfw": "सर्व NSFW लपवा" } \ No newline at end of file diff --git a/public/translations/nl/default.json b/public/translations/nl/default.json index 2c1a7817..8937edbf 100644 --- a/public/translations/nl/default.json +++ b/public/translations/nl/default.json @@ -390,5 +390,24 @@ "drop_here_or": "Sleep hierheen of", "upload_button_warning": "Auto-upload wordt geblokkeerd door het CORS-beleid van uw browser, maar het is beschikbaar op de Seedit Android-app en desktopapp (Win/Mac/Linux).\n\nGa naar de downloadlinks-pagina op GitHub?", "create_community_warning": "Een gemeenschap creëren vereist het uitvoeren van een volledige node. De Seedit desktop-app (Win/Mac/Linux) voert automatisch een volledige node uit.\n\nGa naar de downloadlinks-pagina op GitHub?", - "avatars": "Avatars" + "avatars": "Avatars", + "filtering_by_nsfw": "Filteren op NSFW-gemeenschappen", + "notifications": "Meldingen", + "new_replies_received": "nieuwe reacties ontvangen", + "private_key_warning_title": "Uw privésleutel wordt weergegeven", + "private_key_warning_description": "Je staat op het punt je accountgegevens te bekijken, inclusief je private key. Je moet je private key nooit met iemand delen.", + "go_back": "Ga terug", + "loading_editor": "Editor laden", + "show_thumbnails_next_to_links": "Miniaturen naast links weergeven", + "dont_show_thumbnails_next_to_links": "Miniaturen niet naast links weergeven", + "show_thumbnails_based_on_community_media_preferences": "Toon miniaturen op basis van de mediavoorkeuren van die gemeenschap", + "media_previews": "mediavoorbeelden", + "auto_expand_media_previews": "Automatisch media-voorbeeld uitbreiden", + "dont_auto_expand_media_previews_on_comments_pages": "Media-preview op reactiespagina's niet automatisch uitvouwen", + "video_player": "Videospeler", + "autoplay_videos_on_comments_page": "Video's automatisch afspelen op de reactiespagina", + "mute_videos_by_default": "Video’s standaard dempen", + "expand_media_previews_based_on_community_media_preferences": "Breid mediavoorbeelden uit op basis van de mediavoorkeuren van die gemeenschap", + "show_all_nsfw": "toon alle NSFW", + "hide_all_nsfw": "verberg alle NSFW" } \ No newline at end of file diff --git a/public/translations/no/default.json b/public/translations/no/default.json index ba4d8234..2d2e6d25 100644 --- a/public/translations/no/default.json +++ b/public/translations/no/default.json @@ -390,5 +390,24 @@ "drop_here_or": "Slipp her eller", "upload_button_warning": "Auto-opplasting er blokkert av CORS-policyen til nettleseren din, men det er tilgjengelig på Seedit Android-app og desktopapp (Win/Mac/Linux).\n\nGå til nedlastingslenkesiden på GitHub?", "create_community_warning": "Opprettelse av et fellesskap krever at du kjører en full node. Seedit desktop-appen (Win/Mac/Linux) kjører automatisk en full node.\n\nGå til nedlastingslenkesiden på GitHub?", - "avatars": "Avatarer" + "avatars": "Avatarer", + "filtering_by_nsfw": "Filtrering etter NSFW-fellesskap", + "notifications": "Varsler", + "new_replies_received": "nye svar mottatt", + "private_key_warning_title": "Din private nøkkel vil bli vist", + "private_key_warning_description": "Du er i ferd med å se kontodataene dine, som inkluderer din private nøkkel. Du bør aldri dele din private nøkkel med noen.", + "go_back": "Gå tilbake", + "loading_editor": "Laster editor", + "show_thumbnails_next_to_links": "Vis miniatyrbilder ved siden av lenker", + "dont_show_thumbnails_next_to_links": "Ikke vis miniatyrbilder ved siden av lenker", + "show_thumbnails_based_on_community_media_preferences": "Vis miniatyrbilder basert på det fellesskapets mediainnstillinger", + "media_previews": "mediavisninger", + "auto_expand_media_previews": "Automatisk utvidelse av medievisninger", + "dont_auto_expand_media_previews_on_comments_pages": "Ikke automatisk utvid medieforhåndsvisninger på kommentarsider", + "video_player": "Videospiller", + "autoplay_videos_on_comments_page": "Autospill videoer på kommentarsiden", + "mute_videos_by_default": "Demp videoer som standard", + "expand_media_previews_based_on_community_media_preferences": "Utvid medievisninger basert på det fellesskapets mediepreferanser", + "show_all_nsfw": "vis alle NSFW", + "hide_all_nsfw": "skjul all NSFW" } \ No newline at end of file diff --git a/public/translations/pl/default.json b/public/translations/pl/default.json index 95c77146..30b4fcc4 100644 --- a/public/translations/pl/default.json +++ b/public/translations/pl/default.json @@ -390,5 +390,24 @@ "drop_here_or": "Upuść tutaj lub", "upload_button_warning": "Automatyczne przesyłanie jest blokowane przez politykę CORS w twojej przeglądarce, ale jest dostępne w aplikacji Seedit na Androida oraz w aplikacji desktopowej (Win/Mac/Linux).\n\nPrzejdź do strony linków do pobrania na GitHubie?", "create_community_warning": "Tworzenie społeczności wymaga uruchomienia pełnego węzła. Aplikacja Seedit desktop (Win/Mac/Linux) automatycznie uruchamia pełny węzeł.\n\nPrzejdź do strony linków do pobrania na GitHubie?", - "avatars": "Awatary" + "avatars": "Awatary", + "filtering_by_nsfw": "Filtrowanie według społeczności NSFW", + "notifications": "Powiadomienia", + "new_replies_received": "otrzymano nowe odpowiedzi", + "private_key_warning_title": "Twój klucz prywatny zostanie wyświetlony", + "private_key_warning_description": "Zaraz zobaczysz dane swojego konta, które zawierają Twój klucz prywatny. Nigdy nie powinieneś udostępniać swojego klucza prywatnego nikomu.", + "go_back": "Wróć", + "loading_editor": "Ładowanie edytora", + "show_thumbnails_next_to_links": "Pokaż miniatury obok linków", + "dont_show_thumbnails_next_to_links": "Nie pokazuj miniatur obok linków", + "show_thumbnails_based_on_community_media_preferences": "Pokaż miniatury na podstawie preferencji mediów danej społeczności", + "media_previews": "podglądy mediów", + "auto_expand_media_previews": "Automatyczne rozwijanie podglądów multimediów", + "dont_auto_expand_media_previews_on_comments_pages": "Nie rozwijaj automatycznie podglądów mediów na stronach komentarzy", + "video_player": "Odtwarzacz wideo", + "autoplay_videos_on_comments_page": "Autoodtwarzanie filmów na stronie komentarzy", + "mute_videos_by_default": "Domyślnie wycisz filmy", + "expand_media_previews_based_on_community_media_preferences": "Rozwiń podglądy mediów na podstawie preferencji mediów tej społeczności", + "show_all_nsfw": "pokaż wszystkie NSFW", + "hide_all_nsfw": "ukryj wszystkie NSFW" } \ No newline at end of file diff --git a/public/translations/pt/default.json b/public/translations/pt/default.json index 813554d9..92165202 100644 --- a/public/translations/pt/default.json +++ b/public/translations/pt/default.json @@ -390,5 +390,24 @@ "drop_here_or": "Solte aqui ou", "upload_button_warning": "O upload automático está bloqueado pela política CORS do seu navegador, mas está disponível no aplicativo Seedit para Android e no aplicativo desktop (Win/Mac/Linux).\n\nVá para a página de links de download no GitHub?", "create_community_warning": "Criar uma comunidade exige a execução de um nó completo. O aplicativo Seedit para desktop (Win/Mac/Linux) executa automaticamente um nó completo.\n\nVá para a página de links de download no GitHub?", - "avatars": "Avatares" + "avatars": "Avatares", + "filtering_by_nsfw": "Filtragem por comunidades NSFW", + "notifications": "Notificações", + "new_replies_received": "novas respostas recebidas", + "private_key_warning_title": "Sua chave privada será exibida", + "private_key_warning_description": "Você está prestes a visualizar os dados da sua conta, que incluem sua chave privada. Nunca deve compartilhar sua chave privada com ninguém.", + "go_back": "Voltar", + "loading_editor": "Carregando editor", + "show_thumbnails_next_to_links": "Mostrar miniaturas ao lado dos links", + "dont_show_thumbnails_next_to_links": "Não mostrar miniaturas ao lado dos links", + "show_thumbnails_based_on_community_media_preferences": "Mostrar miniaturas com base nas preferências de mídia daquela comunidade", + "media_previews": "visualizações de mídia", + "auto_expand_media_previews": "Expandir visualizações de mídia automaticamente", + "dont_auto_expand_media_previews_on_comments_pages": "Não expanda automaticamente as pré-visualizações de mídia nas páginas de comentários", + "video_player": "Reprodutor de vídeo", + "autoplay_videos_on_comments_page": "Reproduzir vídeos automaticamente na página de comentários", + "mute_videos_by_default": "Silenciar vídeos por padrão", + "expand_media_previews_based_on_community_media_preferences": "Expanda as visualizações de mídia com base nas preferências de mídia daquela comunidade", + "show_all_nsfw": "mostrar todo NSFW", + "hide_all_nsfw": "ocultar todo NSFW" } \ No newline at end of file diff --git a/public/translations/ro/default.json b/public/translations/ro/default.json index 131988e4..5494b36a 100644 --- a/public/translations/ro/default.json +++ b/public/translations/ro/default.json @@ -390,5 +390,24 @@ "drop_here_or": "Aruncați aici sau", "upload_button_warning": "Încărcarea automată este blocată de politica CORS a browser-ului tău, dar este disponibilă în aplicația Seedit pentru Android și aplicațiile desktop (Win/Mac/Linux).\n\nMergi la pagina cu linkuri de descărcare pe GitHub?", "create_community_warning": "Crearea unei comunități necesită rularea unui nod complet. Aplicația Seedit pentru desktop (Win/Mac/Linux) rulează automat un nod complet.\n\nMergi la pagina cu linkuri de descărcare pe GitHub?", - "avatars": "Avatare" + "avatars": "Avatare", + "filtering_by_nsfw": "Filtrare după comunități NSFW", + "notifications": "Notificări", + "new_replies_received": "noi răspunsuri primite", + "private_key_warning_title": "Cheia dvs. privată va fi afișată", + "private_key_warning_description": "Ești pe punctul de a vizualiza datele contului tău, care includ cheia ta privată. Nu ar trebui să îți împărtășești cheia privată cu nimeni.", + "go_back": "Înapoi", + "loading_editor": "Se încarcă editorul", + "show_thumbnails_next_to_links": "Afișați miniaturi lângă linkuri", + "dont_show_thumbnails_next_to_links": "Nu afișa miniaturi lângă linkuri", + "show_thumbnails_based_on_community_media_preferences": "Afișează miniaturi în funcție de preferințele media ale comunității respective", + "media_previews": "previzualizări media", + "auto_expand_media_previews": "Extindere automată a previzualizărilor media", + "dont_auto_expand_media_previews_on_comments_pages": "Nu extinde automat previzualizările media pe paginile de comentarii", + "video_player": "Player video", + "autoplay_videos_on_comments_page": "Redare automată a videoclipurilor pe pagina de comentarii", + "mute_videos_by_default": "Dezactivează sunetul videoclipurilor în mod implicit", + "expand_media_previews_based_on_community_media_preferences": "Extindeți previzualizările media pe baza preferințelor media ale comunității respective", + "show_all_nsfw": "arată tot NSFW", + "hide_all_nsfw": "ascunde tot NSFW" } \ No newline at end of file diff --git a/public/translations/ru/default.json b/public/translations/ru/default.json index d01a7b37..76bf5480 100644 --- a/public/translations/ru/default.json +++ b/public/translations/ru/default.json @@ -390,5 +390,24 @@ "drop_here_or": "Перетащите сюда или", "upload_button_warning": "Автоматическая загрузка заблокирована политикой CORS вашего браузера, но она доступна в приложении Seedit для Android и в настольных приложениях (Win/Mac/Linux).\n\nПерейдите на страницу ссылок для загрузки на GitHub?", "create_community_warning": "Создание сообщества требует запуска полного узла. Приложение Seedit для настольных ПК (Win/Mac/Linux) автоматически запускает полный узел.\n\nПерейдите на страницу ссылок для загрузки на GitHub?", - "avatars": "Аватары" + "avatars": "Аватары", + "filtering_by_nsfw": "Фильтрация по сообществам NSFW", + "notifications": "Уведомления", + "new_replies_received": "получены новые ответы", + "private_key_warning_title": "Ваш приватный ключ будет отображен", + "private_key_warning_description": "Вы собираетесь просмотреть данные своего аккаунта, которые включают ваш приватный ключ. Никогда не делитесь своим приватным ключом с кем-либо.", + "go_back": "Назад", + "loading_editor": "Загрузка редактора", + "show_thumbnails_next_to_links": "Показывать миниатюры рядом со ссылками", + "dont_show_thumbnails_next_to_links": "Не показывать миниатюры рядом со ссылками", + "show_thumbnails_based_on_community_media_preferences": "Показывать миниатюры на основе медиа-предпочтений сообщества", + "media_previews": "предпросмотры медиа", + "auto_expand_media_previews": "Автоматическое расширение предварительного просмотра медиа", + "dont_auto_expand_media_previews_on_comments_pages": "Не разворачивать автоматически медиа-превью на страницах комментариев", + "video_player": "Видеоплеер", + "autoplay_videos_on_comments_page": "Автовоспроизведение видео на странице комментариев", + "mute_videos_by_default": "Отключать звук видео по умолчанию", + "expand_media_previews_based_on_community_media_preferences": "Расширьте предварительный просмотр медиа на основе предпочтений медиа этого сообщества", + "show_all_nsfw": "показать все NSFW", + "hide_all_nsfw": "скрыть весь NSFW" } \ No newline at end of file diff --git a/public/translations/sq/default.json b/public/translations/sq/default.json index a9d53d1f..15b28433 100644 --- a/public/translations/sq/default.json +++ b/public/translations/sq/default.json @@ -390,5 +390,24 @@ "drop_here_or": "Hidh këtu ose", "upload_button_warning": "Ngarkohet automatikisht është bllokuar nga politika CORS e shfletuesit tuaj, por është në dispozicion në aplikacionin Seedit për Android dhe aplikacionin desktop (Win/Mac/Linux).\n\nShkoni te faqja e lidhjeve për shkarkimin në GitHub?", "create_community_warning": "Krijimi i një komuniteti kërkon ekzekutimin e një nyjeje të plotë. Aplikacioni Seedit për desktop (Win/Mac/Linux) ekzekuton automatikisht një nyje të plotë.\n\nShkoni te faqja e lidhjeve për shkarkimin në GitHub?", - "avatars": "Avataret" + "avatars": "Avataret", + "filtering_by_nsfw": "Filtrimi sipas grupeve NSFW", + "notifications": "Njoftimet", + "new_replies_received": "përgjigje të reja të marra", + "private_key_warning_title": "Çelësi juaj privat do të shfaqet", + "private_key_warning_description": "Jeni për të parë të dhënat e llogarisë suaj, që përfshijnë çelësin tuaj privat. Nuk duhet ta ndani kurrë çelësin tuaj privat me askënd.", + "go_back": "Kthehu", + "loading_editor": "Duke ngarkuar redaktorin", + "show_thumbnails_next_to_links": "Trego miniatura pranë lidhjeve", + "dont_show_thumbnails_next_to_links": "Mos tregoni miniatura pranë lidhjeve", + "show_thumbnails_based_on_community_media_preferences": "Trego miniaturat bazuar në preferencat e mediave të komunitetit", + "media_previews": "paraqitjet e medias", + "auto_expand_media_previews": "Zgjerimi automatik i parashikimeve të mediave", + "dont_auto_expand_media_previews_on_comments_pages": "Mos zgjeroni automatikisht parapamjet e mediave në faqet e komenteve", + "video_player": "Përdorues video", + "autoplay_videos_on_comments_page": "Luaj automatikisht videot në faqen e komenteve", + "mute_videos_by_default": "Çkyçni videot si parazgjedhje", + "expand_media_previews_based_on_community_media_preferences": "Zgjeroni parapamjet e mediave bazuar në preferencat e mediave të asaj komuniteti", + "show_all_nsfw": "trego të gjitha NSFW", + "hide_all_nsfw": "fsheh të gjitha NSFW" } \ No newline at end of file diff --git a/public/translations/sv/default.json b/public/translations/sv/default.json index 3d40276d..909b1cca 100644 --- a/public/translations/sv/default.json +++ b/public/translations/sv/default.json @@ -390,5 +390,24 @@ "drop_here_or": "Dra hit eller", "upload_button_warning": "Auto-uppladdning blockeras av din webbläsares CORS-policy, men det är tillgängligt i Seedit Android-app och skrivbordsapp (Win/Mac/Linux).\n\nGå till sidan för nedladdningslänkar på GitHub?", "create_community_warning": "Att skapa ett community kräver att du kör en full node. Seedit desktop-appen (Win/Mac/Linux) kör automatiskt en full node.\n\nGå till sidan för nedladdningslänkar på GitHub?", - "avatars": "Avatarer" + "avatars": "Avatarer", + "filtering_by_nsfw": "Filtrering efter NSFW-gemenskaper", + "notifications": "Aviseringar", + "new_replies_received": "nya svar mottagna", + "private_key_warning_title": "Din privata nyckel kommer att visas", + "private_key_warning_description": "Du är på väg att visa dina kontouppgifter, inklusive din privata nyckel. Du bör aldrig dela din privata nyckel med någon.", + "go_back": "Gå tillbaka", + "loading_editor": "Laddar redigerare", + "show_thumbnails_next_to_links": "Visa miniatyrer bredvid länkar", + "dont_show_thumbnails_next_to_links": "Visa inte miniatyrer bredvid länkar", + "show_thumbnails_based_on_community_media_preferences": "Visa miniatyrer baserat på det communitys mediainställningar", + "media_previews": "mediavisningar", + "auto_expand_media_previews": "Auto-expandera medieförhandsvisningar", + "dont_auto_expand_media_previews_on_comments_pages": "Expandera inte automatiskt mediavisningar på kommentarsidor", + "video_player": "Videospelare", + "autoplay_videos_on_comments_page": "Autospelning av videor på kommentarsidan", + "mute_videos_by_default": "Stäng av ljudet i videor som standard", + "expand_media_previews_based_on_community_media_preferences": "Expandera medieförhandsvisningar baserat på den gemenskapens mediapreferenser", + "show_all_nsfw": "visa all NSFW", + "hide_all_nsfw": "göm allt NSFW" } \ No newline at end of file diff --git a/public/translations/te/default.json b/public/translations/te/default.json index 851035d4..c1c54612 100644 --- a/public/translations/te/default.json +++ b/public/translations/te/default.json @@ -390,5 +390,24 @@ "drop_here_or": "ఇక్కడ వదిలివేయండి లేదా", "upload_button_warning": "ఆటో-అప్లోడింగ్ మీ బ్రౌజర్ యొక్క CORS విధానం ద్వారా అడ్డుకోబడింది, కానీ ఇది Seedit Android యాప్ మరియు డెస్క్టాప్ యాప్ (Win/Mac/Linux) లో లభిస్తుంది.\n\nGitHub లో డౌన్లోడ్ లింకుల పేజీకి వెళ్లండి?", "create_community_warning": "ఒక కమ్యూనిటీని సృష్టించడానికి పూర్తి నోడు నడపడం అవసరం. Seedit డెస్క్‌టాప్ యాప్ (Win/Mac/Linux) ఆటోమేటిక్‌గా పూర్తి నోడును నడుపుతుంది.\n\nGitHub లో డౌన్లోడ్ లింకుల పేజీకి వెళ్లండి?", - "avatars": "అవతార్లు" + "avatars": "అవతార్లు", + "filtering_by_nsfw": "NSFW సముదాయాల ద్వారా ఫిల్టరింగ్", + "notifications": "సూచనల", + "new_replies_received": "కొత్త సమాధానాలు వచ్చాయి", + "private_key_warning_title": "మీ వ్యక్తిగత కీ ప్రదర్శించబడుతుంది", + "private_key_warning_description": "మీరు మీ ఖాతా డేటాను చూడబోతున్నారు, దీనిలో మీ ప్రైవేట్ కీ కూడా ఉంటుంది. మీ ప్రైవేట్ కీని ఎవరితోనూ పంచుకోకూడదు.", + "go_back": "తిరిగి వెళ్లండి", + "loading_editor": "ఎడిటర్ లోడవుతుంది", + "show_thumbnails_next_to_links": "లింకుల పక్కన థంబ్‌నెయిల్స్ చూపించు", + "dont_show_thumbnails_next_to_links": "లింక్‌ల పక్కన థంబ్‌నెయిల్స్ చూపించవద్దు", + "show_thumbnails_based_on_community_media_preferences": "ఆ కమ్యూనిటీ మాధ్యమాల ఇష్టాలకు అనుగుణంగా థంబ్నెయిల్స్ చూపించు", + "media_previews": "మీడియా ప్రివ్యూలు", + "auto_expand_media_previews": "మీడియా ప్రివ్యూలను ఆటోగా విస్తరించు", + "dont_auto_expand_media_previews_on_comments_pages": "వ్యాఖ్య పేజీలపై మీడియా ప్రివ్యూలను ఆటో ఎక్స్‌పాండ్ చేయవద్దు", + "video_player": "వీడియో ప్లేయర్", + "autoplay_videos_on_comments_page": "వ్యాఖ్యల పేజీలో వీడియోలను ఆటోప్లే చేయండి", + "mute_videos_by_default": "డిఫాల్ట్‌గా వీడియోలను మ్యూట్ చేయండి", + "expand_media_previews_based_on_community_media_preferences": "ఆ కమ్యూనిటీ మీడియా అభిరుచుల ఆధారంగా మీడియా ప్రివ్యూలను విస్తరించండి", + "show_all_nsfw": "అన్నీ NSFW చూపించు", + "hide_all_nsfw": "అన్ని NSFW దాచు" } \ No newline at end of file diff --git a/public/translations/th/default.json b/public/translations/th/default.json index 28953529..a90eb936 100644 --- a/public/translations/th/default.json +++ b/public/translations/th/default.json @@ -390,5 +390,24 @@ "drop_here_or": "วางที่นี่หรือ", "upload_button_warning": "การอัปโหลดอัตโนมัติถูกบล็อกโดยนโยบาย CORS ของเบราว์เซอร์ของคุณ แต่สามารถใช้ได้ในแอป Seedit สำหรับ Android และแอปเดสก์ท็อป (Win/Mac/Linux)\n\nไปที่หน้าลิงก์ดาวน์โหลดบน GitHub?", "create_community_warning": "การสร้างชุมชนต้องการการรันโหนดเต็ม Seedit desktop app (Win/Mac/Linux) รันโหนดเต็มโดยอัตโนมัติ\n\nไปที่หน้าลิงก์ดาวน์โหลดบน GitHub?", - "avatars": "อวาตาร์" + "avatars": "อวาตาร์", + "filtering_by_nsfw": "การกรองโดยชุมชน NSFW", + "notifications": "การแจ้งเตือน", + "new_replies_received": "ได้รับคำตอบใหม่", + "private_key_warning_title": "คีย์ส่วนตัวของคุณจะแสดง", + "private_key_warning_description": "คุณกำลังจะดูข้อมูลบัญชีของคุณ ซึ่งรวมถึงกุญแจส่วนตัวของคุณ คุณไม่ควรแบ่งปันกุญแจส่วนตัวของคุณกับใคร", + "go_back": "ย้อนกลับ", + "loading_editor": "กำลังโหลดตัวแก้ไข", + "show_thumbnails_next_to_links": "แสดงภาพขนาดย่อถัดจากลิงก์", + "dont_show_thumbnails_next_to_links": "ไม่แสดงภาพขนาดย่อถัดจากลิงก์", + "show_thumbnails_based_on_community_media_preferences": "แสดงภาพขนาดย่อโดยอิงตามความชอบสื่อของชุมชนนั้น", + "media_previews": "ตัวอย่างสื่อ", + "auto_expand_media_previews": "ขยายตัวอย่างสื่อโดยอัตโนมัติ", + "dont_auto_expand_media_previews_on_comments_pages": "อย่าแสดงตัวอย่างมีเดียโดยอัตโนมัติบนหน้าคอมเมนต์", + "video_player": "เครื่องเล่นวิดีโอ", + "autoplay_videos_on_comments_page": "เล่นวิดีโออัตโนมัติบนหน้าคอมเมนต์", + "mute_videos_by_default": "ปิดเสียงวิดีโอโดยค่าเริ่มต้น", + "expand_media_previews_based_on_community_media_preferences": "ขยายตัวอย่างสื่อโดยอิงตามความชอบสื่อของชุมชนนั้น", + "show_all_nsfw": "แสดง NSFW ทั้งหมด", + "hide_all_nsfw": "ซ่อนทั้งหมด NSFW" } \ No newline at end of file diff --git a/public/translations/tr/default.json b/public/translations/tr/default.json index 50b76569..492b9b57 100644 --- a/public/translations/tr/default.json +++ b/public/translations/tr/default.json @@ -390,5 +390,24 @@ "drop_here_or": "Burası veya bırak", "upload_button_warning": "Otomatik yükleme, tarayıcınızın CORS politikası nedeniyle engelleniyor, ancak Seedit Android uygulaması ve masaüstü uygulamasında (Win/Mac/Linux) mevcuttur.\n\nGitHub'daki indirme bağlantıları sayfasına gidin?", "create_community_warning": "Bir topluluk oluşturmak, tam bir düğüm çalıştırmayı gerektirir. Seedit masaüstü uygulaması (Win/Mac/Linux) otomatik olarak tam bir düğüm çalıştırır.\n\nGitHub'daki indirme bağlantıları sayfasına gidin?", - "avatars": "Avatarlar" + "avatars": "Avatarlar", + "filtering_by_nsfw": "NSFW topluluklarına göre filtreleme", + "notifications": "Bildirimler", + "new_replies_received": "yeni yanıtlar alındı", + "private_key_warning_title": "Özel anahtarınız görüntülenecek", + "private_key_warning_description": "Hesap verilerinizi görüntülemek üzeresiniz, bu veriler özel anahtarınızı da içerir. Özel anahtarınızı kimseyle paylaşmamalısınız.", + "go_back": "Geri dön", + "loading_editor": "Düzenleyici yükleniyor", + "show_thumbnails_next_to_links": "Bağlantıların yanına küçük resimler göster", + "dont_show_thumbnails_next_to_links": "Linklerin yanında küçük resimleri gösterme", + "show_thumbnails_based_on_community_media_preferences": "O topluluğun medya tercihine göre küçük resimleri göster", + "media_previews": "medya önizlemeleri", + "auto_expand_media_previews": "Medya önizlemelerini otomatik genişlet", + "dont_auto_expand_media_previews_on_comments_pages": "Yorum sayfalarında medya önizlemelerini otomatik genişletme", + "video_player": "Video Oynatıcı", + "autoplay_videos_on_comments_page": "Yorum sayfasında videoları otomatik oynat", + "mute_videos_by_default": "Videoları varsayılan olarak sessize al", + "expand_media_previews_based_on_community_media_preferences": "Topluluğun medya tercihleri temelinde medya önizlemelerini genişlet", + "show_all_nsfw": "tüm NSFW'yi göster", + "hide_all_nsfw": "tüm NSFW'yi gizle" } \ No newline at end of file diff --git a/public/translations/uk/default.json b/public/translations/uk/default.json index 46c4ce31..868b90b7 100644 --- a/public/translations/uk/default.json +++ b/public/translations/uk/default.json @@ -390,5 +390,24 @@ "drop_here_or": "Перетягніть сюди або", "upload_button_warning": "Автозавантаження заблоковано політикою CORS вашого браузера, але воно доступне в додатку Seedit для Android та настільному додатку (Win/Mac/Linux).\n\nПерейдіть на сторінку посилань для завантаження на GitHub?", "create_community_warning": "Створення спільноти вимагає запуску повного вузла. Десктоп-додаток Seedit (Win/Mac/Linux) автоматично запускає повний вузол.\n\nПерейдіть на сторінку посилань для завантаження на GitHub?", - "avatars": "Аватари" + "avatars": "Аватари", + "filtering_by_nsfw": "Фільтрація за спільнотами NSFW", + "notifications": "Повідомлення", + "new_replies_received": "отримано нові відповіді", + "private_key_warning_title": "Ваш приватний ключ буде відображено", + "private_key_warning_description": "Ви збираєтеся переглянути дані свого акаунту, які включають ваш приватний ключ. Ніколи не діліться своїм приватним ключем з кимось.", + "go_back": "Повернутися", + "loading_editor": "Завантаження редактора", + "show_thumbnails_next_to_links": "Показувати мініатюри поруч із посиланнями", + "dont_show_thumbnails_next_to_links": "Не показувати мініатюри поруч із посиланнями", + "show_thumbnails_based_on_community_media_preferences": "Показувати мініатюри відповідно до медіапереваг спільноти", + "media_previews": "попередні перегляди медіа", + "auto_expand_media_previews": "Автоматичне розгортання медіапопередніх переглядів", + "dont_auto_expand_media_previews_on_comments_pages": "Не розгортати медіа-попередній перегляд автоматично на сторінках коментарів", + "video_player": "Відеопрогравач", + "autoplay_videos_on_comments_page": "Автовідтворення відео на сторінці коментарів", + "mute_videos_by_default": "Вимикати звук відео за замовчуванням", + "expand_media_previews_based_on_community_media_preferences": "Розгорніть попередній перегляд медіа на основі медіапредпочтань цієї спільноти", + "show_all_nsfw": "показати всі NSFW", + "hide_all_nsfw": "приховати весь NSFW" } \ No newline at end of file diff --git a/public/translations/ur/default.json b/public/translations/ur/default.json index 0c6663cf..d13ea62b 100644 --- a/public/translations/ur/default.json +++ b/public/translations/ur/default.json @@ -390,5 +390,24 @@ "drop_here_or": "یہاں چھوڑیں یا", "upload_button_warning": "آٹو اپلوڈ آپ کے براؤزر کی CORS پالیسی کے ذریعہ بلاک کر دیا گیا ہے، لیکن یہ Seedit اینڈروئیڈ ایپ اور ڈیسک ٹاپ ایپ (Win/Mac/Linux) پر دستیاب ہے۔\n\nGitHub پر ڈاؤن لوڈ لنکس پیج پر جائیں؟", "create_community_warning": "کمیونٹی بنانے کے لیے مکمل نوڈ چلانا ضروری ہے۔ Seedit ڈیسک ٹاپ ایپ (Win/Mac/Linux) خودکار طور پر مکمل نوڈ چلاتا ہے۔\n\nGitHub پر ڈاؤن لوڈ لنکس پیج پر جائیں؟", - "avatars": "اواتار" + "avatars": "اواتار", + "filtering_by_nsfw": "NSFW کمیونٹیز کے ذریعے فلٹرنگ", + "notifications": "اطلاعات", + "new_replies_received": "نئی جوابات موصول ہوئے", + "private_key_warning_title": "آپ کی نجی کلید دکھائی جائے گی", + "private_key_warning_description": "آپ اپنے اکاؤنٹ کا ڈیٹا دیکھنے والے ہیں، جس میں آپ کی پرائیویٹ کی شامل ہے۔ آپ کو اپنی پرائیویٹ کی کبھی کسی کے ساتھ شیئر نہیں کرنی چاہیے۔", + "go_back": "واپس جاؤ", + "loading_editor": "ایڈیٹر لوڈ ہو رہا ہے", + "show_thumbnails_next_to_links": "لنکس کے ساتھ تھمب نیلز دکھائیں", + "dont_show_thumbnails_next_to_links": "لنکس کے ساتھ تھمب نیلز نہ دکھائیں", + "show_thumbnails_based_on_community_media_preferences": "کمیونٹی کی میڈیا ترجیحات کی بنیاد پر تھمب نیل دکھائیں", + "media_previews": "میڈیا پریویوز", + "auto_expand_media_previews": "میڈیا پری ویوز کو خود بخود پھیلائیں", + "dont_auto_expand_media_previews_on_comments_pages": "تبصرہ صفحات پر میڈیا پریویوز کو خودکار طور پر نہ بڑھائیں", + "video_player": "ویڈیو پلیئر", + "autoplay_videos_on_comments_page": "کمنٹس پیج پر ویڈیوز خودکار چلائیں", + "mute_videos_by_default": "ویڈیوز کو ڈیفالٹ طور پر خاموش کریں", + "expand_media_previews_based_on_community_media_preferences": "کمیونٹی کی میڈیا ترجیحات کی بنیاد پر میڈیا پریویوز کو وسعت دیں", + "show_all_nsfw": "تمام NSFW دکھائیں", + "hide_all_nsfw": "تمام NSFW چھپائیں" } \ No newline at end of file diff --git a/public/translations/vi/default.json b/public/translations/vi/default.json index a94ce6f4..7e325ebc 100644 --- a/public/translations/vi/default.json +++ b/public/translations/vi/default.json @@ -390,5 +390,24 @@ "drop_here_or": "Thả ở đây hoặc", "upload_button_warning": "Tải lên tự động bị chặn bởi chính sách CORS của trình duyệt của bạn, nhưng có sẵn trên ứng dụng Seedit Android và ứng dụng desktop (Win/Mac/Linux).\n\nĐi đến trang liên kết tải xuống trên GitHub?", "create_community_warning": "Tạo cộng đồng yêu cầu chạy một node đầy đủ. Ứng dụng Seedit desktop (Win/Mac/Linux) tự động chạy một node đầy đủ.\n\nĐi đến trang liên kết tải xuống trên GitHub?", - "avatars": "Ảnh đại diện" + "avatars": "Ảnh đại diện", + "filtering_by_nsfw": "Lọc theo cộng đồng NSFW", + "notifications": "Thông báo", + "new_replies_received": "đã nhận phản hồi mới", + "private_key_warning_title": "Khóa riêng tư của bạn sẽ được hiển thị", + "private_key_warning_description": "Bạn sắp xem dữ liệu tài khoản của mình, bao gồm cả khóa riêng tư. Bạn không nên chia sẻ khóa riêng tư của mình với bất kỳ ai.", + "go_back": "Quay lại", + "loading_editor": "Đang tải trình soạn thảo", + "show_thumbnails_next_to_links": "Hiển thị hình thu nhỏ bên cạnh các liên kết", + "dont_show_thumbnails_next_to_links": "Không hiển thị ảnh thu nhỏ bên cạnh liên kết", + "show_thumbnails_based_on_community_media_preferences": "Hiển thị hình thu nhỏ dựa trên sở thích truyền thông của cộng đồng đó", + "media_previews": "xem trước phương tiện", + "auto_expand_media_previews": "Tự động mở rộng bản xem trước phương tiện", + "dont_auto_expand_media_previews_on_comments_pages": "Không tự động mở rộng xem trước phương tiện trên trang bình luận", + "video_player": "Trình phát video", + "autoplay_videos_on_comments_page": "Phát video tự động trên trang bình luận", + "mute_videos_by_default": "Tắt tiếng video theo mặc định", + "expand_media_previews_based_on_community_media_preferences": "Mở rộng bản xem trước phương tiện dựa trên sở thích phương tiện của cộng đồng đó", + "show_all_nsfw": "hiển thị tất cả NSFW", + "hide_all_nsfw": "ẩn tất cả NSFW" } \ No newline at end of file diff --git a/public/translations/zh/default.json b/public/translations/zh/default.json index 900528d0..d4aa74f3 100644 --- a/public/translations/zh/default.json +++ b/public/translations/zh/default.json @@ -390,5 +390,24 @@ "drop_here_or": "拖放到此处或", "upload_button_warning": "自动上传被浏览器的CORS政策阻止,但可以在Seedit Android应用程序和桌面应用程序(Win/Mac/Linux)上使用。\n\n前往GitHub上的下载链接页面?", "create_community_warning": "创建社区需要运行一个完整的节点。Seedit桌面应用程序(Win/Mac/Linux)会自动运行一个完整的节点。\n\n前往GitHub上的下载链接页面?", - "avatars": "头像" + "avatars": "头像", + "filtering_by_nsfw": "根据NSFW社区进行过滤", + "notifications": "通知", + "new_replies_received": "收到新回复", + "private_key_warning_title": "您的私钥将被显示", + "private_key_warning_description": "您即将查看您的账户数据,其中包括您的私钥。您永远不应与任何人分享您的私钥。", + "go_back": "返回", + "loading_editor": "正在加载编辑器", + "show_thumbnails_next_to_links": "在链接旁显示缩略图", + "dont_show_thumbnails_next_to_links": "不要在链接旁显示缩略图", + "show_thumbnails_based_on_community_media_preferences": "根据该社区的媒体偏好显示缩略图", + "media_previews": "媒体预览", + "auto_expand_media_previews": "自动展开媒体预览", + "dont_auto_expand_media_previews_on_comments_pages": "不要在评论页面自动展开媒体预览", + "video_player": "视频播放器", + "autoplay_videos_on_comments_page": "在评论页面自动播放视频", + "mute_videos_by_default": "默认静音视频", + "expand_media_previews_based_on_community_media_preferences": "根据该社区的媒体偏好展开媒体预览", + "show_all_nsfw": "显示所有 NSFW", + "hide_all_nsfw": "隐藏所有NSFW" } \ No newline at end of file diff --git a/src/app.tsx b/src/app.tsx index ae1bd1f1..87f8fa66 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -118,7 +118,6 @@ const App = () => { } /> } /> } /> - } /> } /> diff --git a/src/components/error-display/error-display.tsx b/src/components/error-display/error-display.tsx index 02203400..f1ec63c7 100644 --- a/src/components/error-display/error-display.tsx +++ b/src/components/error-display/error-display.tsx @@ -1,5 +1,6 @@ import { useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { copyToClipboard } from '../../lib/utils/clipboard-utils'; import styles from './error-display.module.css'; const ErrorDisplay = ({ error }: { error: any }) => { @@ -8,25 +9,23 @@ const ErrorDisplay = ({ error }: { error: any }) => { const originalDisplayMessage = error?.message ? `${t('error')}: ${error.message}` : null; - const handleMessageClick = () => { + const handleMessageClick = async () => { if (!error || !error.message || feedbackMessageKey) return; const errorString = JSON.stringify(error, null, 2); - navigator.clipboard - .writeText(errorString) - .then(() => { - setFeedbackMessageKey('copied'); - setTimeout(() => { - setFeedbackMessageKey(null); - }, 1500); - }) - .catch((err) => { - console.error('Failed to copy error: ', err); - setFeedbackMessageKey('failed'); - setTimeout(() => { - setFeedbackMessageKey(null); - }, 1500); - }); + try { + await copyToClipboard(errorString); + setFeedbackMessageKey('copied'); + setTimeout(() => { + setFeedbackMessageKey(null); + }, 1500); + } catch (err) { + console.error('Failed to copy error: ', err); + setFeedbackMessageKey('failed'); + setTimeout(() => { + setFeedbackMessageKey(null); + }, 1500); + } }; let currentDisplayMessage = ''; diff --git a/src/components/post/comment-tools/comment-tools.tsx b/src/components/post/comment-tools/comment-tools.tsx index 8c36ff8a..9e211c74 100644 --- a/src/components/post/comment-tools/comment-tools.tsx +++ b/src/components/post/comment-tools/comment-tools.tsx @@ -68,14 +68,18 @@ const ShareButton = ({ cid, subplebbitAddress }: { cid: string; subplebbitAddres } }, [hasCopied]); + const handleCopy = async () => { + try { + setHasCopied(true); + await copyShareLinkToClipboard(subplebbitAddress, cid); + } catch (error) { + console.error('Failed to copy share link:', error); + setHasCopied(false); + } + }; + return ( -
  • { - setHasCopied(true); - copyShareLinkToClipboard(subplebbitAddress, cid); - }} - > +
  • {hasCopied ? t('link_copied') : t('share')}
  • ); diff --git a/src/components/post/expando/expando.module.css b/src/components/post/expando/expando.module.css index b1f03f45..31eba0e5 100644 --- a/src/components/post/expando/expando.module.css +++ b/src/components/post/expando/expando.module.css @@ -54,7 +54,6 @@ .mediaPreview img { /* TODO: max width should be dynamically calculated based on file dimensions from API */ max-width: 600px; - width: 100%; background-color: var(--background-thumbnail); object-fit: contain; } @@ -62,7 +61,6 @@ .mediaPreview video { /* TODO: max width should be dynamically calculated based on file dimensions from API */ max-width: 700px; - width: 100%; max-height: 50vh; background-color: var(--background-thumbnail); object-fit: contain; diff --git a/src/components/post/post.tsx b/src/components/post/post.tsx index 88307db2..efdd9833 100644 --- a/src/components/post/post.tsx +++ b/src/components/post/post.tsx @@ -9,7 +9,7 @@ import { getHasThumbnail } from '../../lib/utils/media-utils'; import { getPostScore, formatScore } from '../../lib/utils/post-utils'; import { getFormattedTimeAgo, formatLocalizedUTCTimestamp } from '../../lib/utils/time-utils'; import { getHostname } from '../../lib/utils/url-utils'; -import { isAllView, isPendingPostView, isPostPageView, isProfileHiddenView, isProfileView, isSubplebbitView } from '../../lib/utils/view-utils'; +import { isAllView, isAuthorView, isPendingPostView, isPostPageView, isProfileHiddenView, isProfileView, isSubplebbitView } from '../../lib/utils/view-utils'; import { highlightMatchedText } from '../../lib/utils/pattern-utils'; import { usePinnedPostsStore } from '../../stores/use-pinned-posts-store'; import { useCommentMediaInfo } from '../../hooks/use-comment-media-info'; @@ -142,6 +142,7 @@ const Post = ({ index, post = {} }: PostProps) => { const isInPendingPostView = isPendingPostView(location.pathname, params); const isInPostPageView = isPostPageView(location.pathname, params); const isInProfileView = isProfileView(location.pathname); + const isInAuthorView = isAuthorView(location.pathname); const isInProfileHiddenView = isProfileHiddenView(location.pathname); const isInSubplebbitView = isSubplebbitView(location.pathname, params); @@ -202,7 +203,7 @@ const Post = ({ index, post = {} }: PostProps) => {
    - {!isMobile && !isInProfileView && !isInPostPageView &&
    {pinned ? undefined : rank}
    } + {!isMobile && !isInProfileView && !isInAuthorView && !isInPostPageView &&
    {pinned ? undefined : rank}
    }
    diff --git a/src/components/post/thumbnail/thumbnail.tsx b/src/components/post/thumbnail/thumbnail.tsx index 1d51f87a..50578c09 100644 --- a/src/components/post/thumbnail/thumbnail.tsx +++ b/src/components/post/thumbnail/thumbnail.tsx @@ -65,15 +65,18 @@ const Thumbnail = ({ hasLinkDimensions = true; } - const style = hasLinkDimensions ? ({ '--width': displayWidth, '--height': displayHeight } as React.CSSProperties) : {}; - const handleNotFound = () => { setIsNotFound(true); }; let mediaComponent = null; let noMediaLinkIcon = ''; - const gifFrameUrl = useFetchGifFirstFrame(commentMediaInfo?.type === 'gif' ? commentMediaInfo.url : undefined); + const { frameUrl: gifFrameUrl, isLoading: gifFrameLoading } = useFetchGifFirstFrame(commentMediaInfo?.type === 'gif' ? commentMediaInfo.url : undefined); + + const isChromium = (() => { + const ua = navigator.userAgent; + return (ua.includes('Chrome') || ua.includes('Chromium') || ua.includes('Edge')) && !ua.includes('Firefox'); + })(); const [videoDuration, setVideoDuration] = useState(''); @@ -104,7 +107,24 @@ const Thumbnail = ({ } else if (commentMediaInfo?.type === 'iframe') { mediaComponent = iframeThumbnail ? : ; } else if (commentMediaInfo?.type === 'gif') { - mediaComponent = ; + if (gifFrameUrl) { + mediaComponent = ; + } else if (gifFrameLoading) { + displayWidth = '50px'; + displayHeight = '50px'; + hasLinkDimensions = false; + mediaComponent = ; + } else { + // Chrome has strong CORS restrictions that prevent frame extraction, so we show a placeholder + if (isChromium) { + displayWidth = '50px'; + displayHeight = '50px'; + hasLinkDimensions = true; + mediaComponent = ; + } else { + mediaComponent = ; + } + } } if (isText) { @@ -137,6 +157,8 @@ const Thumbnail = ({ noMediaLinkIcon = 'notfound'; } + const style = hasLinkDimensions ? ({ '--width': displayWidth, '--height': displayHeight } as React.CSSProperties) : {}; + return ( diff --git a/src/components/sidebar/sidebar.tsx b/src/components/sidebar/sidebar.tsx index 2011c7c8..541db4bd 100644 --- a/src/components/sidebar/sidebar.tsx +++ b/src/components/sidebar/sidebar.tsx @@ -2,7 +2,7 @@ import { useState, useEffect } from 'react'; import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import Plebbit from '@plebbit/plebbit-js'; -import { Comment, useAccount, useBlock, Role, Subplebbit, useSubplebbitStats, useAccountComment } from '@plebbit/plebbit-react-hooks'; +import { Comment, useAccount, useBlock, Role, Subplebbit, useSubplebbitStats, useAccountComment, usePlebbitRpcSettings } from '@plebbit/plebbit-react-hooks'; import styles from './sidebar.module.css'; import useIsSubplebbitOffline from '../../hooks/use-is-subplebbit-offline'; import useIsMobile from '../../hooks/use-is-mobile'; @@ -250,7 +250,7 @@ const Sidebar = ({ comment, isSubCreatedButNotYetPublished, settings, subplebbit } }, []); // Empty dependency array ensures this runs only once on mount - const isConnectedToRpc = !!account?.plebbitOptions.plebbitRpcClientsOptions; + const isConnectedToRpc = usePlebbitRpcSettings()?.state === 'connected'; const navigate = useNavigate(); const handleCreateCommunity = () => { // creating a community only works if the user is running a full node diff --git a/src/components/topbar/topbar.tsx b/src/components/topbar/topbar.tsx index a7d710f0..ec45f22f 100644 --- a/src/components/topbar/topbar.tsx +++ b/src/components/topbar/topbar.tsx @@ -5,10 +5,11 @@ import { useAccount, useAccountSubplebbits } from '@plebbit/plebbit-react-hooks' import Plebbit from '@plebbit/plebbit-js'; import { isAllView, isDomainView, isHomeView, isModView, isSubplebbitView } from '../../lib/utils/view-utils'; import useContentOptionsStore from '../../stores/use-content-options-store'; -import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits'; +import { useDefaultSubplebbitAddresses, useDefaultSubplebbits } from '../../hooks/use-default-subplebbits'; import useTimeFilter, { setSessionTimeFilterPreference } from '../../hooks/use-time-filter'; import { sortTypes } from '../../constants/sort-types'; import { sortLabels } from '../../constants/sort-labels'; +import { handleNSFWSubscriptionPrompt } from '../../lib/utils/nsfw-subscription-utils'; import styles from './topbar.module.css'; const CommunitiesDropdown = () => { @@ -54,6 +55,8 @@ const CommunitiesDropdown = () => { const TagFilterDropdown = () => { const { t } = useTranslation(); + const account = useAccount(); + const defaultSubplebbits = useDefaultSubplebbits(); const { hideAdultCommunities, hideGoreCommunities, @@ -80,18 +83,38 @@ const TagFilterDropdown = () => { const allHidden = hideAdultCommunities && hideGoreCommunities && hideAntiCommunities && hideVulgarCommunities; - const handleToggleAll = (event: React.MouseEvent) => { + const handleToggleAll = async (event: React.MouseEvent) => { event.stopPropagation(); const newState = !allHidden; + + if (!newState) { + await handleNSFWSubscriptionPrompt({ + account, + defaultSubplebbits, + tagsToShow: ['adult', 'gore', 'anti', 'vulgar'], + isShowingAll: true, + }); + } + setHideAdultCommunities(newState); setHideGoreCommunities(newState); setHideAntiCommunities(newState); setHideVulgarCommunities(newState); }; - const handleToggleTag = (event: React.MouseEvent, setter: (hide: boolean) => void, currentState: boolean) => { + const handleToggleTag = async (event: React.MouseEvent, setter: (hide: boolean) => void, currentState: boolean, tagName: string) => { event.stopPropagation(); - setter(!currentState); + const newState = !currentState; + + if (!newState) { + await handleNSFWSubscriptionPrompt({ + account, + defaultSubplebbits, + tagsToShow: [tagName], + }); + } + + setter(newState); }; useEffect(() => { @@ -112,12 +135,12 @@ const TagFilterDropdown = () => { {t('tags')}
    - {allHidden ? 'show all nsfw' : 'hide all nsfw'} + {allHidden ? t('show_all_nsfw') : t('hide_all_nsfw')}
    {tags.map((tag, index) => ( -
    handleToggleTag(e, tag.setter, tag.isHidden)} style={{ cursor: 'pointer' }}> +
    handleToggleTag(e, tag.setter, tag.isHidden, tag.name)} style={{ cursor: 'pointer' }}> - {tag.isHidden ? 'show' : 'hide'} {tag.name} + {tag.isHidden ? t('show') : t('hide')} {tag.name}
    ))} diff --git a/src/globals.d.ts b/src/globals.d.ts index 721d3c77..d0d7a7f5 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -1,6 +1,5 @@ declare global { interface Window { - STICKY_MENU_SCROLL_LISTENER: boolean; isElectron: boolean; } } diff --git a/src/hooks/use-default-subplebbits.ts b/src/hooks/use-default-subplebbits.ts index 0905b99b..0d9b1702 100644 --- a/src/hooks/use-default-subplebbits.ts +++ b/src/hooks/use-default-subplebbits.ts @@ -15,44 +15,91 @@ export interface MultisubSubplebbit { tags?: string[]; features?: string[]; seeditAutoSubscribe?: boolean; + plebchanAutoSubscribe?: boolean; lowUptime?: boolean; } let cacheSubplebbits: MultisubSubplebbit[] | null = null; let cacheMetadata: MultisubMetadata | null = null; let cacheAutoSubscribeAddresses: string[] | null = null; +let pending = false; + +// Subscriber pattern for notifying all hook instances +const subscribers = new Set<() => void>(); + +const notifySubscribers = () => { + subscribers.forEach((callback) => callback()); +}; + +// Shared fetch function to avoid duplication +const fetchMultisubData = async () => { + if (pending) { + return; + } + pending = true; + + try { + const multisub = await fetch( + 'https://raw.githubusercontent.com/plebbit/temporary-default-subplebbits/master/multisub.json', + // { cache: 'no-cache' } + ).then((res) => res.json()); + + const filteredSubplebbits = multisub.subplebbits.filter((sub: MultisubSubplebbit) => !sub.lowUptime); + + cacheSubplebbits = filteredSubplebbits; + + // Cache auto-subscribe addresses when we fetch subplebbits + cacheAutoSubscribeAddresses = filteredSubplebbits + .filter((sub: MultisubSubplebbit) => sub.seeditAutoSubscribe && sub.address) + .map((sub: MultisubSubplebbit) => sub.address); + + // Also cache metadata since we have the full response + const { title, description, createdAt, updatedAt } = multisub; + cacheMetadata = { title, description, createdAt, updatedAt }; + + // Notify all subscribers that cache has been updated + notifySubscribers(); + + return { subplebbits: filteredSubplebbits, metadata: cacheMetadata }; + } catch (e) { + console.warn(e); + return null; + } finally { + pending = false; + } +}; export const useDefaultSubplebbits = () => { - const [subplebbits, setSubplebbits] = useState([]); + const [subplebbits, setSubplebbits] = useState(cacheSubplebbits || []); useEffect(() => { + // If we already have cached data, use it immediately if (cacheSubplebbits) { + setSubplebbits(cacheSubplebbits); return; } - (async () => { - try { - const multisub = await fetch( - 'https://raw.githubusercontent.com/plebbit/temporary-default-subplebbits/master/multisub.json', - // { cache: 'no-cache' } - ).then((res) => res.json()); - const filteredSubplebbits = multisub.subplebbits.filter((sub: MultisubSubplebbit) => !sub.lowUptime); - - cacheSubplebbits = filteredSubplebbits; - - // Cache auto-subscribe addresses when we fetch subplebbits - cacheAutoSubscribeAddresses = filteredSubplebbits - .filter((sub: MultisubSubplebbit) => sub.seeditAutoSubscribe && sub.address) - .map((sub: MultisubSubplebbit) => sub.address); - - setSubplebbits(filteredSubplebbits); - } catch (e) { - console.warn(e); + // Subscribe to cache updates + const handleCacheUpdate = () => { + if (cacheSubplebbits) { + setSubplebbits(cacheSubplebbits); } - })(); + }; + + subscribers.add(handleCacheUpdate); + + // Trigger fetch if no cache and not pending + if (!pending) { + fetchMultisubData(); + } + + // Cleanup subscription + return () => { + subscribers.delete(handleCacheUpdate); + }; }, []); - return cacheSubplebbits || subplebbits; + return subplebbits; }; export const getAutoSubscribeAddresses = () => cacheAutoSubscribeAddresses || []; @@ -76,29 +123,36 @@ export const useDefaultSubplebbitAddresses = () => { }; export const useMultisubMetadata = () => { - const [metadata, setMetadata] = useState(null); + const [metadata, setMetadata] = useState(cacheMetadata || null); useEffect(() => { + // If we already have cached data, use it immediately if (cacheMetadata) { + setMetadata(cacheMetadata); return; } - (async () => { - try { - const multisub = await fetch( - 'https://raw.githubusercontent.com/plebbit/temporary-default-subplebbits/master/multisub.json', - // { cache: 'no-cache' } - ).then((res) => res.json()); - const { title, description, createdAt, updatedAt } = multisub; - const metadata: MultisubMetadata = { title, description, createdAt, updatedAt }; - cacheMetadata = metadata; - setMetadata(metadata); - } catch (e) { - console.warn(e); + + // Subscribe to cache updates + const handleCacheUpdate = () => { + if (cacheMetadata) { + setMetadata(cacheMetadata); } - })(); + }; + + subscribers.add(handleCacheUpdate); + + // Trigger fetch if no cache and not pending + if (!pending) { + fetchMultisubData(); + } + + // Cleanup subscription + return () => { + subscribers.delete(handleCacheUpdate); + }; }, []); - return cacheMetadata || metadata; + return metadata; }; const getUniqueTags = (multisub: any) => { diff --git a/src/hooks/use-fetch-gif-first-frame.ts b/src/hooks/use-fetch-gif-first-frame.ts index 08cb91c8..1c1a5a74 100644 --- a/src/hooks/use-fetch-gif-first-frame.ts +++ b/src/hooks/use-fetch-gif-first-frame.ts @@ -1,8 +1,14 @@ import { useEffect, useState } from 'react'; +import { decompressFrames, parseGIF } from 'gifuct-js'; import localForageLru from '@plebbit/plebbit-react-hooks/dist/lib/localforage-lru/index.js'; const gifFrameDb = localForageLru.createInstance({ name: 'seeditGifFrames', size: 500 }); +const isChromium = () => { + const ua = navigator.userAgent; + return (ua.includes('Chrome') || ua.includes('Chromium') || ua.includes('Edge')) && !ua.includes('Firefox'); +}; + const getCachedGifFrame = async (url: string): Promise => { return await gifFrameDb.getItem(url); }; @@ -16,17 +22,76 @@ export const fetchImage = (url: string): Promise => { const request = new XMLHttpRequest(); request.open('GET', url, true); request.responseType = 'arraybuffer'; + + // Add error logging to understand what's happening request.onloadend = () => { + console.log('XMLHttpRequest completed:', { + url, + status: request.status, + statusText: request.statusText, + responseType: request.responseType, + response: request.response, + readyState: request.readyState, + }); + if (request.response !== undefined && (request.status === 200 || request.status === 304)) { resolve(request.response); } else { - reject(new Error(`XMLHttpRequest, ${request.statusText}`)); + reject(new Error(`XMLHttpRequest failed: ${request.status} ${request.statusText} for URL: ${url}`)); } }; - request.send(); + + request.onerror = () => { + console.error('XMLHttpRequest error:', { + url, + status: request.status, + statusText: request.statusText, + readyState: request.readyState, + }); + reject(new Error(`XMLHttpRequest network error for URL: ${url}`)); + }; + + request.ontimeout = () => { + console.error('XMLHttpRequest timeout:', { url }); + reject(new Error(`XMLHttpRequest timeout for URL: ${url}`)); + }; + + // Set timeout to prevent hanging + request.timeout = 30000; // 30 seconds + + // Try to handle CORS + try { + request.send(); + } catch (error) { + console.error('XMLHttpRequest send error:', error); + reject(new Error(`XMLHttpRequest send failed: ${error}`)); + } }); }; +// Fallback fetch method for when XMLHttpRequest fails +export const fetchImageWithFetch = async (url: string): Promise => { + try { + console.log('Trying fetch method for:', url); + const response = await fetch(url, { + method: 'GET', + mode: 'cors', + cache: 'default', + }); + + if (!response.ok) { + throw new Error(`Fetch failed: ${response.status} ${response.statusText}`); + } + + const arrayBuffer = await response.arrayBuffer(); + console.log('Fetch successful for:', url); + return arrayBuffer; + } catch (error) { + console.error('Fetch error:', error); + throw error; + } +}; + export const readImage = (file: File): Promise => { return new Promise((resolve) => { const reader = new FileReader(); @@ -37,62 +102,252 @@ export const readImage = (file: File): Promise => { }); }; -const parseGif = async (buf: ArrayBuffer): Promise => { - const image = new Image(); - await new Promise((resolve) => { - image.src = URL.createObjectURL(new Blob([buf])); - image.onload = resolve; - }); - const canvas = document.createElement('canvas'); - canvas.width = image.width; - canvas.height = image.height; - const ctx = canvas.getContext('2d'); - if (ctx === null) throw new Error('Canvas Context null'); - ctx.drawImage(image, 0, 0, image.width, image.height); - return await new Promise((resolve, reject) => - canvas.toBlob((blob) => { - if (blob === null) { - reject('Canvas Blob null'); - } else { - resolve(blob); +const extractFirstFrame = async (buffer: ArrayBuffer): Promise => { + try { + // Parse the GIF using gifuct-js + const gif = parseGIF(buffer); + const frames = decompressFrames(gif, true); + + if (frames.length === 0) { + throw new Error('No frames found in GIF'); + } + + // Get the first frame + const firstFrame = frames[0]; + const { width, height } = firstFrame.dims; + + // Create canvas and draw the frame + const canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + + const ctx = canvas.getContext('2d'); + if (!ctx) { + throw new Error('Cannot get canvas context'); + } + + // Create ImageData from the frame data + const imageData = new ImageData(new Uint8ClampedArray(firstFrame.patch), width, height); + + // Clear canvas and put the image data + ctx.clearRect(0, 0, width, height); + ctx.putImageData(imageData, 0, 0); + + // Convert to blob + return new Promise((resolve, reject) => { + canvas.toBlob( + (blob) => { + if (blob) { + resolve(blob); + } else { + reject(new Error('Failed to create blob from canvas')); + } + }, + 'image/png', + 1.0, + ); + }); + } catch (error) { + throw new Error(`Failed to extract first frame: ${error}`); + } +}; + +// CORS-bypass method using image element +export const fetchImageViaCORSProxy = async (url: string): Promise => { + return new Promise((resolve, reject) => { + const img = new Image(); + img.crossOrigin = 'anonymous'; + + img.onload = () => { + try { + // Create a canvas to get the image data + const canvas = document.createElement('canvas'); + canvas.width = img.naturalWidth; + canvas.height = img.naturalHeight; + + const ctx = canvas.getContext('2d'); + if (!ctx) { + reject(new Error('Cannot get canvas context')); + return; + } + + ctx.drawImage(img, 0, 0); + + // Convert canvas to blob and then to ArrayBuffer + canvas.toBlob( + async (blob) => { + if (blob) { + try { + const arrayBuffer = await blob.arrayBuffer(); + resolve(arrayBuffer); + } catch (error) { + reject(new Error(`Failed to convert blob to ArrayBuffer: ${error}`)); + } + } else { + reject(new Error('Failed to create blob from canvas')); + } + }, + 'image/gif', + 1.0, + ); + } catch (error) { + reject(new Error(`Canvas operation failed: ${error}`)); } - }), - ); + }; + + img.onerror = () => { + reject(new Error(`Image load failed for URL: ${url}`)); + }; + + // Try different approaches for CORS + try { + img.src = url; + } catch (error) { + reject(new Error(`Failed to set image src: ${error}`)); + } + }); +}; + +// Simple proxy approach - just use the image directly without trying to extract frames +export const createStaticImageFromGif = async (url: string): Promise => { + return new Promise((resolve, reject) => { + const img = new Image(); + + img.onload = () => { + try { + const canvas = document.createElement('canvas'); + canvas.width = img.naturalWidth; + canvas.height = img.naturalHeight; + + const ctx = canvas.getContext('2d'); + if (!ctx) { + reject(new Error('Cannot get canvas context')); + return; + } + + // Clear and draw the image (this should get the first frame in most cases) + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(img, 0, 0); + + canvas.toBlob( + (blob) => { + if (blob) { + const objectUrl = URL.createObjectURL(blob); + resolve(objectUrl); + } else { + reject(new Error('Failed to create blob from canvas')); + } + }, + 'image/png', + 1.0, + ); + } catch (error) { + reject(new Error(`Canvas operation failed: ${error}`)); + } + }; + + img.onerror = () => { + reject(new Error(`Image load failed for URL: ${url}`)); + }; + + // Don't set crossOrigin for this approach to avoid CORS issues + img.src = url; + }); }; const useFetchGifFirstFrame = (url: string | undefined) => { const [frameUrl, setFrameUrl] = useState(null); + const [isLoading, setIsLoading] = useState(false); useEffect(() => { if (!url) { setFrameUrl(null); + setIsLoading(false); + return; + } + + // Skip frame extraction on Chromium browsers due to strict CORS policies + if (isChromium()) { + console.log('Skipping GIF frame extraction on Chromium browser due to CORS restrictions'); + setFrameUrl(null); + setIsLoading(false); return; } let isActive = true; + setIsLoading(true); + setFrameUrl(null); const fetchFrame = async () => { try { + // Check cache first const cachedFrame = await getCachedGifFrame(url); - if (cachedFrame) { + if (cachedFrame && isActive) { try { const response = await fetch(cachedFrame); if (response.ok) { - if (isActive) setFrameUrl(cachedFrame); + setFrameUrl(cachedFrame); + setIsLoading(false); return; } - } catch {} + } catch { + // Cache entry is invalid, continue with fresh extraction + } } - const blob = typeof url === 'string' ? await parseGif(await fetchImage(url)) : await parseGif(await readImage(url as File)); + // For Chrome CORS compatibility, try the simple image approach first + if (typeof url === 'string') { + try { + console.log('Trying simple image approach first...'); + const objectUrl = await createStaticImageFromGif(url); + + if (isActive) { + setFrameUrl(objectUrl); + setIsLoading(false); + await setCachedGifFrame(url, objectUrl); + } + return; // Success with simple approach + } catch (simpleError) { + console.log('Simple image approach failed, trying advanced extraction:', simpleError); + } + } + + // Fallback to advanced frame extraction (gifuct-js) + const buffer = typeof url === 'string' ? await fetchImage(url) : await readImage(url as File); + const blob = await extractFirstFrame(buffer); const objectUrl = URL.createObjectURL(blob); + if (isActive) { setFrameUrl(objectUrl); + setIsLoading(false); await setCachedGifFrame(url, objectUrl); } } catch (error) { console.error('Failed to load GIF frame:', error); - if (isActive) setFrameUrl(null); + + // Final fallback attempts (keep the existing fallback logic) + if (typeof url === 'string' && error instanceof Error && error.message.includes('XMLHttpRequest')) { + try { + console.log('Trying fallback fetch method...'); + const buffer = await fetchImageWithFetch(url); + const blob = await extractFirstFrame(buffer); + const objectUrl = URL.createObjectURL(blob); + + if (isActive) { + setFrameUrl(objectUrl); + setIsLoading(false); + await setCachedGifFrame(url, objectUrl); + } + return; // Success with fallback + } catch (fallbackError) { + console.error('All methods failed:', fallbackError); + } + } + + if (isActive) { + setFrameUrl(null); + setIsLoading(false); + } } }; @@ -103,7 +358,7 @@ const useFetchGifFirstFrame = (url: string | undefined) => { }; }, [url]); - return frameUrl; + return { frameUrl, isLoading }; }; export default useFetchGifFirstFrame; diff --git a/src/lib/utils/clipboard-utils.ts b/src/lib/utils/clipboard-utils.ts new file mode 100644 index 00000000..94a46dda --- /dev/null +++ b/src/lib/utils/clipboard-utils.ts @@ -0,0 +1,30 @@ +/** + * Universal clipboard utility that works in both Electron and web environments + */ +export const copyToClipboard = async (text: string): Promise => { + // Check if we're in Electron and use its clipboard API + if (typeof window !== 'undefined' && (window as any).electronApi?.copyToClipboard) { + try { + const result = await (window as any).electronApi.copyToClipboard(text); + if (!result.success) { + throw new Error(result.error || 'Failed to copy to clipboard'); + } + return; + } catch (error) { + console.error('Electron clipboard failed:', error); + // Fall back to web clipboard API + } + } + + // Fallback to web clipboard API + if (navigator.clipboard) { + try { + await navigator.clipboard.writeText(text); + } catch (error) { + console.error('Web clipboard failed:', error); + throw new Error('Failed to copy to clipboard. Your browser may not support this feature.'); + } + } else { + throw new Error('Your browser does not support clipboard API'); + } +}; diff --git a/src/lib/utils/nsfw-subscription-utils.ts b/src/lib/utils/nsfw-subscription-utils.ts new file mode 100644 index 00000000..9d1ab533 --- /dev/null +++ b/src/lib/utils/nsfw-subscription-utils.ts @@ -0,0 +1,77 @@ +import { setAccount } from '@plebbit/plebbit-react-hooks'; + +const NSFW_SUBSCRIPTION_PROMPT_KEY_PREFIX = 'seedit-nsfw-subscription-prompt-shown-'; + +export interface NSFWSubscriptionOptions { + account: any; + defaultSubplebbits: any[]; + tagsToShow: string[]; + isShowingAll?: boolean; +} + +const getPromptKey = (tagsToShow: string[], isShowingAll: boolean): string => { + if (isShowingAll) { + return `${NSFW_SUBSCRIPTION_PROMPT_KEY_PREFIX}all`; + } + // Sort tags to ensure consistent key regardless of order + const sortedTags = [...tagsToShow].sort(); + return `${NSFW_SUBSCRIPTION_PROMPT_KEY_PREFIX}${sortedTags.join('-')}`; +}; + +export const shouldShowNSFWSubscriptionPrompt = (tagsToShow: string[], isShowingAll: boolean = false): boolean => { + const key = getPromptKey(tagsToShow, isShowingAll); + return !localStorage.getItem(key); +}; + +export const markNSFWSubscriptionPromptShown = (tagsToShow: string[], isShowingAll: boolean = false): void => { + const key = getPromptKey(tagsToShow, isShowingAll); + localStorage.setItem(key, 'true'); +}; + +export const getCommunitiesWithTags = (defaultSubplebbits: any[], tags: string[]): string[] => { + return defaultSubplebbits + .filter((sub) => { + const subTags = sub.tags || []; + return tags.some((tag) => subTags.includes(tag)); + }) + .map((sub) => sub.address); +}; + +export const handleNSFWSubscriptionPrompt = async ({ account, defaultSubplebbits, tagsToShow, isShowingAll = false }: NSFWSubscriptionOptions): Promise => { + if (!shouldShowNSFWSubscriptionPrompt(tagsToShow, isShowingAll) || !account || !defaultSubplebbits.length) { + return; + } + + const communitiesToSubscribe = getCommunitiesWithTags(defaultSubplebbits, tagsToShow); + + if (communitiesToSubscribe.length === 0) { + return; + } + + const tagText = isShowingAll + ? 'all NSFW communities' + : tagsToShow.length === 1 + ? `communities tagged as "${tagsToShow[0]}"` + : `communities tagged as: ${tagsToShow.join(', ')}`; + + const confirmMessage = `You're now showing ${tagText}. Would you like to subscribe to these communities? This will add them to your subscriptions.`; + + const shouldSubscribe = window.confirm(confirmMessage); + + // Mark as shown regardless of user choice + markNSFWSubscriptionPromptShown(tagsToShow, isShowingAll); + + if (shouldSubscribe) { + try { + const currentSubscriptions = account.subscriptions || []; + const newSubscriptions = [...new Set([...currentSubscriptions, ...communitiesToSubscribe])]; + + await setAccount({ + ...account, + subscriptions: newSubscriptions, + }); + } catch (error) { + console.error('Error subscribing to NSFW communities:', error); + } + } +}; diff --git a/src/lib/utils/url-utils.ts b/src/lib/utils/url-utils.ts index aacfae92..85cc1ffb 100644 --- a/src/lib/utils/url-utils.ts +++ b/src/lib/utils/url-utils.ts @@ -1,3 +1,5 @@ +import { copyToClipboard } from './clipboard-utils'; + export const getHostname = (url: string) => { try { return new URL(url).hostname.replace(/^www\./, ''); @@ -15,12 +17,7 @@ export const isValidURL = (url: string) => { } }; -export const copyShareLinkToClipboard = (subplebbitAddress: string, cid: string) => { +export const copyShareLinkToClipboard = async (subplebbitAddress: string, cid: string) => { const shareLink = `https://pleb.bz/p/${subplebbitAddress}/c/${cid}`; - - if (navigator.clipboard) { - navigator.clipboard.writeText(shareLink); - } else { - alert('Your browser does not support clipboard API'); - } + await copyToClipboard(shareLink); }; diff --git a/src/views/author/author.tsx b/src/views/author/author.tsx index 767fd884..8c660742 100644 --- a/src/views/author/author.tsx +++ b/src/views/author/author.tsx @@ -89,10 +89,12 @@ const Author = () => { // only show backend errors if the user gets no data const [shouldShowErrorToUser, setShouldShowErrorToUser] = useState(false); useEffect(() => { - if (error?.message && virtuosoData?.length === 0 && hasMore) { + if (error?.message && virtuosoData?.length === 0) { setShouldShowErrorToUser(true); + } else if (virtuosoData?.length > 0) { + setShouldShowErrorToUser(false); } - }, [error, virtuosoData, hasMore]); + }, [error, virtuosoData]); return (
    diff --git a/src/views/inbox/inbox.tsx b/src/views/inbox/inbox.tsx index c645e272..e9ad86fb 100644 --- a/src/views/inbox/inbox.tsx +++ b/src/views/inbox/inbox.tsx @@ -102,6 +102,8 @@ const Inbox = () => { useEffect(() => { if (error?.message && comments.length === 0) { setShouldShowErrorToUser(true); + } else if (comments.length > 0) { + setShouldShowErrorToUser(false); } }, [error, comments]); diff --git a/src/views/post-page/post-page.tsx b/src/views/post-page/post-page.tsx index cf370da0..e2a2fe1c 100644 --- a/src/views/post-page/post-page.tsx +++ b/src/views/post-page/post-page.tsx @@ -302,6 +302,8 @@ const PostPage = () => { useEffect(() => { if (post?.error && ((post?.replyCount > 0 && post?.replies?.length === 0) || (post?.state === 'failed' && post?.error))) { setShouldShowErrorToUser(true); + } else if (post?.replyCount > 0 && post?.replies?.length > 0) { + setShouldShowErrorToUser(false); } }, [post]); diff --git a/src/views/profile/profile.tsx b/src/views/profile/profile.tsx index fd3bd704..4f6b8058 100644 --- a/src/views/profile/profile.tsx +++ b/src/views/profile/profile.tsx @@ -147,6 +147,8 @@ const Overview = () => { useEffect(() => { if (error?.message && sortedComments.length === 0) { setShouldShowErrorToUserOverview(true); + } else if (sortedComments.length > 0) { + setShouldShowErrorToUserOverview(false); } }, [error, sortedComments]); @@ -186,6 +188,8 @@ const Comments = () => { useEffect(() => { if (error?.message && sortedComments.length === 0) { setShouldShowErrorToUserComments(true); + } else if (sortedComments.length > 0) { + setShouldShowErrorToUserComments(false); } }, [error, sortedComments]); @@ -225,6 +229,8 @@ const Submitted = () => { useEffect(() => { if (error?.message && sortedComments.length === 0) { setShouldShowErrorToUserSubmitted(true); + } else if (sortedComments.length > 0) { + setShouldShowErrorToUserSubmitted(false); } }, [error, sortedComments]); @@ -268,6 +274,8 @@ const VotedComments = ({ voteType }: { voteType: 1 | -1 }) => { useEffect(() => { if (error?.message && paginatedCids.length === 0 && hasMore) { setShouldShowErrorToUserVoted(true); + } else if (paginatedCids.length > 0) { + setShouldShowErrorToUserVoted(false); } }, [error, paginatedCids, hasMore]); diff --git a/src/views/settings/account-data-editor/account-data-editor.tsx b/src/views/settings/account-data-editor/account-data-editor.tsx index bc3f365e..4e76af8e 100644 --- a/src/views/settings/account-data-editor/account-data-editor.tsx +++ b/src/views/settings/account-data-editor/account-data-editor.tsx @@ -6,6 +6,7 @@ import useTheme from '../../../stores/use-theme-store'; import stringify from 'json-stringify-pretty-compact'; import styles from './account-data-editor.module.css'; import useIsMobile from '../../../hooks/use-is-mobile'; +import LoadingEllipsis from '../../../components/loading-ellipsis'; const LazyAceEditor = lazy(async () => { const ReactAceModule = await import('react-ace'); @@ -57,11 +58,11 @@ const AccountDataEditor = () => {
    security warning
    -

    Your private key will be displayed

    -

    You're about to view your account data, which includes your private key. You should never share your private key with anyone.

    +

    {t('private_key_warning_title')}

    +

    {t('private_key_warning_description')}

    - +
    @@ -70,7 +71,13 @@ const AccountDataEditor = () => { return (
    - Loading editor...
    }> + + +
    + } + > { const { t } = useTranslation(); @@ -25,13 +28,13 @@ const MediaOptions = () => {
    @@ -50,11 +53,11 @@ const MediaOptions = () => { onChange={() => setThumbnailDisplayOption('community')} disabled /> - Show thumbnails based on that community's media preferences + {t('show_thumbnails_based_on_community_media_preferences')}

    -
    media previews
    +
    {t('media_previews')}
    @@ -76,7 +79,7 @@ const MediaOptions = () => { checked={mediaPreviewOption === 'autoExpandExceptComments'} onChange={() => setMediaPreviewOption('autoExpandExceptComments')} /> - Don't auto-expand media previews on comments pages + {t('dont_auto_expand_media_previews_on_comments_pages')}
    @@ -95,21 +98,21 @@ const MediaOptions = () => { onChange={() => setMediaPreviewOption('community')} disabled /> - Expand media previews based on that community's media preferences + {t('expand_media_previews_based_on_community_media_preferences')}

    -
    Video Player
    +
    {t('video_player')}

    @@ -134,6 +137,8 @@ const MediaOptions = () => { const CommunitiesOptions = () => { const { t } = useTranslation(); + const account = useAccount(); + const defaultSubplebbits = useDefaultSubplebbits(); const { hideAdultCommunities, hideGoreCommunities, @@ -163,8 +168,19 @@ const CommunitiesOptions = () => { el.indeterminate = someHidden && !allHidden; } }} - onChange={(e) => { + onChange={async (e) => { const newValue = e.target.checked; + + // If showing (newValue = false), handle subscription prompt + if (!newValue) { + await handleNSFWSubscriptionPrompt({ + account, + defaultSubplebbits, + tagsToShow: ['adult', 'gore', 'anti', 'vulgar'], + isShowingAll: true, + }); + } + setHideAdultCommunities(newValue); setHideGoreCommunities(newValue); setHideAntiCommunities(newValue); @@ -178,8 +194,19 @@ const CommunitiesOptions = () => { { - setHideAdultCommunities(e.target.checked); + onChange={async (e) => { + const newValue = e.target.checked; + + // If showing (newValue = false), handle subscription prompt + if (!newValue) { + await handleNSFWSubscriptionPrompt({ + account, + defaultSubplebbits, + tagsToShow: ['adult'], + }); + } + + setHideAdultCommunities(newValue); }} /> {t('tagged_as_adult')} @@ -190,8 +217,19 @@ const CommunitiesOptions = () => { { - setHideGoreCommunities(e.target.checked); + onChange={async (e) => { + const newValue = e.target.checked; + + // If showing (newValue = false), handle subscription prompt + if (!newValue) { + await handleNSFWSubscriptionPrompt({ + account, + defaultSubplebbits, + tagsToShow: ['gore'], + }); + } + + setHideGoreCommunities(newValue); }} /> {t('tagged_as_gore')} @@ -202,8 +240,19 @@ const CommunitiesOptions = () => { { - setHideAntiCommunities(e.target.checked); + onChange={async (e) => { + const newValue = e.target.checked; + + // If showing (newValue = false), handle subscription prompt + if (!newValue) { + await handleNSFWSubscriptionPrompt({ + account, + defaultSubplebbits, + tagsToShow: ['anti'], + }); + } + + setHideAntiCommunities(newValue); }} /> {t('tagged_as_anti')} @@ -214,8 +263,19 @@ const CommunitiesOptions = () => { { - setHideVulgarCommunities(e.target.checked); + onChange={async (e) => { + const newValue = e.target.checked; + + // If showing (newValue = false), handle subscription prompt + if (!newValue) { + await handleNSFWSubscriptionPrompt({ + account, + defaultSubplebbits, + tagsToShow: ['vulgar'], + }); + } + + setHideVulgarCommunities(newValue); }} /> {t('tagged_as_vulgar')} diff --git a/src/views/settings/notifications-settings/notifications-settings.tsx b/src/views/settings/notifications-settings/notifications-settings.tsx index f6f28a39..ba8abc35 100644 --- a/src/views/settings/notifications-settings/notifications-settings.tsx +++ b/src/views/settings/notifications-settings/notifications-settings.tsx @@ -1,9 +1,11 @@ import { useCallback, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { requestNotificationPermission, showLocalNotification } from '../../../lib/push'; -import styles from './notifications-settings.module.css'; import useContentOptionsStore from '../../../stores/use-content-options-store'; +import styles from './notifications-settings.module.css'; const NotificationsSettings = () => { + const { t } = useTranslation(); const { enableLocalNotifications, setEnableLocalNotifications } = useContentOptionsStore(); const [permissionStatus, setPermissionStatus] = useState(null); const [platform, setPlatform] = useState(null); @@ -150,7 +152,7 @@ const NotificationsSettings = () => { onChange={handleCheckboxChange} disabled={isLoading || permissionStatus === 'requesting...' || permissionStatus === 'not-supported'} /> - + {/* Not supported message */} {permissionStatus === 'not-supported' && ( diff --git a/src/views/settings/plebbit-options/plebbit-options.tsx b/src/views/settings/plebbit-options/plebbit-options.tsx index ebfc2aa8..6b7737a6 100644 --- a/src/views/settings/plebbit-options/plebbit-options.tsx +++ b/src/views/settings/plebbit-options/plebbit-options.tsx @@ -21,8 +21,7 @@ const IPFSGatewaysSettings = ({ ipfsGatewayUrlsRef, mediaIpfsGatewayUrlRef }: Se const account = useAccount(); const { plebbitOptions, mediaIpfsGatewayUrl } = account || {}; const { ipfsGatewayUrls } = plebbitOptions || {}; - const plebbitRpc = usePlebbitRpcSettings(); - const isConnectedToRpc = plebbitRpc?.state === 'succeeded'; + const isConnectedToRpc = usePlebbitRpcSettings()?.state === 'connected'; const ipfsGatewayUrlsDefaultValue = ipfsGatewayUrls?.join('\n'); return ( @@ -51,7 +50,7 @@ const PubsubProvidersSettings = ({ pubsubProvidersRef }: SettingsProps) => { const { plebbitOptions } = account || {}; const { pubsubHttpClientsOptions } = plebbitOptions || {}; const plebbitRpc = usePlebbitRpcSettings(); - const isConnectedToRpc = plebbitRpc?.state === 'succeeded'; + const isConnectedToRpc = plebbitRpc?.state === 'connected'; const pubsubProvidersDefaultValue = pubsubHttpClientsOptions?.join('\n'); return ( @@ -167,7 +166,7 @@ const PlebbitRPCSettings = ({ plebbitRpcRef }: SettingsProps) => { const PlebbitDataPathSettings = ({ plebbitDataPathRef }: SettingsProps) => { const plebbitRpc = usePlebbitRpcSettings(); const { plebbitRpcSettings } = plebbitRpc || {}; - const isConnectedToRpc = plebbitRpc?.state === 'succeeded'; + const isConnectedToRpc = plebbitRpc?.state === 'connected'; const path = plebbitRpcSettings?.plebbitOptions?.dataPath || ''; return ( diff --git a/src/views/subplebbit-settings/subplebbit-settings.tsx b/src/views/subplebbit-settings/subplebbit-settings.tsx index 9c22137f..4d2df8fe 100644 --- a/src/views/subplebbit-settings/subplebbit-settings.tsx +++ b/src/views/subplebbit-settings/subplebbit-settings.tsx @@ -368,7 +368,11 @@ const SubplebbitSettings = () => { const params = useParams(); const isInCreateSubplebbitView = isCreateSubplebbitView(location.pathname); const isInSubplebbitSettingsView = isSubplebbitSettingsView(location.pathname, params); - const isConnectedToRpc = !!account?.plebbitOptions.plebbitRpcClientsOptions; + const isConnectedToRpc = usePlebbitRpcSettings()?.state === 'connected'; + + if (isInCreateSubplebbitView && !isConnectedToRpc) { + navigate('/', { replace: true }); + } const isReadOnly = (!settings && isInSubplebbitSettingsView) || (!isConnectedToRpc && isInCreateSubplebbitView); diff --git a/src/views/subplebbit/subplebbit.tsx b/src/views/subplebbit/subplebbit.tsx index ada59f5d..bb832797 100644 --- a/src/views/subplebbit/subplebbit.tsx +++ b/src/views/subplebbit/subplebbit.tsx @@ -369,10 +369,12 @@ const Subplebbit = () => { // probably not necessary to show the error to the user if the feed loaded successfully const [shouldShowErrorToUser, setShouldShowErrorToUser] = useState(false); useEffect(() => { - if (error?.message && feed.length === 0 && hasMore) { + if (error?.message && feed.length === 0) { setShouldShowErrorToUser(true); + } else if (feed.length > 0) { + setShouldShowErrorToUser(false); } - }, [error, feed, hasMore]); + }, [error, feed]); return isBroadlyNsfwSubplebbit && !hasUnhiddenAnyNsfwCommunity ? ( diff --git a/src/views/subplebbits/subplebbits.tsx b/src/views/subplebbits/subplebbits.tsx index 35968681..f92113de 100644 --- a/src/views/subplebbits/subplebbits.tsx +++ b/src/views/subplebbits/subplebbits.tsx @@ -15,7 +15,7 @@ import { isSubplebbitsVoteRejectingView, } from '../../lib/utils/view-utils'; import useErrorStore from '../../stores/use-error-store'; -import { useDefaultSubplebbitAddresses, useDefaultSubplebbitTags, useDefaultSubplebbits } from '../../hooks/use-default-subplebbits'; +import { useDefaultSubplebbitAddresses, useDefaultSubplebbits } from '../../hooks/use-default-subplebbits'; import useDisplayedSubscriptions from '../../hooks/use-displayed-subscriptions'; import useIsMobile from '../../hooks/use-is-mobile'; import useIsSubplebbitOffline from '../../hooks/use-is-subplebbit-offline'; @@ -127,20 +127,18 @@ const Infobar = () => { const subscriptions = account?.subscriptions || []; const { t } = useTranslation(); const location = useLocation(); - const defaultSubplebbits = useDefaultSubplebbits(); - const validTags = useDefaultSubplebbitTags(defaultSubplebbits); const isInSubplebbitsSubscriberView = isSubplebbitsSubscriberView(location.pathname); const isInSubplebbitsModeratorView = isSubplebbitsModeratorView(location.pathname); const isInSubplebbitsAdminView = isSubplebbitsAdminView(location.pathname); const isInSubplebbitsOwnerView = isSubplebbitsOwnerView(location.pathname); - // Add tag check - const urlTag = location.pathname.includes('/tag/') ? location.pathname.split('/').pop() : undefined; - const currentTag = urlTag && validTags.includes(urlTag) ? urlTag : undefined; + // Check if we're filtering by any tag + const urlParams = new URLSearchParams(location.search); + const currentTag = urlParams.get('tag'); - // Get base path without tag - const basePath = location.pathname.split('/tag/')[0]; + // Get base path without search params + const basePath = location.pathname; let mainInfobarText; if (isInSubplebbitsSubscriberView) { @@ -162,7 +160,7 @@ const Infobar = () => {
    {currentTag && (
    - {t('filtering_by_tag', { tag: currentTag })} —{' '} + {currentTag === 'nsfw' ? t('filtering_by_nsfw') + ' ("adult", "gore", "vulgar", "anti") —' : t('filtering_by_tag', { tag: currentTag }) + ' —'}{' '} {t('undo')} @@ -177,6 +175,7 @@ const Subplebbit = ({ subplebbit, tags, index, isUnsubscribed, onUnsubscribe }: const { address, createdAt, description, roles, shortAddress, settings, suggested, title } = subplebbit || {}; const [avatarLoadFailed, setAvatarLoadFailed] = useState(false); const showSprout = !suggested?.avatarUrl || avatarLoadFailed; + const location = useLocation(); useEffect(() => { setAvatarLoadFailed(false); @@ -187,6 +186,11 @@ const Subplebbit = ({ subplebbit, tags, index, isUnsubscribed, onUnsubscribe }: const account = useAccount(); const userRole = roles?.[account?.author?.address]?.role; + const getTagFilterRoute = (tag: string) => { + const pathname = location.pathname; + return `${pathname}?tag=${encodeURIComponent(tag)}`; + }; + // TODO: make arrows functional when token voting is implemented in the API const upvoted = false; const downvoted = false; @@ -276,13 +280,17 @@ const Subplebbit = ({ subplebbit, tags, index, isUnsubscribed, onUnsubscribe }: )} - {isNsfw && } + {isNsfw && ( + + + + )} {isOffline && !isOnlineStatusLoading &&