Merge pull request #475 from plebbit/development

Development
This commit is contained in:
Tom (plebeius.eth)
2024-08-14 14:39:30 +02:00
committed by GitHub
15 changed files with 202 additions and 84 deletions
@@ -2,6 +2,7 @@
display: inline-block;
padding: 3px 7px 3px 4px;
filter: var(--filter80);
color-scheme: var(--color-scheme, revert) !important;
}
.modal {
+17 -12
View File
@@ -5,6 +5,7 @@ import { Comment, useAccount, useAccountComments, useAuthorAvatar, useComment, u
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
import styles from '../../views/post/post.module.css';
import { getCommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail } from '../../lib/utils/media-utils';
import { hashStringToColor, getTextColorForBackground } from '../../lib/utils/post-utils';
import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-utils';
import { isValidURL } from '../../lib/utils/url-utils';
import { isAllView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils';
@@ -71,6 +72,9 @@ const PostInfo = ({ openReplyModal, post, postReplyCount = 0, roles, isHidden }:
const handleUserAddressClick = useAuthorAddressClick();
const numberOfPostsByAuthor = document.querySelectorAll(`[data-author-address="${shortAddress}"][data-post-cid="${postCid}"]`).length;
const userIDBackgroundColor = hashStringToColor(shortAddress || accountShortAddress);
const userIDTextColor = getTextColorForBackground(userIDBackgroundColor);
return (
<div className={styles.postInfo}>
{!isHidden && <EditMenu isAccountCommentAuthor={isAccountCommentAuthor} isAccountMod={isAccountMod} isCommentAuthorMod={isCommentAuthorMod} post={post} />}
@@ -106,10 +110,15 @@ const PostInfo = ({ openReplyModal, post, postReplyCount = 0, roles, isHidden }:
<img src={avatarImageUrl} alt='' />
</span>
)}
(u/
(ID: {''}
<Tooltip
children={
<span title={t('highlight_posts')} className={styles.userAddress} onClick={() => handleUserAddressClick(shortAddress || accountShortAddress, postCid)}>
<span
title={t('highlight_posts')}
className={styles.userAddress}
onClick={() => handleUserAddressClick(shortAddress || accountShortAddress, postCid)}
style={{ backgroundColor: userIDBackgroundColor, color: userIDTextColor }}
>
{shortAddress || accountShortAddress}
</span>
}
@@ -138,7 +147,7 @@ const PostInfo = ({ openReplyModal, post, postReplyCount = 0, roles, isHidden }:
<Link to={`/p/${subplebbitAddress}/c/${cid}`} className={styles.linkToPost} title={t('link_to_post')} onClick={(e) => !cid && e.preventDefault()}>
c/
</Link>
<span className={styles.replyToPost} title={t('reply_to_post')} onMouseDown={() => openReplyModal && openReplyModal(cid, postCid)}>
<span className={styles.replyToPost} title={t('reply_to_post')} onMouseDown={() => openReplyModal && openReplyModal(cid, postCid, subplebbitAddress)}>
{shortCid}
</span>
</>
@@ -239,8 +248,7 @@ const PostMedia = ({ post }: PostProps) => {
};
const PostMessage = ({ post }: PostProps) => {
const { cid, content, deleted, edit, original, parentCid, postCid, reason, removed, state, subplebbitAddress } = post || {};
const { isDescription, isRules } = post || {}; // custom properties, not from api
const { cid, content, deleted, edit, original, parentCid, postCid, reason, removed, state } = post || {};
// TODO: commentAuthor is not available outside of editedComment, update when available
// const banned = !!post?.commentAuthor?.banExpiresAt;
const { t } = useTranslation();
@@ -249,7 +257,8 @@ const PostMessage = ({ post }: PostProps) => {
const isInPostView = isPostPageView(location.pathname, params);
const [showOriginal, setShowOriginal] = useState(false);
const displayContent = content && !isInPostView && content.length > 1000 ? content?.slice(0, 1000) + '(...)' : content;
const [showFullComment, setShowFullComment] = useState(false);
const displayContent = content && !isInPostView && content.length > 1000 && !showFullComment ? content.slice(0, 1000) : content;
const quotelinkReply = useComment({ commentCid: parentCid });
const isReply = parentCid;
@@ -318,14 +327,10 @@ const PostMessage = ({ post }: PostProps) => {
/>
</span>
)} */}
{!isReply && content.length > 1000 && !isInPostView && (
{content?.length > 1000 && !isInPostView && !showFullComment && (
<span className={styles.abbr}>
<br />
<Trans
i18nKey={'comment_too_long'}
shouldUnescape={true}
components={{ 1: <Link to={`/p/${subplebbitAddress}/${isDescription ? 'description' : isRules ? 'rules' : `c/${cid}`}`} /> }}
/>
<Trans i18nKey={'comment_too_long'} shouldUnescape={true} components={{ 1: <span onClick={() => setShowFullComment(true)} /> }} />
</span>
)}
{!cid && state === 'pending' && stateString !== 'Failed' && (
@@ -73,7 +73,20 @@ const ImageSearchButton = ({ url, onClose }: { url: string; onClose: () => void
const ViewOnButton = ({ cid, isDescription, isInAllView, isInSubscriptionsView, isRules, subplebbitAddress, onClose }: PostMenuDesktopProps) => {
const { t } = useTranslation();
const [isClientRedirectMenuOpen, setIsClientRedirectMenuOpen] = useState(false);
const viewOnOtherClientLink = `p/${isInAllView ? 'all' : isInSubscriptionsView ? 'subscriptions' : subplebbitAddress}${isDescription || isRules ? '' : `/c/${cid}`}`;
const getViewOnOtherClientLink = () => {
if (isDescription || isRules) {
if (isInAllView || isInSubscriptionsView) {
return `${isInAllView ? 'all' : isInSubscriptionsView && 'subscriptions'}`;
} else {
return `${subplebbitAddress}/${isDescription ? 'description' : isRules && 'rules'}`;
}
} else {
return `${subplebbitAddress}/c/${cid}`;
}
};
const viewOnOtherClientLink = getViewOnOtherClientLink();
const { refs, floatingStyles } = useFloating({
placement: 'right-start',
@@ -91,10 +104,10 @@ const ViewOnButton = ({ cid, isDescription, isInAllView, isInSubscriptionsView,
{_.capitalize(t('view_on'))} »
{isClientRedirectMenuOpen && (
<div ref={refs.setFloating} style={floatingStyles} className={styles.dropdownMenu}>
<a href={`https://seedit.eth.limo/#/${viewOnOtherClientLink}`} target='_blank' rel='noreferrer'>
<a href={`https://seedit.eth.limo/#/p/${viewOnOtherClientLink}`} target='_blank' rel='noreferrer'>
<div className={styles.postMenuItem}>Seedit</div>
</a>
<a href={`https://plebones.eth.limo/#/${viewOnOtherClientLink}`} target='_blank' rel='noreferrer'>
<a href={`https://plebones.eth.limo/#/p/${viewOnOtherClientLink}`} target='_blank' rel='noreferrer'>
<div className={styles.postMenuItem}>Plebones</div>
</a>
</div>
@@ -171,7 +184,7 @@ const PostMenuDesktop = ({ post }: { post: Comment }) => {
<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} />}
{!isInPostPageView && !isDescription && !isRules && (
{!(isInPostPageView && postCid === cid) && !isDescription && !isRules && (
<div
className={styles.postMenuItem}
onClick={() => {
+15 -6
View File
@@ -160,12 +160,11 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
setSubmitStore({
signer: newSigner,
author: {
displayName,
address: newSigner.address,
},
});
}
}, [anonMode, getNewSigner, setSubmitStore, displayName]);
}, [anonMode, getNewSigner, setSubmitStore]);
const onPublishPost = async () => {
if (!title && !content && !link) {
@@ -212,7 +211,6 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
setPublishReplyOptions({
signer: existingSigner,
author: {
displayName,
address: existingSigner.address,
},
});
@@ -221,13 +219,12 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
setPublishReplyOptions({
signer: newSigner,
author: {
displayName,
address: newSigner.address,
},
});
}
}
}, [address, getExistingSigner, getNewSigner, setPublishReplyOptions, displayName, anonMode]);
}, [address, getExistingSigner, getNewSigner, setPublishReplyOptions, anonMode]);
const onPublishReply = () => {
const currentContent = textRef.current?.value || '';
@@ -246,6 +243,14 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
publishReply();
};
const hasSetInitialDisplayName = useRef(false);
useEffect(() => {
if (!hasSetInitialDisplayName.current && displayName) {
setPublishReplyOptions({ displayName });
hasSetInitialDisplayName.current = true;
}
}, [displayName, setPublishReplyOptions]);
useEffect(() => {
if (typeof replyIndex === 'number') {
resetPublishReplyOptions();
@@ -276,7 +281,11 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
defaultValue={displayName || undefined}
onChange={(e) => {
setAccount({ ...account, author: { ...account?.author, displayName: e.target.value } });
setSubmitStore({ displayName: e.target.value });
if (isInPostView) {
setPublishReplyOptions({ displayName: e.target.value });
} else {
setSubmitStore({ displayName: e.target.value });
}
}}
/>
{isInPostView && <button onClick={onPublishReply}>{t('post')}</button>}
@@ -9,7 +9,7 @@ import { copyShareLinkToClipboard, isValidURL } from '../../../lib/utils/url-uti
import useEditCommentPrivileges from '../../../hooks/use-author-privileges';
import useHide from '../../../hooks/use-hide';
import EditMenu from '../../edit-menu/edit-menu';
import { isBoardView, isPostPageView } from '../../../lib/utils/view-utils';
import { isAllView, isBoardView, isPostPageView, isSubscriptionsView } from '../../../lib/utils/view-utils';
import { useLocation, useParams } from 'react-router-dom';
interface PostMenuMobileProps {
@@ -57,7 +57,25 @@ const ImageSearchButtons = ({ url, onClose }: { url: string; onClose: () => void
const ViewOnButtons = ({ cid, isDescription, isRules, subplebbitAddress, onClose }: PostMenuMobileProps) => {
const { t } = useTranslation();
const viewOnOtherClientLink = `p/${subplebbitAddress}${isDescription || isRules ? '' : `/c/${cid}`}`;
const location = useLocation();
const params = useParams();
const isInAllView = isAllView(location.pathname, params);
const isInSubscriptionsView = isSubscriptionsView(location.pathname, params);
const getViewOnOtherClientLink = () => {
if (isDescription || isRules) {
if (isInAllView || isInSubscriptionsView) {
return `${isInAllView ? 'all' : isInSubscriptionsView && 'subscriptions'}`;
} else {
return `${subplebbitAddress}/${isDescription ? 'description' : isRules && 'rules'}`;
}
} else {
return `${subplebbitAddress}/c/${cid}`;
}
};
const viewOnOtherClientLink = getViewOnOtherClientLink();
return (
<div onClick={onClose}>
<a href={`https://seedit.eth.limo/#/${viewOnOtherClientLink}`} target='_blank' rel='noreferrer'>
+17 -12
View File
@@ -5,6 +5,7 @@ import { Comment, useAccount, useAuthorAvatar, useComment, useEditedComment } fr
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
import styles from '../../views/post/post.module.css';
import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
import { getTextColorForBackground, hashStringToColor } from '../../lib/utils/post-utils';
import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-utils';
import { isAllView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils';
import useAuthorAddressClick from '../../hooks/use-author-address-click';
@@ -52,6 +53,9 @@ const PostInfoAndMedia = ({ openReplyModal, post, postReplyCount = 0, roles }: P
const handleUserAddressClick = useAuthorAddressClick();
const numberOfPostsByAuthor = document.querySelectorAll(`[data-author-address="${shortAddress}"][data-post-cid="${postCid}"]`).length;
const userIDBackgroundColor = hashStringToColor(shortAddress || accountShortAddress);
const userIDTextColor = getTextColorForBackground(userIDBackgroundColor);
return (
<>
<div className={styles.postInfo}>
@@ -79,10 +83,15 @@ const PostInfoAndMedia = ({ openReplyModal, post, postReplyCount = 0, roles }: P
<img src={avatarImageUrl} alt='' />
</span>
)}
(u/
(ID: {''}
<Tooltip
children={
<span title={t('highlight_posts')} className={styles.userAddress} onClick={() => handleUserAddressClick(shortAddress || accountShortAddress, postCid)}>
<span
title={t('highlight_posts')}
className={styles.userAddress}
onClick={() => handleUserAddressClick(shortAddress || accountShortAddress, postCid)}
style={{ backgroundColor: userIDBackgroundColor, color: userIDTextColor }}
>
{shortAddress || accountShortAddress}
</span>
}
@@ -127,7 +136,7 @@ const PostInfoAndMedia = ({ openReplyModal, post, postReplyCount = 0, roles }: P
<Link to={`/p/${subplebbitAddress}/c/${cid}`} className={styles.linkToPost} title={t('link_to_post')} onClick={(e) => !cid && e.preventDefault()}>
c/
</Link>
<span className={styles.replyToPost} title={t('reply_to_post')} onMouseDown={() => openReplyModal && openReplyModal(cid, postCid)}>
<span className={styles.replyToPost} title={t('reply_to_post')} onMouseDown={() => openReplyModal && openReplyModal(cid, postCid, subplebbitAddress)}>
{shortCid}
</span>
</>
@@ -163,8 +172,7 @@ const ReplyBacklinks = ({ post }: PostProps) => {
const PostMessageMobile = ({ post }: PostProps) => {
const { t } = useTranslation();
const { cid, content, deleted, edit, original, parentCid, postCid, reason, removed, state, subplebbitAddress } = post || {};
const { isDescription, isRules } = post || {}; // custom properties, not from api
const { cid, content, deleted, edit, original, parentCid, postCid, reason, removed, state } = post || {};
// TODO: commentAuthor is not available outside of editedComment, update when available
// const banned = !!post?.commentAuthor?.banExpiresAt;
const [showOriginal, setShowOriginal] = useState(false);
@@ -173,7 +181,8 @@ const PostMessageMobile = ({ post }: PostProps) => {
const location = useLocation();
const isInPostView = isPostPageView(location.pathname, params);
const displayContent = content && !isInPostView && content.length > 1000 ? content?.slice(0, 1000) : content;
const [showFullComment, setShowFullComment] = useState(false);
const displayContent = content && !isInPostView && content.length > 1000 && !showFullComment ? content.slice(0, 1000) : content;
const quotelinkReply = useComment({ commentCid: parentCid });
const isReply = parentCid;
@@ -242,14 +251,10 @@ const PostMessageMobile = ({ post }: PostProps) => {
/>
</span>
)} */}
{!isReply && content.length > 1000 && !isInPostView && (
{content?.length > 1000 && !isInPostView && !showFullComment && (
<span className={styles.abbr}>
<br />
<Trans
i18nKey={'comment_too_long'}
shouldUnescape={true}
components={{ 1: <Link to={`/p/${subplebbitAddress}/${isDescription ? 'description' : isRules ? 'rules' : `c/${cid}`}`} /> }}
/>
<Trans i18nKey={'comment_too_long'} shouldUnescape={true} components={{ 1: <span onClick={() => setShowFullComment(true)} /> }} />
</span>
)}
{!cid && state === 'pending' && stateString !== 'Failed' && (
+30 -21
View File
@@ -17,14 +17,15 @@ import useAnonMode from '../../hooks/use-anon-mode';
interface ReplyModalProps {
closeModal: () => void;
showReplyModal: boolean;
parentCid: string;
postCid: string;
scrollY: number;
subplebbitAddress: string;
}
const ReplyModal = ({ closeModal, parentCid, postCid, scrollY }: ReplyModalProps) => {
const ReplyModal = ({ closeModal, showReplyModal, parentCid, postCid, scrollY, subplebbitAddress }: ReplyModalProps) => {
const { t } = useTranslation();
const { subplebbitAddress } = useParams() as { subplebbitAddress: string };
const { setPublishReplyOptions, publishReply } = usePublishReply({ cid: parentCid, subplebbitAddress });
const account = useAccount();
const { displayName } = account?.author || {};
@@ -47,7 +48,6 @@ const ReplyModal = ({ closeModal, parentCid, postCid, scrollY }: ReplyModalProps
setPublishReplyOptions({
signer: existingSigner,
author: {
displayName,
address: existingSigner.address,
},
});
@@ -56,13 +56,12 @@ const ReplyModal = ({ closeModal, parentCid, postCid, scrollY }: ReplyModalProps
setPublishReplyOptions({
signer: newSigner,
author: {
displayName,
address: newSigner.address,
},
});
}
}
}, [anonMode, address, getExistingSigner, getNewSigner, displayName, setPublishReplyOptions]);
}, [anonMode, address, getExistingSigner, getNewSigner, setPublishReplyOptions]);
useEffect(() => {
if (anonMode) {
@@ -88,10 +87,17 @@ const ReplyModal = ({ closeModal, parentCid, postCid, scrollY }: ReplyModalProps
closeModal();
};
const hasSetInitialDisplayName = useRef(false);
useEffect(() => {
if (!hasSetInitialDisplayName.current && displayName) {
setPublishReplyOptions({ displayName });
hasSetInitialDisplayName.current = true;
}
}, [displayName, setPublishReplyOptions]);
const nodeRef = useRef<HTMLDivElement>(null);
const isMobile = useIsMobile();
// on mobile, the position is absolute instead of fixed, so we need to calculate the top position
useEffect(() => {
if (nodeRef.current && isMobile) {
const viewportHeight = window.innerHeight;
@@ -122,14 +128,15 @@ const ReplyModal = ({ closeModal, parentCid, postCid, scrollY }: ReplyModalProps
)
: `The subplebbit might be offline and publishing might fail.`;
const setTextRef = (ref: HTMLTextAreaElement | null) => {
if (ref) {
textRef.current = ref;
// if (!isMobile && !urlRef.current?.value) {
// ref.focus();
// }
useEffect(() => {
if (showReplyModal && !isMobile) {
setTimeout(() => {
if (textRef.current) {
textRef.current.focus();
}
}, 0);
}
};
}, [showReplyModal, isMobile]);
useEffect(() => {
if (textRef.current) {
@@ -148,7 +155,6 @@ const ReplyModal = ({ closeModal, parentCid, postCid, scrollY }: ReplyModalProps
};
const handleContentChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
// remove the prefix from the content to publish, and also add newlines for markdown
const contentWithoutPrefix = e.target.value.slice(contentPrefix.length).replace(/\n/g, '\n\n');
if (textRef.current && textRef.current.value !== contentWithoutPrefix) {
setPublishReplyOptions({ content: contentWithoutPrefix });
@@ -196,7 +202,7 @@ const ReplyModal = ({ closeModal, parentCid, postCid, scrollY }: ReplyModalProps
cols={48}
rows={4}
wrap='soft'
ref={setTextRef}
ref={textRef}
spellCheck={false}
defaultValue={contentPrefix + selectedText}
onInput={handleContentInput}
@@ -227,12 +233,15 @@ const ReplyModal = ({ closeModal, parentCid, postCid, scrollY }: ReplyModalProps
</div>
);
return isMobile ? (
modalContent
) : (
<Draggable handle='.replyModalHandle' nodeRef={nodeRef}>
{modalContent}
</Draggable>
return (
showReplyModal &&
(isMobile ? (
modalContent
) : (
<Draggable handle='.replyModalHandle' nodeRef={nodeRef}>
{modalContent}
</Draggable>
))
);
};
+16 -7
View File
@@ -36,15 +36,21 @@ const useReplyStore = create<ReplyState>((set) => ({
signer: {},
spoiler: {},
publishCommentOptions: {},
setReplyStore: (data: SetReplyStoreData) =>
set((state) => {
const { subplebbitAddress, parentCid, author, displayName, content, link, signer, spoiler } = data;
const updatedAuthor = displayName ? { ...author, displayName } : author;
const updatedAuthor = {
...(state.author[parentCid] || author),
...(displayName ? { displayName } : {}),
};
const publishCommentOptions = {
subplebbitAddress,
parentCid,
...(data.author ? { author: updatedAuthor } : {}),
...(data.signer ? { signer: data.signer } : {}),
...(updatedAuthor ? { author: updatedAuthor } : {}),
...(signer ? { signer } : {}),
content,
link,
spoiler,
@@ -57,11 +63,12 @@ const useReplyStore = create<ReplyState>((set) => ({
alert(error.message);
},
};
return {
author: { ...state.author, [parentCid]: updatedAuthor },
signer: { ...state.signer, [parentCid]: signer },
content: { ...state.content, [parentCid]: content },
link: { ...state.link, [parentCid]: link },
signer: { ...state.signer, [parentCid]: signer },
spoiler: { ...state.spoiler, [parentCid]: spoiler },
publishCommentOptions: { ...state.publishCommentOptions, [parentCid]: publishCommentOptions },
};
@@ -70,9 +77,9 @@ const useReplyStore = create<ReplyState>((set) => ({
resetReplyStore: (parentCid) =>
set((state) => ({
author: { ...state.author, [parentCid]: undefined },
signer: { ...state.signer, [parentCid]: undefined },
content: { ...state.content, [parentCid]: undefined },
link: { ...state.link, [parentCid]: undefined },
signer: { ...state.signer, [parentCid]: undefined },
spoiler: { ...state.spoiler, [parentCid]: undefined },
publishCommentOptions: { ...state.publishCommentOptions, [parentCid]: undefined },
})),
@@ -80,8 +87,9 @@ const useReplyStore = create<ReplyState>((set) => ({
const useReply = ({ cid, subplebbitAddress }: { cid: string; subplebbitAddress: string }) => {
const parentCid = cid;
const { author, signer, content, link, spoiler, publishCommentOptions } = useReplyStore((state) => ({
const { author, displayName, signer, content, link, spoiler, publishCommentOptions } = useReplyStore((state) => ({
author: state.author[parentCid],
displayName: state.author[parentCid]?.displayName,
signer: state.signer[parentCid],
content: state.content[parentCid],
link: state.link[parentCid],
@@ -99,6 +107,7 @@ const useReply = ({ cid, subplebbitAddress }: { cid: string; subplebbitAddress:
setReplyStore({
subplebbitAddress,
parentCid,
...(anonMode ? { displayName } : {}),
...(anonMode ? { author } : {}),
content,
link,
@@ -107,7 +116,7 @@ const useReply = ({ cid, subplebbitAddress }: { cid: string; subplebbitAddress:
...options,
});
},
[subplebbitAddress, parentCid, author, signer, content, link, spoiler, setReplyStore, anonMode],
[subplebbitAddress, parentCid, author, displayName, signer, content, link, spoiler, setReplyStore, anonMode],
);
const resetPublishReplyOptions = useCallback(() => resetReplyStore(parentCid), [parentCid, resetReplyStore]);
+4 -2
View File
@@ -6,6 +6,7 @@ const useReplyModal = () => {
const [showReplyModal, setShowReplyModal] = useState(false);
const [activeCid, setActiveCid] = useState<string | null>(null);
const [threadCid, setThreadCid] = useState<string | null>(null);
const [subplebbitAddress, setSubplebbitAddress] = useState<string | null>(null);
const { resetSelectedText, setSelectedText } = useSelectedTextStore();
// on mobile, the css position is absolute instead of fixed, so we need to calculate the top position
@@ -23,7 +24,7 @@ const useReplyModal = () => {
text && setSelectedText(`>${text}\n`);
};
const openReplyModal = (parentCid: string, postCid: string) => {
const openReplyModal = (parentCid: string, postCid: string, subplebbitAddress: string) => {
getSelectedText();
if (isMobile) {
@@ -37,9 +38,10 @@ const useReplyModal = () => {
setActiveCid(parentCid);
setThreadCid(postCid);
setShowReplyModal(true);
setSubplebbitAddress(subplebbitAddress);
};
return { activeCid, threadCid, closeModal, openReplyModal, scrollY, showReplyModal };
return { activeCid, threadCid, closeModal, openReplyModal, scrollY, showReplyModal, subplebbitAddress };
};
export default useReplyModal;
+22
View File
@@ -0,0 +1,22 @@
export function hashStringToColor(str: string): string {
if (!str) {
return '';
}
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
const r = (hash >> 24) & 0xff;
const g = (hash >> 16) & 0xff;
const b = (hash >> 8) & 0xff;
return `rgb(${r}, ${g}, ${b})`;
}
export function getTextColorForBackground(rgb: string): string {
const [r, g, b] = rgb.match(/\d+/g)?.map(Number) || [0, 0, 0];
const brightness = r * 0.299 + g * 0.587 + b * 0.114;
return brightness > 125 ? 'black' : 'white';
}
+6 -6
View File
@@ -23,7 +23,7 @@
--body-background-pattern-image: url("/public/assets/background-fade.png");
--body-font-color: maroon;
--body-font-family: arial, helvetica, sans-serif;
--body-font-size: 13px;
--body-font-size: 10pt;
/* catalog post */
--catalog-post-menu-desktop-btn-color: navy;
@@ -232,7 +232,7 @@
--body-background-pattern-image: url("/public/assets/background-fade-blue.png");
--body-font-color: #000;
--body-font-family: arial, helvetica, sans-serif;
--body-font-size: 13px;
--body-font-size: 10pt;
/* catalog post */
--catalog-post-menu-desktop-btn-color: navy;
@@ -434,7 +434,7 @@
--body-background-pattern-image: none;
--body-font-color: maroon;
--body-font-family: times new roman, serif;
--body-font-size: 16px;
--body-font-size: 12pt;
/* board banner */
--banner-image-border: none;
@@ -616,7 +616,7 @@
--body-background-pattern-image: none;
--body-font-color: #000;
--body-font-family: times new roman, serif;
--body-font-size: 16px;
--body-font-size: 12pt;
/* catalog post */
--catalog-post-menu-desktop-btn-color: navy;
@@ -795,7 +795,7 @@
--body-background-pattern-image: none;
--body-font-color: #c5c8c6;
--body-font-family: arial, helvetica, sans-serif;
--body-font-size: 13px;
--body-font-size: 10pt;
/* board banner */
--banner-image-border: none;
@@ -1008,7 +1008,7 @@
--body-background-pattern-image: none;
--body-font-color: #333;
--body-font-family: arial, helvetica, sans-serif;
--body-font-size: 13px;
--body-font-size: 10pt;
/* board banner */
--banner-image-border: none;
+11 -2
View File
@@ -104,7 +104,7 @@ const Board = () => {
const { createdAt, description, error, rules, shortAddress, state, suggested } = subplebbit || {};
const title = isInAllView ? t('all') : isInSubscriptionsView ? t('subscriptions') : subplebbit?.title;
const { activeCid, threadCid, closeModal, openReplyModal, showReplyModal, scrollY } = useReplyModal();
const { activeCid, threadCid, closeModal, openReplyModal, showReplyModal, scrollY, subplebbitAddress: postSubplebbitAddress } = useReplyModal();
const { blocked, unblock } = useBlock({ address: subplebbitAddress });
const loadingStateString = useFeedStateString(subplebbitAddresses) || t('loading');
@@ -158,7 +158,16 @@ const Board = () => {
return (
<div className={styles.content}>
{location.pathname.endsWith('/settings') && <SettingsModal />}
{showReplyModal && activeCid && threadCid && <ReplyModal closeModal={closeModal} parentCid={activeCid} postCid={threadCid} scrollY={scrollY} />}
{activeCid && threadCid && postSubplebbitAddress && (
<ReplyModal
closeModal={closeModal}
parentCid={activeCid}
postCid={threadCid}
scrollY={scrollY}
showReplyModal={showReplyModal}
subplebbitAddress={postSubplebbitAddress}
/>
)}
{feed.length !== 0 ? (
<>
{rules && rules.length > 0 && <SubplebbitRules subplebbitAddress={subplebbitAddress} createdAt={createdAt} rules={rules} />}
@@ -18,9 +18,11 @@ interface PopularThreadProps {
}
const PopularThreadCard = ({ post, boardTitle, boardShortAddress }: PopularThreadProps) => {
const { cid, content, subplebbitAddress, title } = post || {};
const { cid, subplebbitAddress, title } = post || {};
const commentMediaInfo = getCommentMediaInfo(post);
let content = post?.content?.replace(/\n\n/g, '\n');
return (
<div className={styles.popularThread} key={cid}>
<div className={styles.title}>{boardTitle || boardShortAddress}</div>
+11 -6
View File
@@ -73,11 +73,14 @@
}
.userAddress {
padding: 0 5px;
border-radius: 6px;
font-size: 0.8em;
cursor: pointer;
}
.postDesktop .userAddress:hover {
color: var(--button-desktop-text-color-hover);
.userAddress:hover {
color: var(--post-quotelink-text-color-hover) !important;
}
.postDesktop .subject {
@@ -164,12 +167,13 @@
font-size: var(--post-mobile-abbr-font-size);
}
.postDesktop .abbr a {
.postDesktop .abbr a, .postDesktop .abbr span {
color: var(--post-link-text-color);
text-decoration: var(--post-content-link-text-decoration);
}
.postDesktop .abbr a:hover {
.postDesktop .abbr a:hover, .postDesktop .abbr span:hover {
cursor: pointer;
color: var(--post-link-text-color-hover);
text-decoration: var(--post-content-link-text-decoration-hover);
}
@@ -344,13 +348,14 @@
font-size: var(--post-mobile-abbr-font-size);
}
.postMobile .abbr a {
.postMobile .abbr a, .postMobile .abbr span {
color: var(--post-link-text-color);
text-decoration: var(--post-content-link-text-decoration);
}
.postMobile .abbr a:hover {
.postMobile .abbr a:hover, .postMobile .abbr span:hover {
cursor: pointer;
color: var(--post-link-text-color-hover);
text-decoration: var(--post-content-link-text-decoration-hover);
}
+12 -3
View File
@@ -22,7 +22,7 @@ export interface PostProps {
roles?: Role[];
showAllReplies?: boolean;
showReplies?: boolean;
openReplyModal?: (parentCid: string, postCid: string) => void;
openReplyModal?: (parentCid: string, postCid: string, subplebbitAddress: string) => void;
}
export const Post = ({ post, showAllReplies = false, showReplies = true, openReplyModal }: PostProps) => {
@@ -62,7 +62,7 @@ const PostPage = () => {
const subplebbit = useSubplebbit({ subplebbitAddress });
const { createdAt, description, rules, shortAddress, suggested, title } = subplebbit;
const { activeCid, threadCid, closeModal, openReplyModal, showReplyModal, scrollY } = useReplyModal();
const { activeCid, threadCid, closeModal, openReplyModal, showReplyModal, scrollY, subplebbitAddress: postSubplebbitAddress } = useReplyModal();
const comment = useComment({ commentCid });
@@ -91,7 +91,16 @@ const PostPage = () => {
return (
<div className={styles.content}>
{isInSettigsView && <SettingsModal />}
{showReplyModal && activeCid && threadCid && <ReplyModal closeModal={closeModal} parentCid={activeCid} postCid={threadCid} scrollY={scrollY} />}
{activeCid && threadCid && postSubplebbitAddress && (
<ReplyModal
closeModal={closeModal}
parentCid={activeCid}
postCid={threadCid}
scrollY={scrollY}
showReplyModal={showReplyModal}
subplebbitAddress={postSubplebbitAddress}
/>
)}
{/* TODO: remove this replyCount error once api supports scrolling replies pages */}
{replyCount > 60 && <span className={styles.error}>Error: this thread has too many replies, some of them cannot be displayed right now.</span>}
{error && <span className={styles.error}>Error: {error.message}</span>}