Merge pull request #677 from plebbit/development

Development
This commit is contained in:
plebeius
2025-05-31 11:41:54 +02:00
committed by GitHub
65 changed files with 1585 additions and 326 deletions

View File

@@ -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,
};
});

View File

@@ -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),
});

View File

@@ -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),

View File

@@ -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",

View File

@@ -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": "إخفاء كل المحتوى غير الآمن"
}

View File

@@ -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 লুকান"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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": "הסתר את כל התוכן הלא בטוח"
}

View File

@@ -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 छुपाएं"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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 laudio 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"
}

View File

@@ -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を非表示"
}

View File

@@ -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 숨기기"
}

View File

@@ -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 लपवा"
}

View File

@@ -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": "Videos 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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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 దాచు"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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 چھپائیں"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -118,7 +118,6 @@ const App = () => {
<Route path='/communities/vote' element={<Subplebbits />} />
<Route path='/communities/vote/passing' element={<Subplebbits />} />
<Route path='/communities/vote/rejecting' element={<Subplebbits />} />
<Route path='/communities/vote/tag/:tag' element={<Subplebbits />} />
<Route path='/communities/create' element={<SubplebbitSettings />} />
</Route>
<Route element={feedLayout}>

View File

@@ -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 = '';

View File

@@ -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 (
<li
className={`${!hasCopied ? styles.button : styles.text}`}
onClick={() => {
setHasCopied(true);
copyShareLinkToClipboard(subplebbitAddress, cid);
}}
>
<li className={`${!hasCopied ? styles.button : styles.text}`} onClick={handleCopy}>
{hasCopied ? t('link_copied') : t('share')}
</li>
);

View File

@@ -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;

View File

@@ -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) => {
</div>
<div className={`${styles.container} ${blocked && !isInProfileHiddenView ? styles.hidden : styles.visible}`}>
<div className={styles.row}>
{!isMobile && !isInProfileView && !isInPostPageView && <div className={styles.rank}>{pinned ? undefined : rank}</div>}
{!isMobile && !isInProfileView && !isInAuthorView && !isInPostPageView && <div className={styles.rank}>{pinned ? undefined : rank}</div>}
<div className={styles.leftcol}>
<div className={styles.midcol}>
<div className={styles.arrowWrapper}>

View File

@@ -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<string>('');
@@ -104,7 +107,24 @@ const Thumbnail = ({
} else if (commentMediaInfo?.type === 'iframe') {
mediaComponent = iframeThumbnail ? <img src={iframeThumbnail} alt='' onError={handleNotFound} /> : <span className={`${styles.iconThumbnail} ${styles.linkIcon}`} />;
} else if (commentMediaInfo?.type === 'gif') {
mediaComponent = <img src={gifFrameUrl || commentMediaInfo.url} alt='' onError={handleNotFound} />;
if (gifFrameUrl) {
mediaComponent = <img src={gifFrameUrl} alt='' onError={handleNotFound} />;
} else if (gifFrameLoading) {
displayWidth = '50px';
displayHeight = '50px';
hasLinkDimensions = false;
mediaComponent = <span className={`${styles.iconThumbnail} ${styles.imageIcon}`} />;
} else {
// Chrome has strong CORS restrictions that prevent frame extraction, so we show a placeholder
if (isChromium) {
displayWidth = '50px';
displayHeight = '50px';
hasLinkDimensions = true;
mediaComponent = <span className={`${styles.iconThumbnail} ${styles.imageIcon}`} />;
} else {
mediaComponent = <img src={commentMediaInfo.url} alt='' onError={handleNotFound} />;
}
}
}
if (isText) {
@@ -137,6 +157,8 @@ const Thumbnail = ({
noMediaLinkIcon = 'notfound';
}
const style = hasLinkDimensions ? ({ '--width': displayWidth, '--height': displayHeight } as React.CSSProperties) : {};
return (
<span className={`${styles.thumbnail} ${thumbnailClass}`} style={style}>
<span className={hasLinkDimensions ? styles.transparentThumbnailWrapper : styles.thumbnailWrapper}>

View File

@@ -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

View File

@@ -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 = () => {
<span className={styles.selectedTitle}>{t('tags')}</span>
<div className={`${styles.dropChoices} ${styles.filterDropChoices} ${tagFilterDropdownClass}`} ref={tagFilterdropdownItemsRef}>
<div className={styles.dropdownItem} onClick={handleToggleAll} style={{ cursor: 'pointer' }}>
<span className={styles.dropdownItemText}>{allHidden ? 'show all nsfw' : 'hide all nsfw'}</span>
<span className={styles.dropdownItemText}>{allHidden ? t('show_all_nsfw') : t('hide_all_nsfw')}</span>
</div>
{tags.map((tag, index) => (
<div key={index} className={styles.dropdownItem} onClick={(e) => handleToggleTag(e, tag.setter, tag.isHidden)} style={{ cursor: 'pointer' }}>
<div key={index} className={styles.dropdownItem} onClick={(e) => handleToggleTag(e, tag.setter, tag.isHidden, tag.name)} style={{ cursor: 'pointer' }}>
<span className={styles.dropdownItemText}>
{tag.isHidden ? 'show' : 'hide'} <i>{tag.name}</i>
{tag.isHidden ? t('show') : t('hide')} <i>{tag.name}</i>
</span>
</div>
))}

1
src/globals.d.ts vendored
View File

@@ -1,6 +1,5 @@
declare global {
interface Window {
STICKY_MENU_SCROLL_LISTENER: boolean;
isElectron: boolean;
}
}

View File

@@ -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<MultisubSubplebbit[]>([]);
const [subplebbits, setSubplebbits] = useState<MultisubSubplebbit[]>(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<MultisubMetadata | null>(null);
const [metadata, setMetadata] = useState<MultisubMetadata | null>(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) => {

View File

@@ -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<string | null> => {
return await gifFrameDb.getItem(url);
};
@@ -16,17 +22,76 @@ export const fetchImage = (url: string): Promise<ArrayBuffer> => {
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<ArrayBuffer> => {
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<ArrayBuffer> => {
return new Promise((resolve) => {
const reader = new FileReader();
@@ -37,62 +102,252 @@ export const readImage = (file: File): Promise<ArrayBuffer> => {
});
};
const parseGif = async (buf: ArrayBuffer): Promise<Blob> => {
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<Blob> => {
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<ArrayBuffer> => {
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<string> => {
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<string | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(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;

View File

@@ -0,0 +1,30 @@
/**
* Universal clipboard utility that works in both Electron and web environments
*/
export const copyToClipboard = async (text: string): Promise<void> => {
// 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');
}
};

View File

@@ -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<void> => {
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);
}
}
};

View File

@@ -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);
};

View File

@@ -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 (
<div className={styles.content}>

View File

@@ -102,6 +102,8 @@ const Inbox = () => {
useEffect(() => {
if (error?.message && comments.length === 0) {
setShouldShowErrorToUser(true);
} else if (comments.length > 0) {
setShouldShowErrorToUser(false);
}
}, [error, comments]);

View File

@@ -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]);

View File

@@ -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]);

View File

@@ -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 = () => {
<div className={styles.securityWarning}>
<img src='assets/privacy_icon.png' alt='security warning' />
<div className={styles.warning}>
<h3>Your private key will be displayed</h3>
<p>You're about to view your account data, which includes your private key. You should never share your private key with anyone.</p>
<h3>{t('private_key_warning_title')}</h3>
<p>{t('private_key_warning_description')}</p>
</div>
<div className={styles.warningButtons}>
<button onClick={() => navigate('/settings')}>go back</button>
<button onClick={() => navigate('/settings')}>{t('go_back')}</button>
<button onClick={() => setShowEditor(true)}>{t('continue')}</button>
</div>
</div>
@@ -70,7 +71,13 @@ const AccountDataEditor = () => {
return (
<div className={styles.content}>
<Suspense fallback={<div className={styles.loading}>Loading editor...</div>}>
<Suspense
fallback={
<div className={styles.loading}>
<LoadingEllipsis string={t('loading_editor')} />
</div>
}
>
<LazyAceEditor
mode='json'
theme={theme === 'dark' ? 'tomorrow_night' : 'github'}

View File

@@ -1,6 +1,9 @@
import { useTranslation } from 'react-i18next';
import { useAccount } from '@plebbit/plebbit-react-hooks';
import styles from './content-options.module.css';
import useContentOptionsStore from '../../../stores/use-content-options-store';
import { useDefaultSubplebbits } from '../../../hooks/use-default-subplebbits';
import { handleNSFWSubscriptionPrompt } from '../../../lib/utils/nsfw-subscription-utils';
const MediaOptions = () => {
const { t } = useTranslation();
@@ -25,13 +28,13 @@ const MediaOptions = () => {
<div>
<label>
<input type='radio' name='thumbnailOption' value='show' checked={thumbnailDisplayOption === 'show'} onChange={() => setThumbnailDisplayOption('show')} />
Show thumbnails next to links
{t('show_thumbnails_next_to_links')}
</label>
</div>
<div>
<label>
<input type='radio' name='thumbnailOption' value='hide' checked={thumbnailDisplayOption === 'hide'} onChange={() => setThumbnailDisplayOption('hide')} />
Don't show thumbnails next to links
{t('dont_show_thumbnails_next_to_links')}
</label>
</div>
<div>
@@ -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')}
</label>
</div>
<br />
<div className={styles.contentOptionTitle}>media previews</div>
<div className={styles.contentOptionTitle}>{t('media_previews')}</div>
<div>
<label>
<input
@@ -64,7 +67,7 @@ const MediaOptions = () => {
checked={mediaPreviewOption === 'autoExpandAll'}
onChange={() => setMediaPreviewOption('autoExpandAll')}
/>
Auto-expand media previews
{t('auto_expand_media_previews')}
</label>
</div>
<div>
@@ -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')}
</label>
</div>
<div>
@@ -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')}
</label>
</div>
<br />
<div className={styles.contentOptionTitle}>Video Player</div>
<div className={styles.contentOptionTitle}>{t('video_player')}</div>
<div>
<label>
<input type='checkbox' checked={autoplayVideosOnComments} onChange={(e) => setAutoplayVideosOnComments(e.target.checked)} />
Autoplay videos on the comments page
{t('autoplay_videos_on_comments_page')}
</label>
</div>
<div>
<label>
<input type='checkbox' checked={muteVideosOnComments} onChange={(e) => setMuteVideosOnComments(e.target.checked)} />
Mute videos by default
{t('mute_videos_by_default')}
</label>
</div>
<br />
@@ -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 = () => {
<input
type='checkbox'
checked={hideAdultCommunities}
onChange={(e) => {
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 = () => {
<input
type='checkbox'
checked={hideGoreCommunities}
onChange={(e) => {
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 = () => {
<input
type='checkbox'
checked={hideAntiCommunities}
onChange={(e) => {
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 = () => {
<input
type='checkbox'
checked={hideVulgarCommunities}
onChange={(e) => {
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')}

View File

@@ -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<string | null>(null);
const [platform, setPlatform] = useState<NodeJS.Platform | null>(null);
@@ -150,7 +152,7 @@ const NotificationsSettings = () => {
onChange={handleCheckboxChange}
disabled={isLoading || permissionStatus === 'requesting...' || permissionStatus === 'not-supported'}
/>
<label htmlFor='enableLocalNotifications'>new replies received</label>
<label htmlFor='enableLocalNotifications'>{t('new_replies_received')}</label>
{/* Not supported message */}
{permissionStatus === 'not-supported' && (

View File

@@ -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 (

View File

@@ -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);

View File

@@ -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 ? (
<Over18Warning />

View File

@@ -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 = () => {
</div>
{currentTag && (
<div className={styles.infobar}>
{t('filtering_by_tag', { tag: currentTag })} {' '}
{currentTag === 'nsfw' ? t('filtering_by_nsfw') + ' ("adult", "gore", "vulgar", "anti") —' : t('filtering_by_tag', { tag: currentTag }) + ' —'}{' '}
<Link className={styles.undoLink} to={basePath}>
{t('undo')}
</Link>
@@ -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 }:
<span className={`${styles.moderatorIcon} ${isNsfw ? styles.addMarginRight : ''}`} title={userRole || 'owner'} />
</Link>
)}
{isNsfw && <span className={styles.over18icon} />}
{isNsfw && (
<Link to={getTagFilterRoute('nsfw')}>
<span className={styles.over18icon} title='Filter NSFW communities' />
</Link>
)}
{isOffline && !isOnlineStatusLoading && <Label color='red' title={offlineTitle} text={t('offline')} />}
{tags && tags.length > 0 && (
<span className={styles.tags}>
{tags.map((tag, index) => (
<Fragment key={index}>
<Link to={`/communities/vote/tag/${tag}`}>{tag}</Link>
<Link to={getTagFilterRoute(tag)}>{tag}</Link>
</Fragment>
))}
</span>
@@ -304,18 +312,38 @@ const AccountSubplebbits = ({ viewRole }: { viewRole: string }) => {
const account = useAccount();
const { accountSubplebbits, error: accountSubplebbitsError } = useAccountSubplebbits();
const { setError } = useErrorStore();
const location = useLocation();
const defaultSubplebbits = useDefaultSubplebbits();
useEffect(() => {
setError('AccountSubplebbits_useAccountSubplebbits', accountSubplebbitsError);
}, [accountSubplebbitsError, setError, viewRole]);
const urlParams = new URLSearchParams(location.search);
const currentTag = urlParams.get('tag');
const subplebbitElements = Object.values(accountSubplebbits)
.filter((subplebbit: any) => {
const isUserOwner = subplebbit.settings !== undefined;
const userRole = (subplebbit as any).roles?.[account?.author?.address]?.role;
return isUserOwner || userRole === viewRole;
})
.map((subplebbitData, index) => <Subplebbit key={index} subplebbit={subplebbitData} index={index} />);
.filter((subplebbitData: any) => {
if (currentTag) {
const tags = defaultSubplebbits.find((defaultSub) => defaultSub.address === (subplebbitData as any).address)?.tags;
if (currentTag === 'nsfw') {
return tags?.some((tag) => nsfwTags.includes(tag));
} else {
return tags?.includes(currentTag);
}
}
return true;
})
.map((subplebbitData, index) => {
const tags = defaultSubplebbits.find((defaultSub) => defaultSub.address === (subplebbitData as any).address)?.tags;
return <Subplebbit key={index} subplebbit={subplebbitData} tags={tags} index={index} />;
});
if (subplebbitElements.length === 0) {
return <NoCommunitiesMessage />;
@@ -326,6 +354,11 @@ const AccountSubplebbits = ({ viewRole }: { viewRole: string }) => {
const SubscriberSubplebbits = () => {
const account = useAccount();
const { setError } = useErrorStore();
const location = useLocation();
const defaultSubplebbits = useDefaultSubplebbits();
const urlParams = new URLSearchParams(location.search);
const currentTag = urlParams.get('tag');
const getAccountSubscriptions = useCallback(() => {
return account?.subscriptions ? [...account.subscriptions].reverse() : [];
@@ -347,17 +380,32 @@ const SubscriberSubplebbits = () => {
}, [subplebbitsError, setError]);
const subplebbitElements = Object.values(subplebbits ?? {})
.map((subplebbitData, index) =>
subplebbitData ? (
.filter((subplebbit): subplebbit is SubplebbitType => Boolean(subplebbit))
.filter((subplebbitData) => {
if (currentTag) {
const tags = defaultSubplebbits.find((defaultSub) => defaultSub.address === (subplebbitData as any).address)?.tags;
if (currentTag === 'nsfw') {
return tags?.some((tag) => nsfwTags.includes(tag));
} else {
return tags?.includes(currentTag);
}
}
return true;
})
.map((subplebbitData, index) => {
const tags = defaultSubplebbits.find((defaultSub) => defaultSub.address === (subplebbitData as any).address)?.tags;
return subplebbitData ? (
<Subplebbit
key={subplebbitData.address || index}
subplebbit={subplebbitData}
tags={tags}
index={index}
isUnsubscribed={isUnsubscribed(subplebbitData.address)}
onUnsubscribe={handleUnsubscribe}
/>
) : null,
)
) : null;
})
.filter(Boolean);
if (subplebbitElements.length === 0) {
@@ -369,11 +417,10 @@ const SubscriberSubplebbits = () => {
const AllDefaultSubplebbits = () => {
const defaultSubplebbitsList = useDefaultSubplebbits(); // Renamed to avoid conflict
const subplebbitAddresses = useDefaultSubplebbitAddresses();
const pathname = useLocation().pathname;
const validTags = useDefaultSubplebbitTags(defaultSubplebbitsList);
const location = useLocation();
const urlTag = pathname.includes('/tag/') ? pathname.split('/').pop() : undefined;
const currentTag = urlTag && validTags.includes(urlTag) ? urlTag : undefined;
const urlParams = new URLSearchParams(location.search);
const currentTag = urlParams.get('tag');
const { subplebbits, error: subplebbitsError } = useSubplebbits({ subplebbitAddresses });
const { setError } = useErrorStore();
@@ -385,12 +432,19 @@ const AllDefaultSubplebbits = () => {
const subplebbitElements = Object.values(subplebbits ?? {})
.filter((subplebbit): subplebbit is SubplebbitType => Boolean(subplebbit)) // Type guard
.filter((subplebbitData) => {
const tags = defaultSubplebbitsList.find((defaultSub) => defaultSub.address === subplebbitData.address)?.tags;
if (currentTag && !tags?.includes(currentTag)) return false;
if (currentTag) {
const tags = defaultSubplebbitsList.find((defaultSub) => defaultSub.address === (subplebbitData as any).address)?.tags;
if (currentTag === 'nsfw') {
return tags?.some((tag) => nsfwTags.includes(tag));
} else {
return tags?.includes(currentTag);
}
}
return true;
})
.map((subplebbitData, index) => {
const tags = defaultSubplebbitsList.find((defaultSub) => defaultSub.address === subplebbitData.address)?.tags;
const tags = defaultSubplebbitsList.find((defaultSub) => defaultSub.address === (subplebbitData as any).address)?.tags;
return <Subplebbit key={subplebbitData.address || index} subplebbit={subplebbitData} tags={tags} index={index} />;
});
@@ -404,25 +458,23 @@ const AllAccountSubplebbits = () => {
const account = useAccount();
const { accountSubplebbits, error: accountSubplebbitsError } = useAccountSubplebbits();
const { setError } = useErrorStore();
const location = useLocation();
const defaultSubplebbits = useDefaultSubplebbits();
useEffect(() => {
setError('AllAccountSubplebbits_useAccountSubplebbits', accountSubplebbitsError);
}, [accountSubplebbitsError, setError]);
const urlParams = new URLSearchParams(location.search);
const currentTag = urlParams.get('tag');
const getAllAccountRelatedAddresses = useCallback(() => {
const accountAddrs = Object.keys(accountSubplebbits);
const subs = account?.subscriptions ? [...account.subscriptions].reverse() : [];
return Array.from(new Set([...accountAddrs, ...subs]));
}, [accountSubplebbits, account?.subscriptions]);
const {
list: displayedAddresses,
isUnsubscribed,
handleUnsubscribe,
} = useDisplayedSubscriptions(
getAllAccountRelatedAddresses,
[account?.author?.address], // Reset dependencies
);
const { list: displayedAddresses, isUnsubscribed, handleUnsubscribe } = useDisplayedSubscriptions(getAllAccountRelatedAddresses, [account?.author?.address]);
const { subplebbits, error: subplebbitsError } = useSubplebbits({ subplebbitAddresses: displayedAddresses });
@@ -431,17 +483,32 @@ const AllAccountSubplebbits = () => {
}, [subplebbitsError, setError]);
const subplebbitElements = Object.values(subplebbits ?? {})
.map((subplebbitData, index) =>
subplebbitData ? (
.filter((subplebbit): subplebbit is SubplebbitType => Boolean(subplebbit))
.filter((subplebbitData) => {
if (currentTag) {
const tags = defaultSubplebbits.find((defaultSub) => defaultSub.address === (subplebbitData as any).address)?.tags;
if (currentTag === 'nsfw') {
return tags?.some((tag) => nsfwTags.includes(tag));
} else {
return tags?.includes(currentTag);
}
}
return true;
})
.map((subplebbitData, index) => {
const tags = defaultSubplebbits.find((defaultSub) => defaultSub.address === (subplebbitData as any).address)?.tags;
return subplebbitData ? (
<Subplebbit
key={subplebbitData.address || index}
subplebbit={subplebbitData}
tags={tags}
index={index}
isUnsubscribed={isUnsubscribed(subplebbitData.address)}
onUnsubscribe={handleUnsubscribe}
/>
) : null,
)
) : null;
})
.filter(Boolean);
if (subplebbitElements.length === 0) {
@@ -455,14 +522,12 @@ const Subplebbits = () => {
const location = useLocation();
const { errors, clearAllErrors } = useErrorStore();
// Clear errors on component unmount or location change
useEffect(() => {
return () => {
clearAllErrors();
};
}, [location, clearAllErrors]);
// Console log errors
useEffect(() => {
Object.entries(errors).forEach(([source, errorObj]) => {
if (errorObj) {

153
yarn.lock
View File

@@ -1303,9 +1303,9 @@
optionalDependencies:
global-agent "^3.0.0"
"@electron/node-gyp@git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2":
"@electron/node-gyp@https://github.com/electron/node-gyp#06b29aafb7708acef8b3669835c8a7857ebc92d2":
version "10.2.0-electron.1"
resolved "git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2"
resolved "https://github.com/electron/node-gyp#06b29aafb7708acef8b3669835c8a7857ebc92d2"
dependencies:
env-paths "^2.2.0"
exponential-backoff "^3.1.1"
@@ -2616,12 +2616,11 @@
dependencies:
buffer "^6.0.3"
"@keyv/sqlite@4.0.2":
version "4.0.2"
resolved "https://registry.yarnpkg.com/@keyv/sqlite/-/sqlite-4.0.2.tgz#c64bdcc6ff50280e0c56257a85c7c178fef581da"
integrity sha512-0tMWhvoe5gSkMYxnpkbnV6/f8ZAle1prm9drCyhKPQb3Pconk3zx+/3TX7adf6uY6ZF6xrGQE3o3ENcv/9QLUw==
"@keyv/sqlite@4.0.3":
version "4.0.3"
resolved "https://registry.yarnpkg.com/@keyv/sqlite/-/sqlite-4.0.3.tgz#269c7cd8fb150edb3374b2ef5930a794278f9b43"
integrity sha512-tdgelzrlng0cIiOZpxuDd/Ii/79h9On/0lcEyZ2s0Ml+9NXS6JnRrlb01+nfUWY53ioSDGvPKt4yL+s76uTWSg==
dependencies:
keyv "^5.3.2"
sqlite3 "^5.1.7"
"@leichtgewicht/ip-codec@^2.0.1":
@@ -3599,9 +3598,9 @@
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
"@plebbit/plebbit-js@https://github.com/plebbit/plebbit-js.git#a2cb6ca6462b2e2e3b66f71204375a81ed05cf80":
"@plebbit/plebbit-js@https://github.com/plebbit/plebbit-js.git#692ec349f01aa6d8e939ff1d6d07315ab92e4099":
version "0.0.7"
resolved "https://github.com/plebbit/plebbit-js.git#a2cb6ca6462b2e2e3b66f71204375a81ed05cf80"
resolved "https://github.com/plebbit/plebbit-js.git#692ec349f01aa6d8e939ff1d6d07315ab92e4099"
dependencies:
"@bonfida/spl-name-service" "3.0.10"
"@chainsafe/libp2p-gossipsub" "14.1.0"
@@ -3609,7 +3608,7 @@
"@helia/delegated-routing-v1-http-api-client" "4.2.1"
"@helia/ipns" "8.0.2"
"@helia/unixfs" "4.0.2"
"@keyv/sqlite" "4.0.2"
"@keyv/sqlite" "4.0.3"
"@libp2p/fetch" "3.0.0"
"@libp2p/identify" "3.0.15"
"@libp2p/interfaces" "3.3.2"
@@ -3619,7 +3618,7 @@
"@plebbit/proper-lockfile" "github:plebbit/node-proper-lockfile#7fd6332117340c1d3d98dd0afee2d31cc06f72b8"
"@types/proper-lockfile" "4.1.2"
assert "2.1.0"
better-sqlite3 "11.9.1"
better-sqlite3 "11.10.0"
blockstore-core "5.0.2"
blockstore-fs "2.0.2"
blockstore-idb "2.0.1"
@@ -3637,8 +3636,7 @@
jose "4.11.0"
js-sha256 "0.11.0"
js-sha512 "0.9.0"
keyv "5.3.2"
knex "3.1.0"
keyv "5.3.3"
kubo-rpc-client "5.1.0"
libp2p-crypto "0.21.2"
limiter-es6-compat "2.1.2"
@@ -3678,11 +3676,27 @@
dependencies:
debug "4.3.4"
"@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":
version "0.0.1"
resolved "https://github.com/plebbit/plebbit-react-hooks.git#f760ce2f5961307c93517fed0257b425bbf38c30"
resolved "https://github.com/plebbit/plebbit-react-hooks.git#090e70bed5f799fc140a31a6f2123f9125954fde"
dependencies:
"@plebbit/plebbit-js" "https://github.com/plebbit/plebbit-js.git#a2cb6ca6462b2e2e3b66f71204375a81ed05cf80"
"@plebbit/plebbit-js" "https://github.com/plebbit/plebbit-js.git#692ec349f01aa6d8e939ff1d6d07315ab92e4099"
"@plebbit/plebbit-logger" "https://github.com/plebbit/plebbit-logger.git"
assert "2.0.0"
ethers "5.6.9"
localforage "1.10.0"
lodash.isequal "4.5.0"
memoizee "0.4.15"
quick-lru "5.1.1"
uint8arrays "3.1.1"
uuid "8.3.2"
zustand "4.0.0"
"@plebbit/plebbit-react-hooks@https://github.com/plebbit/plebbit-react-hooks.git#19a31e62db24e47994ed8c076b05a83a8fa3dcfc":
version "0.0.1"
resolved "https://github.com/plebbit/plebbit-react-hooks.git#19a31e62db24e47994ed8c076b05a83a8fa3dcfc"
dependencies:
"@plebbit/plebbit-js" "https://github.com/plebbit/plebbit-js.git#692ec349f01aa6d8e939ff1d6d07315ab92e4099"
"@plebbit/plebbit-logger" "https://github.com/plebbit/plebbit-logger.git"
assert "2.0.0"
ethers "5.6.9"
@@ -5525,10 +5539,10 @@ bech32@1.1.4:
resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9"
integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==
better-sqlite3@11.9.1:
version "11.9.1"
resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-11.9.1.tgz#0540da2f2ce24cbd766bb35db412f4be2c75b8bb"
integrity sha512-Ba0KR+Fzxh2jDRhdg6TSH0SJGzb8C0aBY4hR8w8madIdIzzC6Y1+kx5qR6eS1Z+Gy20h6ZU28aeyg0z1VIrShQ==
better-sqlite3@11.10.0:
version "11.10.0"
resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-11.10.0.tgz#2b1b14c5acd75a43fd84d12cc291ea98cef57d98"
integrity sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==
dependencies:
bindings "^1.5.0"
prebuild-install "^7.1.1"
@@ -6296,11 +6310,6 @@ color@^4.0.1:
color-convert "^2.0.1"
color-string "^1.9.0"
colorette@2.0.19:
version "2.0.19"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798"
integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==
colorette@^2.0.16:
version "2.0.20"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a"
@@ -6318,11 +6327,6 @@ comma-separated-tokens@^2.0.0:
resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee"
integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==
commander@^10.0.0:
version "10.0.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06"
integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==
commander@^12.0.0, commander@^12.1.0:
version "12.1.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3"
@@ -7984,11 +7988,6 @@ eslint@8.56.0:
strip-ansi "^6.0.1"
text-table "^0.2.0"
esm@^3.2.25:
version "3.2.25"
resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10"
integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==
esniff@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308"
@@ -8640,11 +8639,6 @@ get-own-enumerable-property-symbols@^3.0.0:
resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664"
integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==
get-package-type@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a"
integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==
get-port@^7.1.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/get-port/-/get-port-7.1.0.tgz#d5a500ebfc7aa705294ec2b83cc38c5d0e364fec"
@@ -8687,10 +8681,12 @@ get-symbol-description@^1.1.0:
es-errors "^1.3.0"
get-intrinsic "^1.2.6"
getopts@2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.3.0.tgz#71e5593284807e03e2427449d4f6712a268666f4"
integrity sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==
gifuct-js@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/gifuct-js/-/gifuct-js-2.1.2.tgz#06152437ba30ec914db8398bd838bd0fbc8a6ecd"
integrity sha512-rI2asw77u0mGgwhV3qA+OEgYqaDn5UNqgs+Bx0FGwSpuqfYn+Ir6RQY5ENNQ8SbIiG/m5gVa7CD5RriO4f4Lsg==
dependencies:
js-binary-schema-parser "^2.0.3"
git-raw-commits@^4.0.0:
version "4.0.0"
@@ -9468,11 +9464,6 @@ internal-slot@^1.1.0:
hasown "^2.0.2"
side-channel "^1.1.0"
interpret@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9"
integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==
ip-address@^9.0.5:
version "9.0.5"
resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a"
@@ -10467,6 +10458,11 @@ js-base64@^3.6.0:
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.7.tgz#e51b84bf78fbf5702b9541e2cb7bfcb893b43e79"
integrity sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==
js-binary-schema-parser@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/js-binary-schema-parser/-/js-binary-schema-parser-2.0.3.tgz#3d7848748e8586e63b34e8911b643f59cfb6396e"
integrity sha512-xezGJmOb4lk/M1ZZLTR/jaBHQ4gG/lqQnJqdIv4721DMggsa1bDVlHXNeHYogaIEHD9vCRv0fcL4hMA+Coarkg==
js-sha256@0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.11.0.tgz#256a921d9292f7fe98905face82e367abaca9576"
@@ -10612,10 +10608,10 @@ just-performance-es6-compat@4.3.2:
resolved "https://registry.yarnpkg.com/just-performance-es6-compat/-/just-performance-es6-compat-4.3.2.tgz#7877f34cbe25fd994bc2265d0c919bf728dcc42e"
integrity sha512-bNPuqRaV8PrqYVivdN8FsKi0rmXaQ0Y63oasFUQFJooJPH0r26LdfBAfoodhUQI6I6cek9yNo+tnGiF0R3pIHw==
keyv@5.3.2:
version "5.3.2"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-5.3.2.tgz#32edd461b51d44d42926eb72946236d79c71ae78"
integrity sha512-Lji2XRxqqa5Wg+CHLVfFKBImfJZ4pCSccu9eVWK6w4c2SDFLd8JAn1zqTuSFnsxb7ope6rMsnIHfp+eBbRBRZQ==
keyv@5.3.3:
version "5.3.3"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-5.3.3.tgz#ec2d723fbd7b908de5ee7f56b769d46dbbeaf8ba"
integrity sha512-Rwu4+nXI9fqcxiEHtbkvoes2X+QfkTRo1TMkPfwzipGsJlJO/z69vqB4FNl9xJ3xCpAcbkvmEabZfPzrwN3+gQ==
dependencies:
"@keyv/serialize" "^1.0.3"
@@ -10626,13 +10622,6 @@ keyv@^4.0.0, keyv@^4.5.3:
dependencies:
json-buffer "3.0.1"
keyv@^5.3.2:
version "5.3.3"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-5.3.3.tgz#ec2d723fbd7b908de5ee7f56b769d46dbbeaf8ba"
integrity sha512-Rwu4+nXI9fqcxiEHtbkvoes2X+QfkTRo1TMkPfwzipGsJlJO/z69vqB4FNl9xJ3xCpAcbkvmEabZfPzrwN3+gQ==
dependencies:
"@keyv/serialize" "^1.0.3"
kind-of@^6.0.3:
version "6.0.3"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
@@ -10648,26 +10637,6 @@ kleur@^4.0.3, kleur@^4.1.5:
resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780"
integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==
knex@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/knex/-/knex-3.1.0.tgz#b6ddd5b5ad26a6315234a5b09ec38dc4a370bd8c"
integrity sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==
dependencies:
colorette "2.0.19"
commander "^10.0.0"
debug "4.3.4"
escalade "^3.1.1"
esm "^3.2.25"
get-package-type "^0.1.0"
getopts "2.3.0"
interpret "^2.2.0"
lodash "^4.17.21"
pg-connection-string "2.6.2"
rechoir "^0.8.0"
resolve-from "^5.0.0"
tarn "^3.0.2"
tildify "2.0.0"
kubo-rpc-client@5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/kubo-rpc-client/-/kubo-rpc-client-5.1.0.tgz#06f9216b2b8a62487a150261b16fd941a75adb34"
@@ -12693,11 +12662,6 @@ pend@~1.2.0:
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==
pg-connection-string@2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.6.2.tgz#713d82053de4e2bd166fab70cd4f26ad36aab475"
integrity sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==
picocolors@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
@@ -13277,13 +13241,6 @@ readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@^2.3.8:
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
rechoir@^0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22"
integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==
dependencies:
resolve "^1.20.0"
redent@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f"
@@ -13456,7 +13413,7 @@ resolve-from@^5.0.0:
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
resolve@^1.10.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.1, resolve@^1.22.4:
resolve@^1.10.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.22.1, resolve@^1.22.4:
version "1.22.10"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39"
integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==
@@ -14532,11 +14489,6 @@ tar@^6.0.2, tar@^6.0.5, tar@^6.1.11, tar@^6.1.12, tar@^6.1.2, tar@^6.2.1:
mkdirp "^1.0.3"
yallist "^4.0.0"
tarn@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/tarn/-/tarn-3.0.2.tgz#73b6140fbb881b71559c4f8bfde3d9a4b3d27693"
integrity sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==
tcp-port-used@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/tcp-port-used/-/tcp-port-used-1.0.2.tgz#9652b7436eb1f4cfae111c79b558a25769f6faea"
@@ -14622,11 +14574,6 @@ thunky@^1.0.2:
resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d"
integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==
tildify@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/tildify/-/tildify-2.0.0.tgz#f205f3674d677ce698b7067a99e949ce03b4754a"
integrity sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==
time-span@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/time-span/-/time-span-5.1.0.tgz#80c76cf5a0ca28e0842d3f10a4e99034ce94b90d"