From b9b7d0ac78033bd9ff3e746bc68a769b7d91d56b Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Thu, 22 May 2025 00:25:29 +0200 Subject: [PATCH 01/15] some errors were displayed unnecessarily --- src/views/author/author.tsx | 6 ++++-- src/views/inbox/inbox.tsx | 2 ++ src/views/post-page/post-page.tsx | 2 ++ src/views/profile/profile.tsx | 8 ++++++++ src/views/subplebbit/subplebbit.tsx | 6 ++++-- 5 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/views/author/author.tsx b/src/views/author/author.tsx index 767fd884..8c660742 100644 --- a/src/views/author/author.tsx +++ b/src/views/author/author.tsx @@ -89,10 +89,12 @@ const Author = () => { // only show backend errors if the user gets no data const [shouldShowErrorToUser, setShouldShowErrorToUser] = useState(false); useEffect(() => { - if (error?.message && virtuosoData?.length === 0 && hasMore) { + if (error?.message && virtuosoData?.length === 0) { setShouldShowErrorToUser(true); + } else if (virtuosoData?.length > 0) { + setShouldShowErrorToUser(false); } - }, [error, virtuosoData, hasMore]); + }, [error, virtuosoData]); return (
diff --git a/src/views/inbox/inbox.tsx b/src/views/inbox/inbox.tsx index c645e272..e9ad86fb 100644 --- a/src/views/inbox/inbox.tsx +++ b/src/views/inbox/inbox.tsx @@ -102,6 +102,8 @@ const Inbox = () => { useEffect(() => { if (error?.message && comments.length === 0) { setShouldShowErrorToUser(true); + } else if (comments.length > 0) { + setShouldShowErrorToUser(false); } }, [error, comments]); diff --git a/src/views/post-page/post-page.tsx b/src/views/post-page/post-page.tsx index cf370da0..e2a2fe1c 100644 --- a/src/views/post-page/post-page.tsx +++ b/src/views/post-page/post-page.tsx @@ -302,6 +302,8 @@ const PostPage = () => { useEffect(() => { if (post?.error && ((post?.replyCount > 0 && post?.replies?.length === 0) || (post?.state === 'failed' && post?.error))) { setShouldShowErrorToUser(true); + } else if (post?.replyCount > 0 && post?.replies?.length > 0) { + setShouldShowErrorToUser(false); } }, [post]); diff --git a/src/views/profile/profile.tsx b/src/views/profile/profile.tsx index fd3bd704..4f6b8058 100644 --- a/src/views/profile/profile.tsx +++ b/src/views/profile/profile.tsx @@ -147,6 +147,8 @@ const Overview = () => { useEffect(() => { if (error?.message && sortedComments.length === 0) { setShouldShowErrorToUserOverview(true); + } else if (sortedComments.length > 0) { + setShouldShowErrorToUserOverview(false); } }, [error, sortedComments]); @@ -186,6 +188,8 @@ const Comments = () => { useEffect(() => { if (error?.message && sortedComments.length === 0) { setShouldShowErrorToUserComments(true); + } else if (sortedComments.length > 0) { + setShouldShowErrorToUserComments(false); } }, [error, sortedComments]); @@ -225,6 +229,8 @@ const Submitted = () => { useEffect(() => { if (error?.message && sortedComments.length === 0) { setShouldShowErrorToUserSubmitted(true); + } else if (sortedComments.length > 0) { + setShouldShowErrorToUserSubmitted(false); } }, [error, sortedComments]); @@ -268,6 +274,8 @@ const VotedComments = ({ voteType }: { voteType: 1 | -1 }) => { useEffect(() => { if (error?.message && paginatedCids.length === 0 && hasMore) { setShouldShowErrorToUserVoted(true); + } else if (paginatedCids.length > 0) { + setShouldShowErrorToUserVoted(false); } }, [error, paginatedCids, hasMore]); diff --git a/src/views/subplebbit/subplebbit.tsx b/src/views/subplebbit/subplebbit.tsx index ada59f5d..bb832797 100644 --- a/src/views/subplebbit/subplebbit.tsx +++ b/src/views/subplebbit/subplebbit.tsx @@ -369,10 +369,12 @@ const Subplebbit = () => { // probably not necessary to show the error to the user if the feed loaded successfully const [shouldShowErrorToUser, setShouldShowErrorToUser] = useState(false); useEffect(() => { - if (error?.message && feed.length === 0 && hasMore) { + if (error?.message && feed.length === 0) { setShouldShowErrorToUser(true); + } else if (feed.length > 0) { + setShouldShowErrorToUser(false); } - }, [error, feed, hasMore]); + }, [error, feed]); return isBroadlyNsfwSubplebbit && !hasUnhiddenAnyNsfwCommunity ? ( From d02d5480267f458f45eebd157e1ecbb88ff93831 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Fri, 23 May 2025 12:43:17 +0200 Subject: [PATCH 02/15] unnecessary value --- electron/src/preload.ts | 2 +- src/globals.d.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/electron/src/preload.ts b/electron/src/preload.ts index b5245be2..36e7a016 100755 --- a/electron/src/preload.ts +++ b/electron/src/preload.ts @@ -25,7 +25,7 @@ contextBridge.exposeInMainWorld('electron', { }, }); -// Expose a dedicated "electronApi" for your UI code +// Expose a dedicated "electronApi" for UI code contextBridge.exposeInMainWorld('electronApi', { isElectron: true, invoke: (channel: string, ...args: any[]) => ipcRenderer.invoke(channel, ...args), diff --git a/src/globals.d.ts b/src/globals.d.ts index 721d3c77..d0d7a7f5 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -1,6 +1,5 @@ declare global { interface Window { - STICKY_MENU_SCROLL_LISTENER: boolean; isElectron: boolean; } } From 84500ba3842b4a46539f4d32322dadf9315aafaf Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Fri, 23 May 2025 22:36:54 +0200 Subject: [PATCH 03/15] Update use-default-subplebbits.ts --- src/hooks/use-default-subplebbits.ts | 125 +++++++++++++++++++-------- 1 file changed, 89 insertions(+), 36 deletions(-) diff --git a/src/hooks/use-default-subplebbits.ts b/src/hooks/use-default-subplebbits.ts index 0905b99b..5222b6b7 100644 --- a/src/hooks/use-default-subplebbits.ts +++ b/src/hooks/use-default-subplebbits.ts @@ -21,38 +21,84 @@ export interface MultisubSubplebbit { let cacheSubplebbits: MultisubSubplebbit[] | null = null; let cacheMetadata: MultisubMetadata | null = null; let cacheAutoSubscribeAddresses: string[] | null = null; +let pending = false; + +// Subscriber pattern for notifying all hook instances +const subscribers = new Set<() => void>(); + +const notifySubscribers = () => { + subscribers.forEach((callback) => callback()); +}; + +// Shared fetch function to avoid duplication +const fetchMultisubData = async () => { + if (pending) { + return; + } + pending = true; + + try { + const multisub = await fetch( + 'https://raw.githubusercontent.com/plebbit/temporary-default-subplebbits/master/multisub.json', + // { cache: 'no-cache' } + ).then((res) => res.json()); + + const filteredSubplebbits = multisub.subplebbits.filter((sub: MultisubSubplebbit) => !sub.lowUptime); + + cacheSubplebbits = filteredSubplebbits; + + // Cache auto-subscribe addresses when we fetch subplebbits + cacheAutoSubscribeAddresses = filteredSubplebbits + .filter((sub: MultisubSubplebbit) => sub.seeditAutoSubscribe && sub.address) + .map((sub: MultisubSubplebbit) => sub.address); + + // Also cache metadata since we have the full response + const { title, description, createdAt, updatedAt } = multisub; + cacheMetadata = { title, description, createdAt, updatedAt }; + + // Notify all subscribers that cache has been updated + notifySubscribers(); + + return { subplebbits: filteredSubplebbits, metadata: cacheMetadata }; + } catch (e) { + console.warn(e); + return null; + } finally { + pending = false; + } +}; export const useDefaultSubplebbits = () => { - const [subplebbits, setSubplebbits] = useState([]); + const [subplebbits, setSubplebbits] = useState(cacheSubplebbits || []); useEffect(() => { + // If we already have cached data, use it immediately if (cacheSubplebbits) { + setSubplebbits(cacheSubplebbits); return; } - (async () => { - try { - const multisub = await fetch( - 'https://raw.githubusercontent.com/plebbit/temporary-default-subplebbits/master/multisub.json', - // { cache: 'no-cache' } - ).then((res) => res.json()); - const filteredSubplebbits = multisub.subplebbits.filter((sub: MultisubSubplebbit) => !sub.lowUptime); - - cacheSubplebbits = filteredSubplebbits; - - // Cache auto-subscribe addresses when we fetch subplebbits - cacheAutoSubscribeAddresses = filteredSubplebbits - .filter((sub: MultisubSubplebbit) => sub.seeditAutoSubscribe && sub.address) - .map((sub: MultisubSubplebbit) => sub.address); - - setSubplebbits(filteredSubplebbits); - } catch (e) { - console.warn(e); + // Subscribe to cache updates + const handleCacheUpdate = () => { + if (cacheSubplebbits) { + setSubplebbits(cacheSubplebbits); } - })(); + }; + + subscribers.add(handleCacheUpdate); + + // Trigger fetch if no cache and not pending + if (!pending) { + fetchMultisubData(); + } + + // Cleanup subscription + return () => { + subscribers.delete(handleCacheUpdate); + }; }, []); - return cacheSubplebbits || subplebbits; + return subplebbits; }; export const getAutoSubscribeAddresses = () => cacheAutoSubscribeAddresses || []; @@ -76,29 +122,36 @@ export const useDefaultSubplebbitAddresses = () => { }; export const useMultisubMetadata = () => { - const [metadata, setMetadata] = useState(null); + const [metadata, setMetadata] = useState(cacheMetadata || null); useEffect(() => { + // If we already have cached data, use it immediately if (cacheMetadata) { + setMetadata(cacheMetadata); return; } - (async () => { - try { - const multisub = await fetch( - 'https://raw.githubusercontent.com/plebbit/temporary-default-subplebbits/master/multisub.json', - // { cache: 'no-cache' } - ).then((res) => res.json()); - const { title, description, createdAt, updatedAt } = multisub; - const metadata: MultisubMetadata = { title, description, createdAt, updatedAt }; - cacheMetadata = metadata; - setMetadata(metadata); - } catch (e) { - console.warn(e); + + // Subscribe to cache updates + const handleCacheUpdate = () => { + if (cacheMetadata) { + setMetadata(cacheMetadata); } - })(); + }; + + subscribers.add(handleCacheUpdate); + + // Trigger fetch if no cache and not pending + if (!pending) { + fetchMultisubData(); + } + + // Cleanup subscription + return () => { + subscribers.delete(handleCacheUpdate); + }; }, []); - return cacheMetadata || metadata; + return metadata; }; const getUniqueTags = (multisub: any) => { From 460eeb96fe1aee332b8c5ca798bf666aed09ab8f Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Sat, 24 May 2025 13:06:01 +0200 Subject: [PATCH 04/15] Update expando.module.css --- src/components/post/expando/expando.module.css | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/post/expando/expando.module.css b/src/components/post/expando/expando.module.css index b1f03f45..31eba0e5 100644 --- a/src/components/post/expando/expando.module.css +++ b/src/components/post/expando/expando.module.css @@ -54,7 +54,6 @@ .mediaPreview img { /* TODO: max width should be dynamically calculated based on file dimensions from API */ max-width: 600px; - width: 100%; background-color: var(--background-thumbnail); object-fit: contain; } @@ -62,7 +61,6 @@ .mediaPreview video { /* TODO: max width should be dynamically calculated based on file dimensions from API */ max-width: 700px; - width: 100%; max-height: 50vh; background-color: var(--background-thumbnail); object-fit: contain; From d0bc190bfdbc20b86d16de8561bba43d06c46f50 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Sat, 24 May 2025 17:22:53 +0200 Subject: [PATCH 05/15] fix(thumbnail): gifs were stuck on playing endlessly on chromium-based browsers --- package.json | 1 + src/components/post/thumbnail/thumbnail.tsx | 30 +- src/hooks/use-fetch-gif-first-frame.ts | 311 ++++++++++++++++++-- yarn.lock | 16 +- 4 files changed, 324 insertions(+), 34 deletions(-) diff --git a/package.json b/package.json index bd409da1..e8edc420 100755 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "ext-name": "5.0.0", "form-data": "4.0.2", "fs-extra": "11.2.0", + "gifuct-js": "2.1.2", "http-proxy": "1.18.1", "i18next": "23.5.1", "i18next-browser-languagedetector": "7.1.0", diff --git a/src/components/post/thumbnail/thumbnail.tsx b/src/components/post/thumbnail/thumbnail.tsx index 1d51f87a..50578c09 100644 --- a/src/components/post/thumbnail/thumbnail.tsx +++ b/src/components/post/thumbnail/thumbnail.tsx @@ -65,15 +65,18 @@ const Thumbnail = ({ hasLinkDimensions = true; } - const style = hasLinkDimensions ? ({ '--width': displayWidth, '--height': displayHeight } as React.CSSProperties) : {}; - const handleNotFound = () => { setIsNotFound(true); }; let mediaComponent = null; let noMediaLinkIcon = ''; - const gifFrameUrl = useFetchGifFirstFrame(commentMediaInfo?.type === 'gif' ? commentMediaInfo.url : undefined); + const { frameUrl: gifFrameUrl, isLoading: gifFrameLoading } = useFetchGifFirstFrame(commentMediaInfo?.type === 'gif' ? commentMediaInfo.url : undefined); + + const isChromium = (() => { + const ua = navigator.userAgent; + return (ua.includes('Chrome') || ua.includes('Chromium') || ua.includes('Edge')) && !ua.includes('Firefox'); + })(); const [videoDuration, setVideoDuration] = useState(''); @@ -104,7 +107,24 @@ const Thumbnail = ({ } else if (commentMediaInfo?.type === 'iframe') { mediaComponent = iframeThumbnail ? : ; } else if (commentMediaInfo?.type === 'gif') { - mediaComponent = ; + if (gifFrameUrl) { + mediaComponent = ; + } else if (gifFrameLoading) { + displayWidth = '50px'; + displayHeight = '50px'; + hasLinkDimensions = false; + mediaComponent = ; + } else { + // Chrome has strong CORS restrictions that prevent frame extraction, so we show a placeholder + if (isChromium) { + displayWidth = '50px'; + displayHeight = '50px'; + hasLinkDimensions = true; + mediaComponent = ; + } else { + mediaComponent = ; + } + } } if (isText) { @@ -137,6 +157,8 @@ const Thumbnail = ({ noMediaLinkIcon = 'notfound'; } + const style = hasLinkDimensions ? ({ '--width': displayWidth, '--height': displayHeight } as React.CSSProperties) : {}; + return ( diff --git a/src/hooks/use-fetch-gif-first-frame.ts b/src/hooks/use-fetch-gif-first-frame.ts index 08cb91c8..1c1a5a74 100644 --- a/src/hooks/use-fetch-gif-first-frame.ts +++ b/src/hooks/use-fetch-gif-first-frame.ts @@ -1,8 +1,14 @@ import { useEffect, useState } from 'react'; +import { decompressFrames, parseGIF } from 'gifuct-js'; import localForageLru from '@plebbit/plebbit-react-hooks/dist/lib/localforage-lru/index.js'; const gifFrameDb = localForageLru.createInstance({ name: 'seeditGifFrames', size: 500 }); +const isChromium = () => { + const ua = navigator.userAgent; + return (ua.includes('Chrome') || ua.includes('Chromium') || ua.includes('Edge')) && !ua.includes('Firefox'); +}; + const getCachedGifFrame = async (url: string): Promise => { return await gifFrameDb.getItem(url); }; @@ -16,17 +22,76 @@ export const fetchImage = (url: string): Promise => { const request = new XMLHttpRequest(); request.open('GET', url, true); request.responseType = 'arraybuffer'; + + // Add error logging to understand what's happening request.onloadend = () => { + console.log('XMLHttpRequest completed:', { + url, + status: request.status, + statusText: request.statusText, + responseType: request.responseType, + response: request.response, + readyState: request.readyState, + }); + if (request.response !== undefined && (request.status === 200 || request.status === 304)) { resolve(request.response); } else { - reject(new Error(`XMLHttpRequest, ${request.statusText}`)); + reject(new Error(`XMLHttpRequest failed: ${request.status} ${request.statusText} for URL: ${url}`)); } }; - request.send(); + + request.onerror = () => { + console.error('XMLHttpRequest error:', { + url, + status: request.status, + statusText: request.statusText, + readyState: request.readyState, + }); + reject(new Error(`XMLHttpRequest network error for URL: ${url}`)); + }; + + request.ontimeout = () => { + console.error('XMLHttpRequest timeout:', { url }); + reject(new Error(`XMLHttpRequest timeout for URL: ${url}`)); + }; + + // Set timeout to prevent hanging + request.timeout = 30000; // 30 seconds + + // Try to handle CORS + try { + request.send(); + } catch (error) { + console.error('XMLHttpRequest send error:', error); + reject(new Error(`XMLHttpRequest send failed: ${error}`)); + } }); }; +// Fallback fetch method for when XMLHttpRequest fails +export const fetchImageWithFetch = async (url: string): Promise => { + try { + console.log('Trying fetch method for:', url); + const response = await fetch(url, { + method: 'GET', + mode: 'cors', + cache: 'default', + }); + + if (!response.ok) { + throw new Error(`Fetch failed: ${response.status} ${response.statusText}`); + } + + const arrayBuffer = await response.arrayBuffer(); + console.log('Fetch successful for:', url); + return arrayBuffer; + } catch (error) { + console.error('Fetch error:', error); + throw error; + } +}; + export const readImage = (file: File): Promise => { return new Promise((resolve) => { const reader = new FileReader(); @@ -37,62 +102,252 @@ export const readImage = (file: File): Promise => { }); }; -const parseGif = async (buf: ArrayBuffer): Promise => { - const image = new Image(); - await new Promise((resolve) => { - image.src = URL.createObjectURL(new Blob([buf])); - image.onload = resolve; - }); - const canvas = document.createElement('canvas'); - canvas.width = image.width; - canvas.height = image.height; - const ctx = canvas.getContext('2d'); - if (ctx === null) throw new Error('Canvas Context null'); - ctx.drawImage(image, 0, 0, image.width, image.height); - return await new Promise((resolve, reject) => - canvas.toBlob((blob) => { - if (blob === null) { - reject('Canvas Blob null'); - } else { - resolve(blob); +const extractFirstFrame = async (buffer: ArrayBuffer): Promise => { + try { + // Parse the GIF using gifuct-js + const gif = parseGIF(buffer); + const frames = decompressFrames(gif, true); + + if (frames.length === 0) { + throw new Error('No frames found in GIF'); + } + + // Get the first frame + const firstFrame = frames[0]; + const { width, height } = firstFrame.dims; + + // Create canvas and draw the frame + const canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + + const ctx = canvas.getContext('2d'); + if (!ctx) { + throw new Error('Cannot get canvas context'); + } + + // Create ImageData from the frame data + const imageData = new ImageData(new Uint8ClampedArray(firstFrame.patch), width, height); + + // Clear canvas and put the image data + ctx.clearRect(0, 0, width, height); + ctx.putImageData(imageData, 0, 0); + + // Convert to blob + return new Promise((resolve, reject) => { + canvas.toBlob( + (blob) => { + if (blob) { + resolve(blob); + } else { + reject(new Error('Failed to create blob from canvas')); + } + }, + 'image/png', + 1.0, + ); + }); + } catch (error) { + throw new Error(`Failed to extract first frame: ${error}`); + } +}; + +// CORS-bypass method using image element +export const fetchImageViaCORSProxy = async (url: string): Promise => { + return new Promise((resolve, reject) => { + const img = new Image(); + img.crossOrigin = 'anonymous'; + + img.onload = () => { + try { + // Create a canvas to get the image data + const canvas = document.createElement('canvas'); + canvas.width = img.naturalWidth; + canvas.height = img.naturalHeight; + + const ctx = canvas.getContext('2d'); + if (!ctx) { + reject(new Error('Cannot get canvas context')); + return; + } + + ctx.drawImage(img, 0, 0); + + // Convert canvas to blob and then to ArrayBuffer + canvas.toBlob( + async (blob) => { + if (blob) { + try { + const arrayBuffer = await blob.arrayBuffer(); + resolve(arrayBuffer); + } catch (error) { + reject(new Error(`Failed to convert blob to ArrayBuffer: ${error}`)); + } + } else { + reject(new Error('Failed to create blob from canvas')); + } + }, + 'image/gif', + 1.0, + ); + } catch (error) { + reject(new Error(`Canvas operation failed: ${error}`)); } - }), - ); + }; + + img.onerror = () => { + reject(new Error(`Image load failed for URL: ${url}`)); + }; + + // Try different approaches for CORS + try { + img.src = url; + } catch (error) { + reject(new Error(`Failed to set image src: ${error}`)); + } + }); +}; + +// Simple proxy approach - just use the image directly without trying to extract frames +export const createStaticImageFromGif = async (url: string): Promise => { + return new Promise((resolve, reject) => { + const img = new Image(); + + img.onload = () => { + try { + const canvas = document.createElement('canvas'); + canvas.width = img.naturalWidth; + canvas.height = img.naturalHeight; + + const ctx = canvas.getContext('2d'); + if (!ctx) { + reject(new Error('Cannot get canvas context')); + return; + } + + // Clear and draw the image (this should get the first frame in most cases) + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(img, 0, 0); + + canvas.toBlob( + (blob) => { + if (blob) { + const objectUrl = URL.createObjectURL(blob); + resolve(objectUrl); + } else { + reject(new Error('Failed to create blob from canvas')); + } + }, + 'image/png', + 1.0, + ); + } catch (error) { + reject(new Error(`Canvas operation failed: ${error}`)); + } + }; + + img.onerror = () => { + reject(new Error(`Image load failed for URL: ${url}`)); + }; + + // Don't set crossOrigin for this approach to avoid CORS issues + img.src = url; + }); }; const useFetchGifFirstFrame = (url: string | undefined) => { const [frameUrl, setFrameUrl] = useState(null); + const [isLoading, setIsLoading] = useState(false); useEffect(() => { if (!url) { setFrameUrl(null); + setIsLoading(false); + return; + } + + // Skip frame extraction on Chromium browsers due to strict CORS policies + if (isChromium()) { + console.log('Skipping GIF frame extraction on Chromium browser due to CORS restrictions'); + setFrameUrl(null); + setIsLoading(false); return; } let isActive = true; + setIsLoading(true); + setFrameUrl(null); const fetchFrame = async () => { try { + // Check cache first const cachedFrame = await getCachedGifFrame(url); - if (cachedFrame) { + if (cachedFrame && isActive) { try { const response = await fetch(cachedFrame); if (response.ok) { - if (isActive) setFrameUrl(cachedFrame); + setFrameUrl(cachedFrame); + setIsLoading(false); return; } - } catch {} + } catch { + // Cache entry is invalid, continue with fresh extraction + } } - const blob = typeof url === 'string' ? await parseGif(await fetchImage(url)) : await parseGif(await readImage(url as File)); + // For Chrome CORS compatibility, try the simple image approach first + if (typeof url === 'string') { + try { + console.log('Trying simple image approach first...'); + const objectUrl = await createStaticImageFromGif(url); + + if (isActive) { + setFrameUrl(objectUrl); + setIsLoading(false); + await setCachedGifFrame(url, objectUrl); + } + return; // Success with simple approach + } catch (simpleError) { + console.log('Simple image approach failed, trying advanced extraction:', simpleError); + } + } + + // Fallback to advanced frame extraction (gifuct-js) + const buffer = typeof url === 'string' ? await fetchImage(url) : await readImage(url as File); + const blob = await extractFirstFrame(buffer); const objectUrl = URL.createObjectURL(blob); + if (isActive) { setFrameUrl(objectUrl); + setIsLoading(false); await setCachedGifFrame(url, objectUrl); } } catch (error) { console.error('Failed to load GIF frame:', error); - if (isActive) setFrameUrl(null); + + // Final fallback attempts (keep the existing fallback logic) + if (typeof url === 'string' && error instanceof Error && error.message.includes('XMLHttpRequest')) { + try { + console.log('Trying fallback fetch method...'); + const buffer = await fetchImageWithFetch(url); + const blob = await extractFirstFrame(buffer); + const objectUrl = URL.createObjectURL(blob); + + if (isActive) { + setFrameUrl(objectUrl); + setIsLoading(false); + await setCachedGifFrame(url, objectUrl); + } + return; // Success with fallback + } catch (fallbackError) { + console.error('All methods failed:', fallbackError); + } + } + + if (isActive) { + setFrameUrl(null); + setIsLoading(false); + } } }; @@ -103,7 +358,7 @@ const useFetchGifFirstFrame = (url: string | undefined) => { }; }, [url]); - return frameUrl; + return { frameUrl, isLoading }; }; export default useFetchGifFirstFrame; diff --git a/yarn.lock b/yarn.lock index 3d38f423..1f0e441e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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" @@ -8692,6 +8692,13 @@ getopts@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" resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-4.0.0.tgz#b212fd2bff9726d27c1283a1157e829490593285" @@ -10467,6 +10474,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" From 5bd47a5ae14bd63749946691f8f80ba2a04d4ec3 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Sat, 24 May 2025 21:58:46 +0200 Subject: [PATCH 06/15] use more accurate check for rpc connection --- src/components/sidebar/sidebar.tsx | 4 ++-- src/views/settings/plebbit-options/plebbit-options.tsx | 3 +-- src/views/subplebbit-settings/subplebbit-settings.tsx | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/sidebar/sidebar.tsx b/src/components/sidebar/sidebar.tsx index 2011c7c8..54a79cd9 100644 --- a/src/components/sidebar/sidebar.tsx +++ b/src/components/sidebar/sidebar.tsx @@ -2,7 +2,7 @@ import { useState, useEffect } from 'react'; import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import Plebbit from '@plebbit/plebbit-js'; -import { Comment, useAccount, useBlock, Role, Subplebbit, useSubplebbitStats, useAccountComment } from '@plebbit/plebbit-react-hooks'; +import { Comment, useAccount, useBlock, Role, Subplebbit, useSubplebbitStats, useAccountComment, usePlebbitRpcSettings } from '@plebbit/plebbit-react-hooks'; import styles from './sidebar.module.css'; import useIsSubplebbitOffline from '../../hooks/use-is-subplebbit-offline'; import useIsMobile from '../../hooks/use-is-mobile'; @@ -250,7 +250,7 @@ const Sidebar = ({ comment, isSubCreatedButNotYetPublished, settings, subplebbit } }, []); // Empty dependency array ensures this runs only once on mount - const isConnectedToRpc = !!account?.plebbitOptions.plebbitRpcClientsOptions; + const isConnectedToRpc = usePlebbitRpcSettings()?.state === 'succeeded'; const navigate = useNavigate(); const handleCreateCommunity = () => { // creating a community only works if the user is running a full node diff --git a/src/views/settings/plebbit-options/plebbit-options.tsx b/src/views/settings/plebbit-options/plebbit-options.tsx index ebfc2aa8..c7a20fa3 100644 --- a/src/views/settings/plebbit-options/plebbit-options.tsx +++ b/src/views/settings/plebbit-options/plebbit-options.tsx @@ -21,8 +21,7 @@ const IPFSGatewaysSettings = ({ ipfsGatewayUrlsRef, mediaIpfsGatewayUrlRef }: Se const account = useAccount(); const { plebbitOptions, mediaIpfsGatewayUrl } = account || {}; const { ipfsGatewayUrls } = plebbitOptions || {}; - const plebbitRpc = usePlebbitRpcSettings(); - const isConnectedToRpc = plebbitRpc?.state === 'succeeded'; + const isConnectedToRpc = usePlebbitRpcSettings()?.state === 'succeeded'; const ipfsGatewayUrlsDefaultValue = ipfsGatewayUrls?.join('\n'); return ( diff --git a/src/views/subplebbit-settings/subplebbit-settings.tsx b/src/views/subplebbit-settings/subplebbit-settings.tsx index 9c22137f..90430bf7 100644 --- a/src/views/subplebbit-settings/subplebbit-settings.tsx +++ b/src/views/subplebbit-settings/subplebbit-settings.tsx @@ -368,7 +368,7 @@ 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 === 'succeeded'; const isReadOnly = (!settings && isInSubplebbitSettingsView) || (!isConnectedToRpc && isInCreateSubplebbitView); From ca48aef8e041a5169ed3e66ae4b23d7dbbfd5f0e Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Sat, 24 May 2025 22:01:07 +0200 Subject: [PATCH 07/15] fix(author page): posts were incorrectly ranked --- src/components/post/post.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/post/post.tsx b/src/components/post/post.tsx index 88307db2..efdd9833 100644 --- a/src/components/post/post.tsx +++ b/src/components/post/post.tsx @@ -9,7 +9,7 @@ import { getHasThumbnail } from '../../lib/utils/media-utils'; import { getPostScore, formatScore } from '../../lib/utils/post-utils'; import { getFormattedTimeAgo, formatLocalizedUTCTimestamp } from '../../lib/utils/time-utils'; import { getHostname } from '../../lib/utils/url-utils'; -import { isAllView, isPendingPostView, isPostPageView, isProfileHiddenView, isProfileView, isSubplebbitView } from '../../lib/utils/view-utils'; +import { isAllView, isAuthorView, isPendingPostView, isPostPageView, isProfileHiddenView, isProfileView, isSubplebbitView } from '../../lib/utils/view-utils'; import { highlightMatchedText } from '../../lib/utils/pattern-utils'; import { usePinnedPostsStore } from '../../stores/use-pinned-posts-store'; import { useCommentMediaInfo } from '../../hooks/use-comment-media-info'; @@ -142,6 +142,7 @@ const Post = ({ index, post = {} }: PostProps) => { const isInPendingPostView = isPendingPostView(location.pathname, params); const isInPostPageView = isPostPageView(location.pathname, params); const isInProfileView = isProfileView(location.pathname); + const isInAuthorView = isAuthorView(location.pathname); const isInProfileHiddenView = isProfileHiddenView(location.pathname); const isInSubplebbitView = isSubplebbitView(location.pathname, params); @@ -202,7 +203,7 @@ const Post = ({ index, post = {} }: PostProps) => {
- {!isMobile && !isInProfileView && !isInPostPageView &&
{pinned ? undefined : rank}
} + {!isMobile && !isInProfileView && !isInAuthorView && !isInPostPageView &&
{pinned ? undefined : rank}
}
From ffcd28d1af06be7ed9e13a13c5a7c689adadc9a8 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Sat, 24 May 2025 22:04:03 +0200 Subject: [PATCH 08/15] state value from API changed --- src/components/sidebar/sidebar.tsx | 2 +- src/views/settings/plebbit-options/plebbit-options.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/sidebar/sidebar.tsx b/src/components/sidebar/sidebar.tsx index 54a79cd9..541db4bd 100644 --- a/src/components/sidebar/sidebar.tsx +++ b/src/components/sidebar/sidebar.tsx @@ -250,7 +250,7 @@ const Sidebar = ({ comment, isSubCreatedButNotYetPublished, settings, subplebbit } }, []); // Empty dependency array ensures this runs only once on mount - const isConnectedToRpc = usePlebbitRpcSettings()?.state === 'succeeded'; + const isConnectedToRpc = usePlebbitRpcSettings()?.state === 'connected'; const navigate = useNavigate(); const handleCreateCommunity = () => { // creating a community only works if the user is running a full node diff --git a/src/views/settings/plebbit-options/plebbit-options.tsx b/src/views/settings/plebbit-options/plebbit-options.tsx index c7a20fa3..51b12f9a 100644 --- a/src/views/settings/plebbit-options/plebbit-options.tsx +++ b/src/views/settings/plebbit-options/plebbit-options.tsx @@ -50,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 ( @@ -166,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 ( From 1808275d8acc467bf1939f51930df04e1f04448e Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Sun, 25 May 2025 22:21:42 +0200 Subject: [PATCH 09/15] feat(nsfw filter): ask user whether to auto-subscribe to nsfw subs when disabling filter --- src/components/topbar/topbar.tsx | 33 ++++++-- src/lib/utils/nsfw-subscription-utils.ts | 77 ++++++++++++++++++ .../content-options/content-options.tsx | 78 ++++++++++++++++--- 3 files changed, 174 insertions(+), 14 deletions(-) create mode 100644 src/lib/utils/nsfw-subscription-utils.ts diff --git a/src/components/topbar/topbar.tsx b/src/components/topbar/topbar.tsx index a7d710f0..cb6aca9d 100644 --- a/src/components/topbar/topbar.tsx +++ b/src/components/topbar/topbar.tsx @@ -5,10 +5,11 @@ import { useAccount, useAccountSubplebbits } from '@plebbit/plebbit-react-hooks' import Plebbit from '@plebbit/plebbit-js'; import { isAllView, isDomainView, isHomeView, isModView, isSubplebbitView } from '../../lib/utils/view-utils'; import useContentOptionsStore from '../../stores/use-content-options-store'; -import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits'; +import { useDefaultSubplebbitAddresses, useDefaultSubplebbits } from '../../hooks/use-default-subplebbits'; import useTimeFilter, { setSessionTimeFilterPreference } from '../../hooks/use-time-filter'; import { sortTypes } from '../../constants/sort-types'; import { sortLabels } from '../../constants/sort-labels'; +import { handleNSFWSubscriptionPrompt } from '../../lib/utils/nsfw-subscription-utils'; import styles from './topbar.module.css'; const CommunitiesDropdown = () => { @@ -54,6 +55,8 @@ const CommunitiesDropdown = () => { const TagFilterDropdown = () => { const { t } = useTranslation(); + const account = useAccount(); + const defaultSubplebbits = useDefaultSubplebbits(); const { hideAdultCommunities, hideGoreCommunities, @@ -80,18 +83,38 @@ const TagFilterDropdown = () => { const allHidden = hideAdultCommunities && hideGoreCommunities && hideAntiCommunities && hideVulgarCommunities; - const handleToggleAll = (event: React.MouseEvent) => { + const handleToggleAll = async (event: React.MouseEvent) => { event.stopPropagation(); const newState = !allHidden; + + if (!newState) { + await handleNSFWSubscriptionPrompt({ + account, + defaultSubplebbits, + tagsToShow: ['adult', 'gore', 'anti', 'vulgar'], + isShowingAll: true, + }); + } + setHideAdultCommunities(newState); setHideGoreCommunities(newState); setHideAntiCommunities(newState); setHideVulgarCommunities(newState); }; - const handleToggleTag = (event: React.MouseEvent, setter: (hide: boolean) => void, currentState: boolean) => { + const handleToggleTag = async (event: React.MouseEvent, setter: (hide: boolean) => void, currentState: boolean, tagName: string) => { event.stopPropagation(); - setter(!currentState); + const newState = !currentState; + + if (!newState) { + await handleNSFWSubscriptionPrompt({ + account, + defaultSubplebbits, + tagsToShow: [tagName], + }); + } + + setter(newState); }; useEffect(() => { @@ -115,7 +138,7 @@ const TagFilterDropdown = () => { {allHidden ? 'show all nsfw' : 'hide all nsfw'}
{tags.map((tag, index) => ( -
handleToggleTag(e, tag.setter, tag.isHidden)} style={{ cursor: 'pointer' }}> +
handleToggleTag(e, tag.setter, tag.isHidden, tag.name)} style={{ cursor: 'pointer' }}> {tag.isHidden ? 'show' : 'hide'} {tag.name} diff --git a/src/lib/utils/nsfw-subscription-utils.ts b/src/lib/utils/nsfw-subscription-utils.ts new file mode 100644 index 00000000..9d1ab533 --- /dev/null +++ b/src/lib/utils/nsfw-subscription-utils.ts @@ -0,0 +1,77 @@ +import { setAccount } from '@plebbit/plebbit-react-hooks'; + +const NSFW_SUBSCRIPTION_PROMPT_KEY_PREFIX = 'seedit-nsfw-subscription-prompt-shown-'; + +export interface NSFWSubscriptionOptions { + account: any; + defaultSubplebbits: any[]; + tagsToShow: string[]; + isShowingAll?: boolean; +} + +const getPromptKey = (tagsToShow: string[], isShowingAll: boolean): string => { + if (isShowingAll) { + return `${NSFW_SUBSCRIPTION_PROMPT_KEY_PREFIX}all`; + } + // Sort tags to ensure consistent key regardless of order + const sortedTags = [...tagsToShow].sort(); + return `${NSFW_SUBSCRIPTION_PROMPT_KEY_PREFIX}${sortedTags.join('-')}`; +}; + +export const shouldShowNSFWSubscriptionPrompt = (tagsToShow: string[], isShowingAll: boolean = false): boolean => { + const key = getPromptKey(tagsToShow, isShowingAll); + return !localStorage.getItem(key); +}; + +export const markNSFWSubscriptionPromptShown = (tagsToShow: string[], isShowingAll: boolean = false): void => { + const key = getPromptKey(tagsToShow, isShowingAll); + localStorage.setItem(key, 'true'); +}; + +export const getCommunitiesWithTags = (defaultSubplebbits: any[], tags: string[]): string[] => { + return defaultSubplebbits + .filter((sub) => { + const subTags = sub.tags || []; + return tags.some((tag) => subTags.includes(tag)); + }) + .map((sub) => sub.address); +}; + +export const handleNSFWSubscriptionPrompt = async ({ account, defaultSubplebbits, tagsToShow, isShowingAll = false }: NSFWSubscriptionOptions): Promise => { + if (!shouldShowNSFWSubscriptionPrompt(tagsToShow, isShowingAll) || !account || !defaultSubplebbits.length) { + return; + } + + const communitiesToSubscribe = getCommunitiesWithTags(defaultSubplebbits, tagsToShow); + + if (communitiesToSubscribe.length === 0) { + return; + } + + const tagText = isShowingAll + ? 'all NSFW communities' + : tagsToShow.length === 1 + ? `communities tagged as "${tagsToShow[0]}"` + : `communities tagged as: ${tagsToShow.join(', ')}`; + + const confirmMessage = `You're now showing ${tagText}. Would you like to subscribe to these communities? This will add them to your subscriptions.`; + + const shouldSubscribe = window.confirm(confirmMessage); + + // Mark as shown regardless of user choice + markNSFWSubscriptionPromptShown(tagsToShow, isShowingAll); + + if (shouldSubscribe) { + try { + const currentSubscriptions = account.subscriptions || []; + const newSubscriptions = [...new Set([...currentSubscriptions, ...communitiesToSubscribe])]; + + await setAccount({ + ...account, + subscriptions: newSubscriptions, + }); + } catch (error) { + console.error('Error subscribing to NSFW communities:', error); + } + } +}; diff --git a/src/views/settings/content-options/content-options.tsx b/src/views/settings/content-options/content-options.tsx index 2abc0138..54ad0c13 100644 --- a/src/views/settings/content-options/content-options.tsx +++ b/src/views/settings/content-options/content-options.tsx @@ -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(); @@ -134,6 +137,8 @@ const MediaOptions = () => { const CommunitiesOptions = () => { const { t } = useTranslation(); + const account = useAccount(); + const defaultSubplebbits = useDefaultSubplebbits(); const { hideAdultCommunities, hideGoreCommunities, @@ -163,8 +168,19 @@ const CommunitiesOptions = () => { el.indeterminate = someHidden && !allHidden; } }} - onChange={(e) => { + onChange={async (e) => { const newValue = e.target.checked; + + // If showing (newValue = false), handle subscription prompt + if (!newValue) { + await handleNSFWSubscriptionPrompt({ + account, + defaultSubplebbits, + tagsToShow: ['adult', 'gore', 'anti', 'vulgar'], + isShowingAll: true, + }); + } + setHideAdultCommunities(newValue); setHideGoreCommunities(newValue); setHideAntiCommunities(newValue); @@ -178,8 +194,19 @@ const CommunitiesOptions = () => { { - setHideAdultCommunities(e.target.checked); + onChange={async (e) => { + const newValue = e.target.checked; + + // If showing (newValue = false), handle subscription prompt + if (!newValue) { + await handleNSFWSubscriptionPrompt({ + account, + defaultSubplebbits, + tagsToShow: ['adult'], + }); + } + + setHideAdultCommunities(newValue); }} /> {t('tagged_as_adult')} @@ -190,8 +217,19 @@ const CommunitiesOptions = () => { { - setHideGoreCommunities(e.target.checked); + onChange={async (e) => { + const newValue = e.target.checked; + + // If showing (newValue = false), handle subscription prompt + if (!newValue) { + await handleNSFWSubscriptionPrompt({ + account, + defaultSubplebbits, + tagsToShow: ['gore'], + }); + } + + setHideGoreCommunities(newValue); }} /> {t('tagged_as_gore')} @@ -202,8 +240,19 @@ const CommunitiesOptions = () => { { - setHideAntiCommunities(e.target.checked); + onChange={async (e) => { + const newValue = e.target.checked; + + // If showing (newValue = false), handle subscription prompt + if (!newValue) { + await handleNSFWSubscriptionPrompt({ + account, + defaultSubplebbits, + tagsToShow: ['anti'], + }); + } + + setHideAntiCommunities(newValue); }} /> {t('tagged_as_anti')} @@ -214,8 +263,19 @@ const CommunitiesOptions = () => { { - setHideVulgarCommunities(e.target.checked); + onChange={async (e) => { + const newValue = e.target.checked; + + // If showing (newValue = false), handle subscription prompt + if (!newValue) { + await handleNSFWSubscriptionPrompt({ + account, + defaultSubplebbits, + tagsToShow: ['vulgar'], + }); + } + + setHideVulgarCommunities(newValue); }} /> {t('tagged_as_vulgar')} From 35cc4bb48f543ff94971d943783e3195d5112d9d Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Sun, 25 May 2025 22:30:43 +0200 Subject: [PATCH 10/15] update API value, redirect from restricted view --- src/views/settings/plebbit-options/plebbit-options.tsx | 2 +- src/views/subplebbit-settings/subplebbit-settings.tsx | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/views/settings/plebbit-options/plebbit-options.tsx b/src/views/settings/plebbit-options/plebbit-options.tsx index 51b12f9a..6b7737a6 100644 --- a/src/views/settings/plebbit-options/plebbit-options.tsx +++ b/src/views/settings/plebbit-options/plebbit-options.tsx @@ -21,7 +21,7 @@ const IPFSGatewaysSettings = ({ ipfsGatewayUrlsRef, mediaIpfsGatewayUrlRef }: Se const account = useAccount(); const { plebbitOptions, mediaIpfsGatewayUrl } = account || {}; const { ipfsGatewayUrls } = plebbitOptions || {}; - const isConnectedToRpc = usePlebbitRpcSettings()?.state === 'succeeded'; + const isConnectedToRpc = usePlebbitRpcSettings()?.state === 'connected'; const ipfsGatewayUrlsDefaultValue = ipfsGatewayUrls?.join('\n'); return ( diff --git a/src/views/subplebbit-settings/subplebbit-settings.tsx b/src/views/subplebbit-settings/subplebbit-settings.tsx index 90430bf7..4d2df8fe 100644 --- a/src/views/subplebbit-settings/subplebbit-settings.tsx +++ b/src/views/subplebbit-settings/subplebbit-settings.tsx @@ -368,7 +368,11 @@ const SubplebbitSettings = () => { const params = useParams(); const isInCreateSubplebbitView = isCreateSubplebbitView(location.pathname); const isInSubplebbitSettingsView = isSubplebbitSettingsView(location.pathname, params); - const isConnectedToRpc = usePlebbitRpcSettings()?.state === 'succeeded'; + const isConnectedToRpc = usePlebbitRpcSettings()?.state === 'connected'; + + if (isInCreateSubplebbitView && !isConnectedToRpc) { + navigate('/', { replace: true }); + } const isReadOnly = (!settings && isInSubplebbitSettingsView) || (!isConnectedToRpc && isInCreateSubplebbitView); From 045f64c7c37d261f681923fb042bcb2783326fc6 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Mon, 26 May 2025 14:45:29 +0200 Subject: [PATCH 11/15] chore(package.json): upgrade plebbit-react-hooks --- package.json | 2 +- yarn.lock | 125 +++++++++------------------------------------------ 2 files changed, 23 insertions(+), 104 deletions(-) diff --git a/package.json b/package.json index e8edc420..9c765615 100755 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "@capacitor/app": "7.0.1", "@capacitor/local-notifications": "7.0.1", "@floating-ui/react": "0.26.1", - "@plebbit/plebbit-react-hooks": "https://github.com/plebbit/plebbit-react-hooks.git#f760ce2f5961307c93517fed0257b425bbf38c30", + "@plebbit/plebbit-react-hooks": "https://github.com/plebbit/plebbit-react-hooks.git#19a31e62db24e47994ed8c076b05a83a8fa3dcfc", "@testing-library/jest-dom": "5.14.1", "@testing-library/react": "13.0.0", "@testing-library/user-event": "13.2.1", diff --git a/yarn.lock b/yarn.lock index 1f0e441e..e0288ee9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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,11 @@ 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#19a31e62db24e47994ed8c076b05a83a8fa3dcfc": version "0.0.1" - resolved "https://github.com/plebbit/plebbit-react-hooks.git#f760ce2f5961307c93517fed0257b425bbf38c30" + resolved "https://github.com/plebbit/plebbit-react-hooks.git#19a31e62db24e47994ed8c076b05a83a8fa3dcfc" 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" @@ -5525,10 +5523,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 +6294,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 +6311,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 +7972,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 +8623,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,12 +8665,7 @@ 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: +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== @@ -9475,11 +9448,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" @@ -10624,10 +10592,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" @@ -10638,13 +10606,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" @@ -10660,26 +10621,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" @@ -12705,11 +12646,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" @@ -13289,13 +13225,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" @@ -13468,7 +13397,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== @@ -14544,11 +14473,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" @@ -14634,11 +14558,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" From 5e3bf59387024a3f902735ca563d0abd4a012e75 Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Thu, 29 May 2025 11:58:24 +0200 Subject: [PATCH 12/15] feat(communities page): filter by nsfw communities by clicking the "18+" label, filter in each --- public/translations/ar/default.json | 3 +- public/translations/bn/default.json | 3 +- public/translations/cs/default.json | 3 +- public/translations/da/default.json | 3 +- public/translations/de/default.json | 3 +- public/translations/el/default.json | 3 +- public/translations/en/default.json | 3 +- public/translations/es/default.json | 3 +- public/translations/fa/default.json | 3 +- public/translations/fi/default.json | 3 +- public/translations/fil/default.json | 3 +- public/translations/fr/default.json | 3 +- public/translations/he/default.json | 3 +- public/translations/hi/default.json | 3 +- public/translations/hu/default.json | 3 +- public/translations/id/default.json | 3 +- public/translations/it/default.json | 3 +- public/translations/ja/default.json | 3 +- public/translations/ko/default.json | 3 +- public/translations/mr/default.json | 3 +- public/translations/nl/default.json | 3 +- public/translations/no/default.json | 3 +- public/translations/pl/default.json | 3 +- public/translations/pt/default.json | 3 +- public/translations/ro/default.json | 3 +- public/translations/ru/default.json | 3 +- public/translations/sq/default.json | 3 +- public/translations/sv/default.json | 3 +- public/translations/te/default.json | 3 +- public/translations/th/default.json | 3 +- public/translations/tr/default.json | 3 +- public/translations/uk/default.json | 3 +- public/translations/ur/default.json | 3 +- public/translations/vi/default.json | 3 +- public/translations/zh/default.json | 3 +- src/app.tsx | 1 - src/hooks/use-default-subplebbits.ts | 1 + src/views/subplebbits/subplebbits.tsx | 137 +++++++++++++++++++------- 38 files changed, 172 insertions(+), 72 deletions(-) diff --git a/public/translations/ar/default.json b/public/translations/ar/default.json index 94428653..f59598bf 100644 --- a/public/translations/ar/default.json +++ b/public/translations/ar/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/bn/default.json b/public/translations/bn/default.json index 83a6a2fe..2ab9e8a6 100644 --- a/public/translations/bn/default.json +++ b/public/translations/bn/default.json @@ -390,5 +390,6 @@ "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 কমিউনিটি দ্বারা ফিল্টারিং" } \ No newline at end of file diff --git a/public/translations/cs/default.json b/public/translations/cs/default.json index 9c729ab5..a7c1db87 100644 --- a/public/translations/cs/default.json +++ b/public/translations/cs/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/da/default.json b/public/translations/da/default.json index 2daef35b..aa6727dc 100644 --- a/public/translations/da/default.json +++ b/public/translations/da/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/de/default.json b/public/translations/de/default.json index f3ad4f44..9c497000 100644 --- a/public/translations/de/default.json +++ b/public/translations/de/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/el/default.json b/public/translations/el/default.json index 68027709..99d6f345 100644 --- a/public/translations/el/default.json +++ b/public/translations/el/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/en/default.json b/public/translations/en/default.json index c2319dda..be809c5c 100644 --- a/public/translations/en/default.json +++ b/public/translations/en/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/es/default.json b/public/translations/es/default.json index df09e179..58b4d6b8 100644 --- a/public/translations/es/default.json +++ b/public/translations/es/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/fa/default.json b/public/translations/fa/default.json index a8cecfc3..91fa4cf1 100644 --- a/public/translations/fa/default.json +++ b/public/translations/fa/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/fi/default.json b/public/translations/fi/default.json index e77c6419..1353a144 100644 --- a/public/translations/fi/default.json +++ b/public/translations/fi/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/fil/default.json b/public/translations/fil/default.json index 1289a4e4..e455f730 100644 --- a/public/translations/fil/default.json +++ b/public/translations/fil/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/fr/default.json b/public/translations/fr/default.json index db27407c..516329fc 100644 --- a/public/translations/fr/default.json +++ b/public/translations/fr/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/he/default.json b/public/translations/he/default.json index 105625cc..adde7e01 100644 --- a/public/translations/he/default.json +++ b/public/translations/he/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/hi/default.json b/public/translations/hi/default.json index d5dbbf89..94f14dbc 100644 --- a/public/translations/hi/default.json +++ b/public/translations/hi/default.json @@ -390,5 +390,6 @@ "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 समुदायों द्वारा फ़िल्टरिंग" } \ No newline at end of file diff --git a/public/translations/hu/default.json b/public/translations/hu/default.json index 8466f4ea..e8e56165 100644 --- a/public/translations/hu/default.json +++ b/public/translations/hu/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/id/default.json b/public/translations/id/default.json index 01708683..ecbe0750 100644 --- a/public/translations/id/default.json +++ b/public/translations/id/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/it/default.json b/public/translations/it/default.json index e19d4201..782d9edc 100644 --- a/public/translations/it/default.json +++ b/public/translations/it/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/ja/default.json b/public/translations/ja/default.json index ee248dd3..65dbe475 100644 --- a/public/translations/ja/default.json +++ b/public/translations/ja/default.json @@ -390,5 +390,6 @@ "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 コミュニティによるフィルタリング" } \ No newline at end of file diff --git a/public/translations/ko/default.json b/public/translations/ko/default.json index 65bf28be..d830fef7 100644 --- a/public/translations/ko/default.json +++ b/public/translations/ko/default.json @@ -390,5 +390,6 @@ "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 커뮤니티별 필터링" } \ No newline at end of file diff --git a/public/translations/mr/default.json b/public/translations/mr/default.json index 71cfaf12..86014293 100644 --- a/public/translations/mr/default.json +++ b/public/translations/mr/default.json @@ -390,5 +390,6 @@ "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 समुदायांद्वारे फिल्टरिंग" } \ No newline at end of file diff --git a/public/translations/nl/default.json b/public/translations/nl/default.json index 2c1a7817..0180da65 100644 --- a/public/translations/nl/default.json +++ b/public/translations/nl/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/no/default.json b/public/translations/no/default.json index ba4d8234..9a0e9eed 100644 --- a/public/translations/no/default.json +++ b/public/translations/no/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/pl/default.json b/public/translations/pl/default.json index 95c77146..d9c59368 100644 --- a/public/translations/pl/default.json +++ b/public/translations/pl/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/pt/default.json b/public/translations/pt/default.json index 813554d9..691f78f7 100644 --- a/public/translations/pt/default.json +++ b/public/translations/pt/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/ro/default.json b/public/translations/ro/default.json index 131988e4..d43e16c2 100644 --- a/public/translations/ro/default.json +++ b/public/translations/ro/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/ru/default.json b/public/translations/ru/default.json index d01a7b37..e08e8723 100644 --- a/public/translations/ru/default.json +++ b/public/translations/ru/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/sq/default.json b/public/translations/sq/default.json index a9d53d1f..e2016fe6 100644 --- a/public/translations/sq/default.json +++ b/public/translations/sq/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/sv/default.json b/public/translations/sv/default.json index 3d40276d..b69725b3 100644 --- a/public/translations/sv/default.json +++ b/public/translations/sv/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/te/default.json b/public/translations/te/default.json index 851035d4..92948bfd 100644 --- a/public/translations/te/default.json +++ b/public/translations/te/default.json @@ -390,5 +390,6 @@ "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 సముదాయాల ద్వారా ఫిల్టరింగ్" } \ No newline at end of file diff --git a/public/translations/th/default.json b/public/translations/th/default.json index 28953529..27be9a45 100644 --- a/public/translations/th/default.json +++ b/public/translations/th/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/tr/default.json b/public/translations/tr/default.json index 50b76569..10ab7f16 100644 --- a/public/translations/tr/default.json +++ b/public/translations/tr/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/uk/default.json b/public/translations/uk/default.json index 46c4ce31..478d785b 100644 --- a/public/translations/uk/default.json +++ b/public/translations/uk/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/ur/default.json b/public/translations/ur/default.json index 0c6663cf..696a2510 100644 --- a/public/translations/ur/default.json +++ b/public/translations/ur/default.json @@ -390,5 +390,6 @@ "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 کمیونٹیز کے ذریعے فلٹرنگ" } \ No newline at end of file diff --git a/public/translations/vi/default.json b/public/translations/vi/default.json index a94ce6f4..15b15b44 100644 --- a/public/translations/vi/default.json +++ b/public/translations/vi/default.json @@ -390,5 +390,6 @@ "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" } \ No newline at end of file diff --git a/public/translations/zh/default.json b/public/translations/zh/default.json index 900528d0..56b118b4 100644 --- a/public/translations/zh/default.json +++ b/public/translations/zh/default.json @@ -390,5 +390,6 @@ "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社区进行过滤" } \ No newline at end of file diff --git a/src/app.tsx b/src/app.tsx index ae1bd1f1..87f8fa66 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -118,7 +118,6 @@ const App = () => { } /> } /> } /> - } /> } /> diff --git a/src/hooks/use-default-subplebbits.ts b/src/hooks/use-default-subplebbits.ts index 5222b6b7..0d9b1702 100644 --- a/src/hooks/use-default-subplebbits.ts +++ b/src/hooks/use-default-subplebbits.ts @@ -15,6 +15,7 @@ export interface MultisubSubplebbit { tags?: string[]; features?: string[]; seeditAutoSubscribe?: boolean; + plebchanAutoSubscribe?: boolean; lowUptime?: boolean; } diff --git a/src/views/subplebbits/subplebbits.tsx b/src/views/subplebbits/subplebbits.tsx index 35968681..7e513a21 100644 --- a/src/views/subplebbits/subplebbits.tsx +++ b/src/views/subplebbits/subplebbits.tsx @@ -127,20 +127,18 @@ const Infobar = () => { const subscriptions = account?.subscriptions || []; const { t } = useTranslation(); const location = useLocation(); - const defaultSubplebbits = useDefaultSubplebbits(); - const validTags = useDefaultSubplebbitTags(defaultSubplebbits); const isInSubplebbitsSubscriberView = isSubplebbitsSubscriberView(location.pathname); const isInSubplebbitsModeratorView = isSubplebbitsModeratorView(location.pathname); const isInSubplebbitsAdminView = isSubplebbitsAdminView(location.pathname); const isInSubplebbitsOwnerView = isSubplebbitsOwnerView(location.pathname); - // Add tag check - const urlTag = location.pathname.includes('/tag/') ? location.pathname.split('/').pop() : undefined; - const currentTag = urlTag && validTags.includes(urlTag) ? urlTag : undefined; + // Check if we're filtering by any tag + const urlParams = new URLSearchParams(location.search); + const currentTag = urlParams.get('tag'); - // Get base path without tag - const basePath = location.pathname.split('/tag/')[0]; + // Get base path without search params + const basePath = location.pathname; let mainInfobarText; if (isInSubplebbitsSubscriberView) { @@ -162,7 +160,7 @@ const Infobar = () => {
{currentTag && (
- {t('filtering_by_tag', { tag: currentTag })} —{' '} + {currentTag === 'nsfw' ? t('filtering_by_nsfw') + ' ("adult", "gore", "vulgar", "anti") —' : t('filtering_by_tag', { tag: currentTag }) + ' —'}{' '} {t('undo')} @@ -177,6 +175,7 @@ const Subplebbit = ({ subplebbit, tags, index, isUnsubscribed, onUnsubscribe }: const { address, createdAt, description, roles, shortAddress, settings, suggested, title } = subplebbit || {}; const [avatarLoadFailed, setAvatarLoadFailed] = useState(false); const showSprout = !suggested?.avatarUrl || avatarLoadFailed; + const location = useLocation(); useEffect(() => { setAvatarLoadFailed(false); @@ -187,6 +186,11 @@ const Subplebbit = ({ subplebbit, tags, index, isUnsubscribed, onUnsubscribe }: const account = useAccount(); const userRole = roles?.[account?.author?.address]?.role; + const getTagFilterRoute = (tag: string) => { + const pathname = location.pathname; + return `${pathname}?tag=${encodeURIComponent(tag)}`; + }; + // TODO: make arrows functional when token voting is implemented in the API const upvoted = false; const downvoted = false; @@ -276,13 +280,17 @@ const Subplebbit = ({ subplebbit, tags, index, isUnsubscribed, onUnsubscribe }: )} - {isNsfw && } + {isNsfw && ( + + + + )} {isOffline && !isOnlineStatusLoading &&