mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
feat(catalog): add post menu
This commit is contained in:
@@ -79,36 +79,10 @@
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.postMenuBtnPadding {
|
||||
left: 5px;
|
||||
top: 5px;
|
||||
position: relative;
|
||||
.post .postMenu {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.postMenuBtn {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
line-height: 1em;
|
||||
color: var(--catalog-post-menu-btn-color);
|
||||
opacity: 0;
|
||||
transition: transform 0.1s;
|
||||
}
|
||||
|
||||
.post:hover .postMenuBtn {
|
||||
opacity: var(--catalog-post-menu-btn-opacity);
|
||||
}
|
||||
|
||||
.post:hover .postMenuBtn:hover {
|
||||
cursor: pointer;
|
||||
color: var(--catalog-post-menu-btn-hover-color);
|
||||
opacity: var(--catalog-post-menu-btn-hover-opacity);
|
||||
}
|
||||
|
||||
.postMenuBtn {
|
||||
/* preserve unicode triangle for button, some browsers will use an emoji */
|
||||
font-variant: normal;
|
||||
font-feature-settings: "tnum";
|
||||
-webkit-font-feature-settings: "tnum"; /* Chrome, Opera, and Safari */
|
||||
-moz-font-feature-settings: "tnum"; /* Firefox */
|
||||
font-variant-numeric: tabular-nums;
|
||||
.post:hover .postMenu {
|
||||
visibility: visible;
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Comment } from '@plebbit/plebbit-react-hooks';
|
||||
@@ -7,6 +6,7 @@ import { isAllView } from '../../lib/utils/view-utils';
|
||||
import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame';
|
||||
import useCountLinksInReplies from '../../hooks/use-count-links-in-replies';
|
||||
import styles from './catalog-row.module.css';
|
||||
import PostMenu from '../post/post-menu/post-menu';
|
||||
|
||||
export const CatalogPostMedia = ({ commentMediaInfo }: { commentMediaInfo: any }) => {
|
||||
const { patternThumbnailUrl, thumbnail, type, url } = commentMediaInfo || {};
|
||||
@@ -30,13 +30,7 @@ export const CatalogPostMedia = ({ commentMediaInfo }: { commentMediaInfo: any }
|
||||
return thumbnailComponent;
|
||||
};
|
||||
|
||||
interface CatalogPostProps {
|
||||
post: Comment;
|
||||
openMenu: boolean;
|
||||
toggleMenu: () => void;
|
||||
}
|
||||
|
||||
const CatalogPost = ({ openMenu, post, toggleMenu }: CatalogPostProps) => {
|
||||
const CatalogPost = ({ post }: { post: Comment }) => {
|
||||
const { t } = useTranslation();
|
||||
const { cid, content, isDescription, isRules, link, linkHeight, linkWidth, locked, pinned, replyCount, subplebbitAddress, title } = post || {};
|
||||
const commentMediaInfo = getCommentMediaInfo(post);
|
||||
@@ -76,19 +70,6 @@ const CatalogPost = ({ openMenu, post, toggleMenu }: CatalogPostProps) => {
|
||||
</div>
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const handlePageClick = () => {
|
||||
if (openMenu) {
|
||||
toggleMenu();
|
||||
}
|
||||
};
|
||||
document.addEventListener('click', handlePageClick);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('click', handlePageClick);
|
||||
};
|
||||
}, [openMenu, toggleMenu]);
|
||||
|
||||
return (
|
||||
<div className={styles.post}>
|
||||
{hasThumbnail ? (
|
||||
@@ -111,18 +92,8 @@ const CatalogPost = ({ openMenu, post, toggleMenu }: CatalogPostProps) => {
|
||||
/ L: <b>{linkCount}</b>
|
||||
</span>
|
||||
)}
|
||||
<span className={styles.postMenuBtnPadding}>
|
||||
<span
|
||||
className={styles.postMenuBtn}
|
||||
title='Thread Menu'
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
toggleMenu();
|
||||
}}
|
||||
style={{ transform: openMenu ? 'rotate(90deg)' : 'rotate(0deg)' }}
|
||||
>
|
||||
▶
|
||||
</span>
|
||||
<span className={styles.postMenu}>
|
||||
<PostMenu post={post} />
|
||||
</span>
|
||||
</div>
|
||||
<Link to={postLink}>
|
||||
@@ -141,16 +112,10 @@ interface CatalogRowProps {
|
||||
}
|
||||
|
||||
const CatalogRow = ({ row }: CatalogRowProps) => {
|
||||
const [postWithMenuOpen, setPostWithMenuOpen] = useState<number | null>(null);
|
||||
|
||||
const handleToggleMenu = (index: number) => {
|
||||
setPostWithMenuOpen(postWithMenuOpen === index ? null : index);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.row}>
|
||||
{row.map((post, index) => (
|
||||
<CatalogPost key={index} post={post} openMenu={postWithMenuOpen === index} toggleMenu={() => handleToggleMenu(index)} />
|
||||
<CatalogPost key={index} post={post} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -60,6 +60,36 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
.postMenuBtnCatalogWrapper {
|
||||
left: 5px;
|
||||
top: 5px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.postMenuBtnCatalog {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
line-height: 1em;
|
||||
color: var(--catalog-post-menu-btn-color);
|
||||
opacity: var(--catalog-post-menu-btn-opacity);
|
||||
transition: transform 0.1s;
|
||||
}
|
||||
|
||||
.postMenuBtnCatalog:hover {
|
||||
cursor: pointer;
|
||||
color: var(--catalog-post-menu-btn-hover-color);
|
||||
opacity: var(--catalog-post-menu-btn-hover-opacity);
|
||||
}
|
||||
|
||||
.postMenuBtnCatalog {
|
||||
/* preserve unicode triangle for button, some browsers will use an emoji */
|
||||
font-variant: normal;
|
||||
font-feature-settings: "tnum";
|
||||
-webkit-font-feature-settings: "tnum"; /* Chrome, Opera, and Safari */
|
||||
-moz-font-feature-settings: "tnum"; /* Firefox */
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.postMenuBtn {
|
||||
width: 1em;
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { autoUpdate, flip, FloatingFocusManager, offset, shift, useClick, useDismiss, useFloating, useId, useInteractions, useRole } from '@floating-ui/react';
|
||||
import { Comment } from '@plebbit/plebbit-react-hooks';
|
||||
import styles from './post-menu.module.css';
|
||||
import { getCommentMediaInfo } from '../../../lib/utils/media-utils';
|
||||
import { copyShareLinkToClipboard, isValidURL } from '../../../lib/utils/url-utils';
|
||||
import { isCatalogView } from '../../../lib/utils/view-utils';
|
||||
|
||||
interface PostMenuProps {
|
||||
cid: string;
|
||||
@@ -84,11 +87,13 @@ const PostMenu = ({ post }: { post: Comment }) => {
|
||||
const { thumbnail, type, url } = commentMediaInfo || {};
|
||||
const [menuBtnRotated, setMenuBtnRotated] = useState(false);
|
||||
|
||||
const isInCatalogView = isCatalogView(useLocation().pathname);
|
||||
|
||||
const { refs, floatingStyles, context } = useFloating({
|
||||
placement: 'bottom-start',
|
||||
open: menuBtnRotated,
|
||||
onOpenChange: setMenuBtnRotated,
|
||||
middleware: [offset({ mainAxis: 10, crossAxis: 5 }), flip({ fallbackAxisSideDirection: 'end' }), shift({ padding: 10 })],
|
||||
middleware: [offset({ mainAxis: isInCatalogView ? -2 : 6, crossAxis: isInCatalogView ? -1 : 5 }), flip({ fallbackAxisSideDirection: 'end' }), shift({ padding: 10 })],
|
||||
whileElementsMounted: autoUpdate,
|
||||
});
|
||||
const click = useClick(context);
|
||||
@@ -101,9 +106,9 @@ const PostMenu = ({ post }: { post: Comment }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className={styles.postMenuBtnWrapper} ref={refs.setReference} {...getReferenceProps()}>
|
||||
<span className={isInCatalogView ? styles.postMenuBtnCatalogWrapper : styles.postMenuBtnWrapper} ref={refs.setReference} {...getReferenceProps()}>
|
||||
<span
|
||||
className={styles.postMenuBtn}
|
||||
className={isInCatalogView ? styles.postMenuBtnCatalog : styles.postMenuBtn}
|
||||
title='Post menu'
|
||||
onClick={() => setMenuBtnRotated((prev) => !prev)}
|
||||
style={{ transform: menuBtnRotated ? 'rotate(90deg)' : 'rotate(0deg)' }}
|
||||
@@ -111,15 +116,17 @@ const PostMenu = ({ post }: { post: Comment }) => {
|
||||
▶
|
||||
</span>
|
||||
</span>
|
||||
{menuBtnRotated && (
|
||||
<FloatingFocusManager context={context} modal={false}>
|
||||
<div className={styles.postMenu} ref={refs.setFloating} style={floatingStyles} aria-labelledby={headingId} {...getFloatingProps()}>
|
||||
{cid && subplebbitAddress && <CopyLinkButton cid={cid} subplebbitAddress={subplebbitAddress} onClose={handleClose} />}
|
||||
{link && isValidURL(link) && (type === 'image' || type === 'gif' || thumbnail) && url && <ImageSearchButton url={url} onClose={handleClose} />}
|
||||
<ViewOnButton cid={cid} isDescription={isDescription} isRules={isRules} subplebbitAddress={subplebbitAddress} onClose={handleClose} />
|
||||
</div>
|
||||
</FloatingFocusManager>
|
||||
)}
|
||||
{menuBtnRotated &&
|
||||
createPortal(
|
||||
<FloatingFocusManager context={context} modal={false}>
|
||||
<div className={styles.postMenu} ref={refs.setFloating} style={floatingStyles} aria-labelledby={headingId} {...getFloatingProps()}>
|
||||
{cid && subplebbitAddress && <CopyLinkButton cid={cid} subplebbitAddress={subplebbitAddress} onClose={handleClose} />}
|
||||
{link && isValidURL(link) && (type === 'image' || type === 'gif' || thumbnail) && url && <ImageSearchButton url={url} onClose={handleClose} />}
|
||||
<ViewOnButton cid={cid} isDescription={isDescription} isRules={isRules} subplebbitAddress={subplebbitAddress} onClose={handleClose} />
|
||||
</div>
|
||||
</FloatingFocusManager>,
|
||||
document.body,
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user