feat: rename plebchan to 5chan

This commit is contained in:
plebeius
2025-10-17 23:30:35 +02:00
parent e1f1f280cc
commit 1360dd8348
71 changed files with 316 additions and 316 deletions
+7 -7
View File
@@ -13,7 +13,7 @@ import CommentMedia from '../comment-media';
import styles from './markdown.module.css';
import { Link, useLocation, useParams } from 'react-router-dom';
import { canEmbed } from '../embed';
import { isPlebchanLink, transformPlebchanLinkToInternal, preprocessPlebchanPatterns } from '../../lib/utils/url-utils';
import { is5chanLink, transform5chanLinkToInternal, preprocess5chanPatterns } from '../../lib/utils/url-utils';
interface ContentLinkEmbedProps {
children: any;
@@ -159,9 +159,9 @@ const renderAnchorLink = (children: React.ReactNode, href: string) => {
return <span>{children}</span>;
}
// Check if this is a valid plebchan link that should be handled internally
if (isPlebchanLink(href)) {
const internalPath = transformPlebchanLinkToInternal(href);
// Check if this is a valid 5chan link that should be handled internally
if (is5chanLink(href)) {
const internalPath = transform5chanLinkToInternal(href);
if (internalPath) {
// Check if the link text should be replaced with the internal path
let shouldReplaceText = false;
@@ -182,7 +182,7 @@ const renderAnchorLink = (children: React.ReactNode, href: string) => {
return <Link to={internalPath}>{displayText}</Link>;
} else {
console.warn('Failed to transform plebchan link to internal path:', href);
console.warn('Failed to transform 5chan link to internal path:', href);
return <Link to={href}>{children}</Link>;
}
}
@@ -226,8 +226,8 @@ const Markdown = ({ content, title }: MarkdownProps) => {
const isInCatalogView = isCatalogView(useLocation().pathname, useParams());
// Preprocess content to convert plain text plebchan patterns to markdown links
const processedContent = preprocessPlebchanPatterns(content || '');
// Preprocess content to convert plain text 5chan patterns to markdown links
const processedContent = preprocess5chanPatterns(content || '');
return (
<span className={styles.markdown}>
@@ -22,21 +22,21 @@ const CheckForUpdates = () => {
const checkForUpdates = async () => {
try {
setLoading(true);
const packageRes = await fetch('https://raw.githubusercontent.com/plebbit/plebchan/master/package.json', { cache: 'no-cache' });
const packageRes = await fetch('https://raw.githubusercontent.com/plebbit/5chan/master/package.json', { cache: 'no-cache' });
const packageData = await packageRes.json();
let updateAvailable = false;
if (packageJson.version !== packageData.version) {
const newVersionText = t('new_stable_version', { newVersion: packageData.version, oldVersion: packageJson.version });
const updateActionText = isElectron
? t('download_latest_desktop', { link: 'https://github.com/plebbit/plebchan/releases/latest', interpolation: { escapeValue: false } })
? t('download_latest_desktop', { link: 'https://github.com/plebbit/5chan/releases/latest', interpolation: { escapeValue: false } })
: t('refresh_to_update');
alert(newVersionText + ' ' + updateActionText);
updateAvailable = true;
}
if (commitRef && commitRef.length > 0) {
const commitRes = await fetch('https://api.github.com/repos/plebbit/plebchan/commits?per_page=1&sha=development', { cache: 'no-cache' });
const commitRes = await fetch('https://api.github.com/repos/plebbit/5chan/commits?per_page=1&sha=development', { cache: 'no-cache' });
const commitData = await commitRes.json();
const latestCommitHash = commitData[0].sha;
@@ -52,7 +52,7 @@ const CheckForUpdates = () => {
if (!updateAvailable) {
alert(
commitRef
? `${t('latest_development_version', { commit: commitRef.slice(0, 7), link: 'https://plebchan.app/#/', interpolation: { escapeValue: false } })}`
? `${t('latest_development_version', { commit: commitRef.slice(0, 7), link: 'https://5chan.app/#/', interpolation: { escapeValue: false } })}`
: `${t('latest_stable_version', { version: packageJson.version })}`,
);
}
+1 -1
View File
@@ -109,7 +109,7 @@ const TopBarDesktop = () => {
)}
[
<span className={styles.temporaryButton}>
<a href='https://plebbit.github.io/docs/learn/clients/plebchan/create-a-board' target='_blank' rel='noopener noreferrer'>
<a href='https://plebbit.github.io/docs/learn/clients/5chan/create-a-board' target='_blank' rel='noopener noreferrer'>
{t('create_board')}
</a>
</span>
+1 -1
View File
@@ -10,7 +10,7 @@ const Version = () => {
return (
<>
<a
href={commitRef ? `https://github.com/plebbit/plebchan/commit/${commitRef}` : `https://github.com/plebbit/plebchan/releases/tag/v${version}`}
href={commitRef ? `https://github.com/plebbit/5chan/commit/${commitRef}` : `https://github.com/plebbit/5chan/releases/tag/v${version}`}
target='_blank'
rel='noopener noreferrer'
>
+1 -1
View File
@@ -1,2 +1,2 @@
// plebchan shouldn't consider 'vulgar' or 'anti' as nsfw tags, unlike more sfw-oriented clients
// 5chan shouldn't consider 'vulgar' or 'anti' as nsfw tags, unlike more sfw-oriented clients
export const nsfwTags = ['adult', 'gore'];
+1 -1
View File
@@ -3,7 +3,7 @@ import { useAccount, setAccount } from '@plebbit/plebbit-react-hooks';
import { getAutoSubscribeAddresses, useDefaultSubplebbits } from './use-default-subplebbits';
import { useAutoSubscribeStore } from '../stores/use-auto-subscribe-store';
const AUTO_SUBSCRIBE_KEY_PREFIX = 'plebchan-auto-subscribe-done-';
const AUTO_SUBSCRIBE_KEY_PREFIX = '5chan-auto-subscribe-done-';
// Keep track of which accounts have been processed globally
const processedAccounts = new Set<string>();
+2 -2
View File
@@ -12,7 +12,7 @@ export interface MultisubSubplebbit {
address: string;
tags?: string[];
features?: string[];
plebchanAutoSubscribe?: boolean;
autoSubscribe5Chan?: boolean;
lowUptime?: boolean;
}
@@ -58,7 +58,7 @@ export const useDefaultSubplebbits = () => {
// Cache auto-subscribe addresses when we fetch subplebbits
cacheAutoSubscribeAddresses = filteredSubplebbits
.filter((sub: MultisubSubplebbit) => sub.plebchanAutoSubscribe && sub.address)
.filter((sub: MultisubSubplebbit) => sub.autoSubscribe5Chan && sub.address)
.map((sub: MultisubSubplebbit) => sub.address);
setState({
+1 -1
View File
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
import localForageLru from '@plebbit/plebbit-react-hooks/dist/lib/localforage-lru/index.js';
const gifFrameDb = localForageLru.createInstance({ name: 'plebchanGifFrames', size: 500 });
const gifFrameDb = localForageLru.createInstance({ name: '5chanGifFrames', size: 500 });
const getCachedGifFrame = async (url: string): Promise<string | null> => {
return await gifFrameDb.getItem(url);
+2 -2
View File
@@ -2,11 +2,11 @@ import assert from 'assert';
import { useParams } from 'react-router-dom';
// the timestamp the last time the user visited
const lastVisitTimestamp = localStorage.getItem('plebchanLastVisitTimestamp');
const lastVisitTimestamp = localStorage.getItem('5chanLastVisitTimestamp');
// update the last visited timestamp every n seconds
setInterval(() => {
localStorage.setItem('plebchanLastVisitTimestamp', Date.now().toString());
localStorage.setItem('5chanLastVisitTimestamp', Date.now().toString());
}, 60 * 1000);
const timeFilterNamesToSeconds: Record<string, number | undefined> = {
+1 -1
View File
@@ -245,7 +245,7 @@ export const getMediaDimensions = memoize(
{ max: 1000 },
);
const thumbnailUrlsDb = localForageLru.createInstance({ name: 'plebchanThumbnailUrls', size: 500 });
const thumbnailUrlsDb = localForageLru.createInstance({ name: '5chanThumbnailUrls', size: 500 });
export const getCachedThumbnail = async (url: string): Promise<string | null> => {
return await thumbnailUrlsDb.getItem(url);
+12 -12
View File
@@ -18,19 +18,19 @@ export const isValidURL = (url: string) => {
};
export const copyShareLinkToClipboard = async (subplebbitAddress: string, cid: string) => {
const shareLink = `https://pleb.bz/p/${subplebbitAddress}/c/${cid}?redirect=plebchan.app`;
const shareLink = `https://pleb.bz/p/${subplebbitAddress}/c/${cid}?redirect=5chan.app`;
await copyToClipboard(shareLink);
};
const PLEBCHAN_HOSTNAMES = ['pleb.bz', 'plebchan.app', 'plebchan.eth.limo', 'plebchan.eth.link', 'plebchan.eth.sucks', 'plebchan.netlify.app'];
const CHAN_5_HOSTNAMES = ['pleb.bz', '5chan.app', '5chan.eth.limo', '5chan.eth.link', '5chan.eth.sucks', '5chan.netlify.app'];
// Check if a URL is a valid plebchan link that should be handled internally
export const isPlebchanLink = (url: string): boolean => {
// Check if a URL is a valid 5chan link that should be handled internally
export const is5chanLink = (url: string): boolean => {
try {
const parsedUrl = new URL(url);
const hostname = parsedUrl.hostname.replace(/^www\./, '');
if (!PLEBCHAN_HOSTNAMES.includes(hostname)) {
if (!CHAN_5_HOSTNAMES.includes(hostname)) {
return false;
}
@@ -49,7 +49,7 @@ export const isPlebchanLink = (url: string): boolean => {
return /^\/p\/[^/]+\/c\/[^/]+$/.test(routePath);
}
// For other plebchan hostnames, support:
// For other 5chan hostnames, support:
// - /p/{subplebbitAddress}
// - /p/{subplebbitAddress}/c/{commentCid}
return /^\/p\/[^/]+(\/c\/[^/]+)?$/.test(routePath);
@@ -58,9 +58,9 @@ export const isPlebchanLink = (url: string): boolean => {
}
};
// Transform a valid plebchan URL to an internal route
export const transformPlebchanLinkToInternal = (url: string): string | null => {
if (!isPlebchanLink(url)) {
// Transform a valid 5chan URL to an internal route
export const transform5chanLinkToInternal = (url: string): string | null => {
if (!is5chanLink(url)) {
return null;
}
@@ -97,7 +97,7 @@ const isValidDomain = (str: string): boolean => {
return str.includes('.') && str.split('.').length >= 2 && str.split('.').every((part) => part.length > 0);
};
// Check if a plain text pattern is a valid plebchan subplebbit reference
// Check if a plain text pattern is a valid 5chan subplebbit reference
export const isValidSubplebbitPattern = (pattern: string): boolean => {
// Must start with "p/"
if (!pattern.startsWith('p/')) {
@@ -118,8 +118,8 @@ export const isValidSubplebbitPattern = (pattern: string): boolean => {
return isValidDomain(pathPart) || isValidIPNSKey(pathPart);
};
// Preprocess content to convert plain text plebchan patterns to markdown links
export const preprocessPlebchanPatterns = (content: string): string => {
// Preprocess content to convert plain text 5chan patterns to markdown links
export const preprocess5chanPatterns = (content: string): string => {
// Pattern to match "p/something" or "p/something/c/something"
// Negative lookbehind prevents matching patterns that are already part of URLs
const pattern = /(?<!https?:\/\/[^\s]*)\bp\/([a-zA-Z0-9\-.]+(?:\/c\/[a-zA-Z0-9]{10,100})?)[.,:;!?]*/g;
+1 -1
View File
@@ -266,7 +266,7 @@ const Board = () => {
useEffect(() => {
const boardTitle = title ? title : shortAddress || subplebbitAddress;
document.title = boardTitle + ' - plebchan';
document.title = boardTitle + ' - 5chan';
}, [title, shortAddress, subplebbitAddress]);
const shouldShowErrorToUser = error?.message && feed.length === 0;
+1 -1
View File
@@ -450,7 +450,7 @@ const Catalog = () => {
let documentTitle = title ? title : shortAddress;
if (isInAllView) documentTitle = t('all');
else if (isInSubscriptionsView) documentTitle = t('subscriptions');
document.title = documentTitle + ` - ${t('catalog')} - plebchan`;
document.title = documentTitle + ` - ${t('catalog')} - 5chan`;
}, [title, shortAddress, isInAllView, isInSubscriptionsView, t]);
// Clear matched filters when component mounts or when subplebbit changes
+26 -26
View File
@@ -6,7 +6,7 @@ import styles from './faq.module.css';
const FAQ = () => {
useEffect(() => {
window.scrollTo(0, 0);
document.title = 'FAQ - plebchan';
document.title = 'FAQ - 5chan';
}, []);
return (
@@ -18,8 +18,8 @@ const FAQ = () => {
<h2>Frequently Asked Questions</h2>
</div>
<div className={styles.boxContent}>
Welcome to Plebchan's <strong>F</strong>requently <strong>A</strong>sked <strong>Q</strong>uestions page. Please remember that Plebchan does <i>not</i> have
global admins or rules.
Welcome to 5chan's <strong>F</strong>requently <strong>A</strong>sked <strong>Q</strong>uestions page. Please remember that 5chan does <i>not</i> have global
admins or rules.
</div>
</div>
<div className={styles.columns}>
@@ -35,7 +35,7 @@ const FAQ = () => {
</strong>
<ul>
<li>
<HashLink to='#whatplebchan'>What is Plebchan?</HashLink>
<HashLink to='#what5chan'>What is 5chan?</HashLink>
</li>
<ul>
<li>
@@ -82,11 +82,11 @@ const FAQ = () => {
</div>
<div className={styles.boxContent}>
<dl>
<dt className={styles.first} id='whatplebchan'>
What is Plebchan?
<dt className={styles.first} id='what5chan'>
What is 5chan?
</dt>
<dd>
Plebchan is a serverless, adminless, decentralized 4chan alternative where any pleb can create and own unlimited boards. All data comes from the Plebbit
5chan is a serverless, adminless, decentralized 4chan alternative where any pleb can create and own unlimited boards. All data comes from the Plebbit
protocol, it's all text including links from which media is embedded, shared peer-to-peer. Users do not need to register an account before participating
in the community.
</dd>
@@ -106,9 +106,9 @@ const FAQ = () => {
To post as "Anonymous", simply do not fill in the [Name] field when submitting content.
<br />
<br />
Plebchan uses the Plebbit protocol to function, which does not leak IP addresses of people who post. When you post on Plebchan, no board admin can know
your IP address, nor can the app itself, which is just static HTML. However, the Plebbit protocol is not fully anonymous, it uses IPFS, which means your
IP address is part of a public P2P swarm, similarly to how BitTorrent works.
5chan uses the Plebbit protocol to function, which does not leak IP addresses of people who post. When you post on 5chan, no board admin can know your
IP address, nor can the app itself, which is just static HTML. However, the Plebbit protocol is not fully anonymous, it uses IPFS, which means your IP
address is part of a public P2P swarm, similarly to how BitTorrent works.
</dd>
<dt id='register'>Can I register a username?</dt>
<dd>
@@ -123,16 +123,16 @@ const FAQ = () => {
<dt id='howimage'>How do I post an image?</dt>
<dd>
You need a link to the image, ideally using an image hosting service, like Imgur or catbox.moe. Paste the link to the image in the [Link] field when
submitting content. Plebchan will attempt to load the media from the link and show its type next to the [Link] field. If the link type is "webpage", the
submitting content. 5chan will attempt to load the media from the link and show its type next to the [Link] field. If the link type is "webpage", the
link is not an image, and you should try another link.
<br />
<br />
You can also post videos, audios and gifs by pasting their direct links. Plebchan also supports the following websites to embed media without a direct
You can also post videos, audios and gifs by pasting their direct links. 5chan also supports the following websites to embed media without a direct
link: YouTube, Twitter/X, Reddit, Twitch, TikTok, Instagram, Odysee, Bitchute, Streamable, Spotify and Soundcloud.
</dd>
<dt id='uploadimage'>Can I upload an image?</dt>
<dd>
Yes, but only in the Android app, which you can download on <a href='https://github.com/plebbit/plebchan/releases/latest'>GitHub</a>. The app is able to
Yes, but only in the Android app, which you can download on <a href='https://github.com/plebbit/5chan/releases/latest'>GitHub</a>. The app is able to
automatically upload media to image hosting services, like Imgur or catbox.moe, sharing your IP address with the image hosting service. This is not
possible in the browser, which can't make backend requests.
<br />
@@ -141,21 +141,21 @@ const FAQ = () => {
when submitting content. You can paste any link, not just image links. You can also get an image link from social media, by right clicking the image and
selecting "Copy image URL", or by using tools such as <a href='https://cobalt.tools/'>cobalt</a>.<br />
<br />
The reason why uploading media directly to boards is not possible, is because Plebchan is a client for the Plebbit protocol, which is text-only
(including links, from which media is embedded by clients). However, Plebbit uses IPFS, so in theory it could let users upload media to the subplebbit
(board) owner's IPFS node, and then post the direct IPFS link for the media. This is not enabled because loading media from IPFS is extremely slow at
the moment (because most people have slow internet).
The reason why uploading media directly to boards is not possible, is because 5chan is a client for the Plebbit protocol, which is text-only (including
links, from which media is embedded by clients). However, Plebbit uses IPFS, so in theory it could let users upload media to the subplebbit (board)
owner's IPFS node, and then post the direct IPFS link for the media. This is not enabled because loading media from IPFS is extremely slow at the moment
(because most people have slow internet).
</dd>
<dt id='postimage'>Must I post an image?</dt>
<dd>
It depends on the board. Each board has its own rules, and a board owner might decide to only allow posts with images in their community. Plebchan
It depends on the board. Each board has its own rules, and a board owner might decide to only allow posts with images in their community. 5chan
automatically filters out text-only threads in the catalog view, to resemble an imageboard, and you can disable this in the [Filters] menu.
</dd>
<dt id='replyimage'>Can I reply with an image?</dt>
<dd>
Yes. To reply to a thread with an image of your own, fill in the post box as you normally would, making sure to specify a direct image link (e.g.,
ending in .png or .jpeg) in the "Link" field. Plebchan will attempt to load the image, and if it worked it will show the Link type as "image", next to
the field, before posting. If the Link type is "webpage", the link is not an image, and you should try another link.
ending in .png or .jpeg) in the "Link" field. 5chan will attempt to load the image, and if it worked it will show the Link type as "image", next to the
field, before posting. If the Link type is "webpage", the link is not an image, and you should try another link.
</dd>
<dt id='quote'>How do I quote somebody?</dt>
<dd>
@@ -163,14 +163,14 @@ const FAQ = () => {
<span className='greentext'>{'>'}This is a quote</span>").
<br />
<br />
Unlike 4chan and other imageboards, Plebchan does <i>not</i> allow to quote more than one post at a time. You can only reply to one post at a time. This
is because Plebchan is a client for the Plebbit protocol, which is designed to be an alternative to Reddit-like social media, in which you can only
reply to one post at a time.
Unlike 4chan and other imageboards, 5chan does <i>not</i> allow to quote more than one post at a time. You can only reply to one post at a time. This is
because 5chan is a client for the Plebbit protocol, which is designed to be an alternative to Reddit-like social media, in which you can only reply to
one post at a time.
<br />
<br />
Further, post numbers are not possible on Plebchan, because Plebbit is fully decentralized (serverless) using IPFS, meaning there is no central database
to store post numbers, and it uses CIDs to load posts directly. Retrieving the CID from an hypothetical post number from the single board's database
would be far too expensive for the node to calculate.
Further, post numbers are not possible on 5chan, because Plebbit is fully decentralized (serverless) using IPFS, meaning there is no central database to
store post numbers, and it uses CIDs to load posts directly. Retrieving the CID from an hypothetical post number from the single board's database would
be far too expensive for the node to calculate.
<br />
<br />
</dd>
+4 -4
View File
@@ -47,11 +47,11 @@ const InfoBox = () => {
return (
<div className={`${styles.box} ${styles.infoBox}`}>
<div className={styles.infoboxBar}>
<h2>{t('what_is_plebchan')}</h2>
<h2>{t('what_is_5chan')}</h2>
</div>
<div className={styles.boxContent}>
<Trans
i18nKey='plebchan_description'
i18nKey='5chan_description'
shouldUnescape={true}
components={{
1: <Link key='plebbit-link' to='https://plebbit.com' target='_blank' rel='noopener noreferrer' />,
@@ -138,7 +138,7 @@ export const Footer = () => {
</a>
</li>
<li>
<a href='https://github.com/plebbit/plebchan' target='_blank' rel='noopener noreferrer'>
<a href='https://github.com/plebbit/5chan' target='_blank' rel='noopener noreferrer'>
GitHub
</a>
</li>
@@ -176,7 +176,7 @@ const Home = () => {
const { subplebbits } = useSubplebbits({ subplebbitAddresses });
useEffect(() => {
document.title = 'plebchan';
document.title = '5chan';
}, []);
return (
+2 -2
View File
@@ -80,8 +80,8 @@ const PostPage = () => {
useEffect(() => {
const boardTitle = title ? title : shortAddress || subplebbitAddress;
const postTitle = post?.title?.slice(0, 30) || post?.content?.slice(0, 30);
const postDucumentTitle = (postTitle ? postTitle.trim() + '... - ' : '') + boardTitle + ' - plebchan';
document.title = isInAllView ? `${t('all')} - plebchan` : postDucumentTitle;
const postDucumentTitle = (postTitle ? postTitle.trim() + '... - ' : '') + boardTitle + ' - 5chan';
document.title = isInAllView ? `${t('all')} - 5chan` : postDucumentTitle;
}, [title, shortAddress, subplebbitAddress, post?.title, post?.content, isInAllView, t]);
// probably not necessary to show the error to the user if the post loaded successfully