mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
feat: use s.5chan.app share links and remove report menu items
Copy share links as path-based s.5chan.app URLs so the share server can render social previews, rename the menu label, remove the stub report action, and update the FAQ.
This commit is contained in:
@@ -144,7 +144,7 @@ describe('PostMenuDesktop', () => {
|
||||
await renderMenu();
|
||||
|
||||
await openMenu();
|
||||
const copyLink = Array.from(document.body.querySelectorAll('button')).find((node) => node.textContent === 'copy_direct_link');
|
||||
const copyLink = Array.from(document.body.querySelectorAll('button')).find((node) => node.textContent === 'copy_share_link');
|
||||
await act(async () => {
|
||||
copyLink?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
||||
});
|
||||
|
||||
@@ -46,7 +46,7 @@ const CopyLinkButton = ({ cid, communityAddress, linkType, onClose }: CopyLinkBu
|
||||
const { t } = useTranslation();
|
||||
const directories = useDirectories();
|
||||
const boardIdentifier = getBoardPath(communityAddress, directories);
|
||||
const copyDirectLink = async () => {
|
||||
const copyShareLink = async () => {
|
||||
await safeCopyShareLink(boardIdentifier, linkType, linkType === 'thread' ? cid : undefined);
|
||||
onClose();
|
||||
};
|
||||
@@ -55,15 +55,15 @@ const CopyLinkButton = ({ cid, communityAddress, linkType, onClose }: CopyLinkBu
|
||||
type='button'
|
||||
className={styles.postMenuItem}
|
||||
tabIndex={0}
|
||||
onClick={copyDirectLink}
|
||||
onClick={copyShareLink}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
copyDirectLink();
|
||||
copyShareLink();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('copy_direct_link')}
|
||||
{t('copy_share_link')}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -225,24 +225,6 @@ const PostMenuDesktop = ({ postMenu }: PostMenuDesktopProps) => {
|
||||
createPortal(
|
||||
<FloatingFocusManager context={context} modal={false}>
|
||||
<div className={styles.postMenu} ref={refs.setFloating} style={floatingStyles} aria-labelledby={headingId} {...getFloatingProps()}>
|
||||
<button
|
||||
type='button'
|
||||
className={styles.postMenuItem}
|
||||
tabIndex={0}
|
||||
onClick={() => {
|
||||
alert("Reporting isn't available yet.");
|
||||
handleClose();
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
alert("Reporting isn't available yet.");
|
||||
handleClose();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('report_post')}
|
||||
</button>
|
||||
{!(isInPostPageView && postCid === cid) && (
|
||||
<button
|
||||
type='button'
|
||||
|
||||
@@ -185,7 +185,7 @@ describe('PostMenuMobile', () => {
|
||||
expect(document.body.querySelector('[data-testid="edit-menu"]')?.textContent).toBe('cid-1');
|
||||
|
||||
await openMenu();
|
||||
const copyLink = Array.from(document.body.querySelectorAll('div')).find((node) => node.textContent === 'copy_direct_link');
|
||||
const copyLink = Array.from(document.body.querySelectorAll('div')).find((node) => node.textContent === 'copy_share_link');
|
||||
await act(async () => {
|
||||
copyLink?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
||||
});
|
||||
|
||||
@@ -65,7 +65,7 @@ const CopyLinkButton = ({ cid, communityAddress, linkType, onClose }: CopyLinkBu
|
||||
const { t } = useTranslation();
|
||||
const directories = useDirectories();
|
||||
const boardIdentifier = getBoardPath(communityAddress, directories);
|
||||
const copyDirectLink = async () => {
|
||||
const copyShareLink = async () => {
|
||||
await copyShareLinkSafe(boardIdentifier, linkType, linkType === 'thread' ? cid : undefined);
|
||||
onClose();
|
||||
};
|
||||
@@ -73,15 +73,15 @@ const CopyLinkButton = ({ cid, communityAddress, linkType, onClose }: CopyLinkBu
|
||||
<button
|
||||
type='button'
|
||||
tabIndex={0}
|
||||
onClick={copyDirectLink}
|
||||
onClick={copyShareLink}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
copyDirectLink();
|
||||
copyShareLink();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className={styles.postMenuItem}>{t('copy_direct_link')}</div>
|
||||
<div className={styles.postMenuItem}>{t('copy_share_link')}</div>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -150,29 +150,6 @@ const ImageSearchButtons = ({ url, onClose }: { url: string; onClose: () => void
|
||||
);
|
||||
};
|
||||
|
||||
const ReportPostButton = ({ onClose }: { onClose: () => void }) => {
|
||||
const { t } = useTranslation();
|
||||
const reportPost = () => {
|
||||
alert("Reporting isn't available yet.");
|
||||
onClose();
|
||||
};
|
||||
return (
|
||||
<button
|
||||
type='button'
|
||||
tabIndex={0}
|
||||
onClick={reportPost}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
reportPost();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className={styles.postMenuItem}>{t('report_post')}</div>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
type DeletePostButtonProps = {
|
||||
post: Comment;
|
||||
onClose: () => void;
|
||||
@@ -353,7 +330,6 @@ const PostMenuMobile = ({ postMenu, editMenuPost }: PostMenuMobileProps) => {
|
||||
createPortal(
|
||||
<FloatingFocusManager context={context} modal={false}>
|
||||
<div className={styles.postMenu} ref={refs.setFloating} style={floatingStyles} aria-labelledby={headingId} {...getFloatingProps()}>
|
||||
<ReportPostButton onClose={handleClose} />
|
||||
{cid && communityAddress && <HidePostButton cid={cid} comment={postMenu.comment} isReply={!!parentCid} postCid={postCid} onClose={handleClose} />}
|
||||
{(isAccountCommentAuthor || canAttemptAuthorDelete) && cid && editMenuPost && <DeletePostButton post={editMenuPost} onClose={handleClose} />}
|
||||
{cid && communityAddress && <CopyLinkButton cid={cid} communityAddress={communityAddress} linkType='thread' onClose={handleClose} />}
|
||||
|
||||
@@ -72,12 +72,12 @@ describe('url-utils', () => {
|
||||
expect(getExpiringMediaLinkHostname('not-a-url')).toBeNull();
|
||||
});
|
||||
|
||||
it('copies share links for threads and catalog pages using the production fallback base url', async () => {
|
||||
it('copies path-based share links for threads and catalog pages using the share subdomain', async () => {
|
||||
await copyShareLinkToClipboard('music.eth', 'thread', 'cid-123');
|
||||
expect(testState.copyToClipboardMock).toHaveBeenCalledWith('https://5chan.app/#/music.eth/thread/cid-123');
|
||||
expect(testState.copyToClipboardMock).toHaveBeenCalledWith('https://s.5chan.app/music.eth/thread/cid-123');
|
||||
|
||||
await copyShareLinkToClipboard('music.eth', 'catalog');
|
||||
expect(testState.copyToClipboardMock).toHaveBeenCalledWith('https://5chan.app/#/music.eth/catalog');
|
||||
expect(testState.copyToClipboardMock).toHaveBeenCalledWith('https://s.5chan.app/music.eth/catalog');
|
||||
|
||||
const copyThreadWithoutCid = copyShareLinkToClipboard as (boardIdentifier: string, linkType: 'thread', cid?: string) => Promise<void>;
|
||||
await expect(copyThreadWithoutCid('music.eth', 'thread')).rejects.toThrow('copyShareLinkToClipboard: thread links require a cid');
|
||||
@@ -89,6 +89,8 @@ describe('url-utils', () => {
|
||||
expect(is5chanLink('https://5chan.app/#/music.eth/catalog')).toBe(true);
|
||||
expect(is5chanLink('https://5chan.app/p/music.eth/c/cid-123')).toBe(true);
|
||||
expect(is5chanLink('https://5chan.app/all/catalog')).toBe(true);
|
||||
expect(is5chanLink('https://s.5chan.app/music.eth/thread/cid-123')).toBe(true);
|
||||
expect(is5chanLink('https://s.5chan.app/music.eth/catalog')).toBe(true);
|
||||
expect(is5chanLink('https://example.com/music.eth')).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
@@ -128,10 +128,14 @@ export const getPublishURLFilename = (url: string): string | null => {
|
||||
}
|
||||
};
|
||||
|
||||
const CHAN_5_HOSTNAMES = ['5chan.app', '5chan.eth.limo', '5chan.eth.link', '5chan.eth.sucks', '5chan.netlify.app'];
|
||||
// Dedicated subdomain that serves shared posts. Its server renders social-media link
|
||||
// previews (OpenGraph/Twitter cards) for the shared post before handing off to the app.
|
||||
const SHARE_HOSTNAME = 's.5chan.app';
|
||||
|
||||
const CHAN_5_HOSTNAMES = ['5chan.app', SHARE_HOSTNAME, '5chan.eth.limo', '5chan.eth.link', '5chan.eth.sucks', '5chan.netlify.app'];
|
||||
|
||||
function getShareBaseUrl(): string {
|
||||
return `https://${CHAN_5_HOSTNAMES[0]}`;
|
||||
return `https://${SHARE_HOSTNAME}`;
|
||||
}
|
||||
|
||||
export type ShareLinkType = 'thread' | 'catalog';
|
||||
@@ -140,16 +144,18 @@ export type ShareLinkType = 'thread' | 'catalog';
|
||||
export function copyShareLinkToClipboard(boardIdentifier: string, linkType: 'thread', cid: string): Promise<void>;
|
||||
export function copyShareLinkToClipboard(boardIdentifier: string, linkType: Exclude<ShareLinkType, 'thread'>): Promise<void>;
|
||||
export async function copyShareLinkToClipboard(boardIdentifier: string, linkType: ShareLinkType, cid?: string): Promise<void> {
|
||||
// Share links are path-based (no HashRouter `#/`) so the share server can read the post
|
||||
// from the request path and render its preview; URL fragments never reach the server.
|
||||
if (linkType === 'thread') {
|
||||
if (!cid) {
|
||||
throw new Error('copyShareLinkToClipboard: thread links require a cid');
|
||||
}
|
||||
const shareLink = `${getShareBaseUrl()}/#/${boardIdentifier}/thread/${cid}`;
|
||||
const shareLink = `${getShareBaseUrl()}/${boardIdentifier}/thread/${cid}`;
|
||||
await copyToClipboard(shareLink);
|
||||
return;
|
||||
}
|
||||
|
||||
const shareLink = `${getShareBaseUrl()}/#/${boardIdentifier}/${linkType}`;
|
||||
const shareLink = `${getShareBaseUrl()}/${boardIdentifier}/${linkType}`;
|
||||
await copyToClipboard(shareLink);
|
||||
}
|
||||
|
||||
|
||||
@@ -230,8 +230,8 @@ const FAQ_SECTIONS: FAQSection[] = [
|
||||
question: 'How do I report posts?',
|
||||
answer: (
|
||||
<>
|
||||
Reporting is not available yet. For now, board moderation happens through board-specific moderator tools and rules. If you are a board moderator, use the mod
|
||||
queue and edit controls available for that board.
|
||||
5chan does not use manual user reports. Moderation is handled automatically by AI moderation, and board moderators review flagged content through the mod
|
||||
queue. If you run a board, use the mod queue and edit controls available for that board.
|
||||
</>
|
||||
),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user