refactor(feed post): move functions to utils, move expando and button code to new components, remove repetitions

This commit is contained in:
plebeius.eth
2023-10-18 14:23:52 +02:00
parent fa9b39a6ce
commit 397b58ea3d
10 changed files with 216 additions and 162 deletions

View File

@@ -0,0 +1,36 @@
.buttonWrapper {
padding: 2px 5px 2px 0;
float: left;
cursor: pointer;
}
.buttonCommon {
background-size: cover;
height: 23px;
width: 23px;
display: block;
}
.textButton {
background-image: var(--text-button);
}
.textButton:hover {
background-image: var(--text-button-hover);
}
.playButton {
background-image: var(--play-button);
}
.playButton:hover {
background-image: var(--play-button-hover);
}
.closeButton {
background-image: var(--close-button);
}
.closeButton:hover {
background-image: var(--close-button-hover);
}

View File

@@ -0,0 +1,31 @@
import { FC } from 'react';
import { useComment } from '@plebbit/plebbit-react-hooks';
import styles from './expand-button.module.css';
import utils from '../../lib/utils';
interface ExpandButtonProps {
commentCid: string;
expanded: boolean;
hasThumbnail: boolean;
toggleExpanded: () => void;
}
const ExpandButton: FC<ExpandButtonProps> = ({ commentCid, expanded, hasThumbnail, toggleExpanded }) => {
const comment = useComment({ commentCid });
const { content, link } = comment || {};
const commentMediaInfo = utils.getCommentMediaInfoMemoized(comment);
const initialButtonType = hasThumbnail || commentMediaInfo?.type === 'audio' || commentMediaInfo?.type === 'iframe' ? 'playButton' : 'textButton';
const buttonType = expanded ? 'closeButton' : initialButtonType;
return (
((content && !link) || link) && (
<div className={styles.buttonWrapper} onClick={toggleExpanded}>
<div className={`${styles.buttonCommon} ${styles[buttonType]}`}></div>
</div>
)
);
};
export default ExpandButton;

View File

@@ -0,0 +1 @@
export {default} from './expand-button';

View File

@@ -0,0 +1,44 @@
.expandoHidden {
display: none;
}
.expando {
display: block;
padding: 5px 0 5px 0;
clear: left;
position: relative;
}
.usertext {
unicode-bidi: isolate;
font-size: small;
}
.markdown {
background-color: var(--background-markdown);
border: 1px solid var(--text-primary);
border-radius: 7px;
padding: 5px 10px;
font-weight: 400;
color: var(--text-markdown);
max-width: 60em;
word-wrap: break-word;
font-size: 1em;
}
.mediaPreview {
max-width: 354px;
overflow: auto;
position: relative;
}
.mediaPreview img, .mediaPreview video {
width: 100%;
height: auto;
}
.mediaPreview iframe {
width: 100%;
height: 100%;
border: none;
}

View File

@@ -0,0 +1,67 @@
import { FC } from 'react';
import { useComment } from '@plebbit/plebbit-react-hooks';
import utils from '../../lib/utils';
import { Link } from 'react-router-dom';
import styles from './expando.module.css';
import Embed from '../embed';
interface ExpandoProps {
commentCid: string;
expanded: boolean;
}
const Expando: FC<ExpandoProps> = ({ commentCid, expanded }) => {
const comment = useComment({ commentCid });
const { cid, content, link, subplebbitAddress } = comment || {};
const commentMediaInfo = utils.getCommentMediaInfoMemoized(comment);
return (
<div className={expanded ? styles.expando : styles.expandoHidden}>
{link && (
<div className={styles.mediaPreview}>
<Link to={`p/${subplebbitAddress}/c/${cid}`} onClick={(e) => e.preventDefault()}>
{commentMediaInfo?.type === 'image' && (
<img
src={commentMediaInfo.url}
alt='thumbnail'
onError={(e) => {
e.currentTarget.alt = '';
}}
/>
)}
{commentMediaInfo?.type === 'video' &&
(commentMediaInfo.thumbnail ? (
<img
src={commentMediaInfo.thumbnail}
alt='thumbnail'
onError={(e) => {
e.currentTarget.alt = '';
}}
/>
) : (
<video src={commentMediaInfo.url} controls />
))}
{commentMediaInfo?.type === 'webpage' && commentMediaInfo.thumbnail && (
<img
src={commentMediaInfo.thumbnail}
alt='thumbnail'
onError={(e) => {
e.currentTarget.alt = '';
}}
/>
)}
{commentMediaInfo?.type === 'audio' && <audio src={commentMediaInfo.url} controls />}
{commentMediaInfo?.type === 'iframe' && expanded && <Embed url={commentMediaInfo.url} />}
</Link>
</div>
)}
{content && (
<div className={styles.usertext}>
<div className={styles.markdown}>{content}</div>
</div>
)}
</div>
);
};
export default Expando;

View File

@@ -0,0 +1 @@
export {default} from './expando';

View File

@@ -91,87 +91,4 @@
.tagline .subplebbit:hover {
text-decoration: underline;
}
.buttonWrapper {
padding: 2px 5px 2px 0;
float: left;
cursor: pointer;
}
.buttonCommon {
background-size: cover;
height: 23px;
width: 23px;
display: block;
}
.textButton {
background-image: var(--text-button);
}
.textButton:hover {
background-image: var(--text-button-hover);
}
.playButton {
background-image: var(--play-button);
}
.playButton:hover {
background-image: var(--play-button-hover);
}
.closeButton {
background-image: var(--close-button);
}
.closeButton:hover {
background-image: var(--close-button-hover);
}
.expandoHidden {
display: none;
}
.expando {
display: block;
padding: 5px 0 5px 0;
clear: left;
position: relative;
}
.usertext {
unicode-bidi: isolate;
font-size: small;
}
.markdown {
background-color: var(--background-markdown);
border: 1px solid var(--text-primary);
border-radius: 7px;
padding: 5px 10px;
font-weight: 400;
color: var(--text-markdown);
max-width: 60em;
word-wrap: break-word;
font-size: 1em;
}
.mediaPreview {
max-width: 354px;
overflow: auto;
position: relative;
}
.mediaPreview img, .mediaPreview video {
width: 100%;
height: auto;
}
.mediaPreview iframe {
width: 100%;
height: 100%;
border: none;
}

View File

@@ -4,7 +4,8 @@ import { Link } from 'react-router-dom';
import utils from '../../lib/utils';
import { Comment, useSubplebbit } from '@plebbit/plebbit-react-hooks';
import { useTranslation } from 'react-i18next';
import Embed from '../embed';
import ExpandButton from '../expand-button';
import Expando from '../expando';
import Flair from '../flair';
import PostTools from '../post-tools';
import Thumbnail from '../thumbnail';
@@ -16,26 +17,15 @@ interface FeedPostProps {
const FeedPost: FC<FeedPostProps> = ({ post, index }) => {
const { author, cid, content, downvoteCount, flair, link, subplebbitAddress, timestamp, title, upvoteCount } = post || {};
const { t } = useTranslation();
const [expandoVisible, setExpandoVisible] = useState(false);
const subplebbit = useSubplebbit({ subplebbitAddress });
const commentMediaInfo = utils.getCommentMediaInfoMemo(post);
const iframeThumbnail = commentMediaInfo?.patternThumbnailUrl || commentMediaInfo?.thumbnail;
const hasThumbnail =
link &&
commentMediaInfo &&
(commentMediaInfo.type === 'image' ||
commentMediaInfo.type === 'video' ||
(commentMediaInfo.type === 'webpage' && commentMediaInfo.thumbnail) ||
(commentMediaInfo.type === 'iframe' && iframeThumbnail))
? true
: false;
const initialButtonType = hasThumbnail || commentMediaInfo?.type === 'audio' || commentMediaInfo?.type === 'iframe' ? 'playButton' : 'textButton';
const [buttonType, setButtonType] = useState<'textButton' | 'playButton' | 'closeButton'>(initialButtonType);
const toggleExpando = () => {
setExpandoVisible(!expandoVisible);
setButtonType(buttonType === 'closeButton' ? 'textButton' : 'closeButton');
};
const { t } = useTranslation();
const [expanded, setExpanded] = useState(false);
const toggleExpanded = () => setExpanded(!expanded);
const postTitleOrContent = (title?.length > 90 ? title?.slice(0, 90) + '...' : title) || (content?.length > 90 ? content?.slice(0, 90) + '...' : content);
const commentMediaInfo = utils.getCommentMediaInfoMemoized(content);
const hasThumbnail = utils.hasThumbnail(commentMediaInfo, link);
// TEMPORARY: e.preventDefault() in Link elements because routes aren't implemented yet
return (
<div className={styles.container} key={index}>
@@ -53,7 +43,7 @@ const FeedPost: FC<FeedPostProps> = ({ post, index }) => {
<div className={styles.topMatter}>
<p className={styles.title}>
<Link className={styles.link} to={`p/${subplebbitAddress}/c/${cid}`} onClick={(e) => e.preventDefault()}>
{(title?.length > 90 ? title?.slice(0, 90) + '...' : title) || (content?.length > 90 ? content?.slice(0, 90) + '...' : content)}
{postTitleOrContent}
</Link>
{flair && (
<>
@@ -72,16 +62,7 @@ const FeedPost: FC<FeedPostProps> = ({ post, index }) => {
</span>
)}
</p>
{content && !link && (
<div className={styles.buttonWrapper} onClick={toggleExpando}>
<div className={`${styles.buttonCommon} ${styles[buttonType]}`}></div>
</div>
)}
{link && (
<div className={styles.buttonWrapper} onClick={toggleExpando}>
<div className={`${styles.buttonCommon} ${styles[buttonType]}`}></div>
</div>
)}
<ExpandButton commentCid={cid} expanded={expanded} hasThumbnail={hasThumbnail} toggleExpanded={toggleExpanded} />
<p className={styles.tagline}>
{t('feed_post_submitted')} {utils.getFormattedTime(timestamp)} {t('feed_post_by')}&nbsp;
<Link className={styles.author} to={`u/${author.shortAddress}`} onClick={(e) => e.preventDefault()}>
@@ -94,51 +75,7 @@ const FeedPost: FC<FeedPostProps> = ({ post, index }) => {
</p>
<PostTools commentCid={cid} />
</div>
<div className={expandoVisible ? styles.expando : styles.expandoHidden}>
{link && (
<div className={styles.mediaPreview}>
<Link to={`p/${subplebbitAddress}/c/${cid}`} onClick={(e) => e.preventDefault()}>
{commentMediaInfo?.type === 'image' && (
<img
src={commentMediaInfo.url}
alt='thumbnail'
onError={(e) => {
e.currentTarget.alt = '';
}}
/>
)}
{commentMediaInfo?.type === 'video' &&
(commentMediaInfo.thumbnail ? (
<img
src={commentMediaInfo.thumbnail}
alt='thumbnail'
onError={(e) => {
e.currentTarget.alt = '';
}}
/>
) : (
<video src={commentMediaInfo.url} controls />
))}
{commentMediaInfo?.type === 'webpage' && commentMediaInfo.thumbnail && (
<img
src={commentMediaInfo.thumbnail}
alt='thumbnail'
onError={(e) => {
e.currentTarget.alt = '';
}}
/>
)}
{commentMediaInfo?.type === 'audio' && <audio src={commentMediaInfo.url} controls />}
{commentMediaInfo?.type === 'iframe' && expandoVisible && <Embed url={commentMediaInfo.url} />}
</Link>
</div>
)}
{content && (
<div className={styles.usertext}>
<div className={styles.markdown}>{content}</div>
</div>
)}
</div>
<Expando commentCid={cid} expanded={expanded} />
</div>
</div>
);

View File

@@ -12,7 +12,7 @@ const Thumbnail: FC<ThumbnailProps> = ({ commentCid }) => {
const comment = useComment({ commentCid });
const subplebbitAddress = comment.subplebbitAddress;
const { cid, linkHeight, linkWidth } = comment;
const commentMediaInfo = utils.getCommentMediaInfoMemo(comment);
const commentMediaInfo = utils.getCommentMediaInfoMemoized(comment);
const iframeThumbnail = commentMediaInfo?.patternThumbnailUrl || commentMediaInfo?.thumbnail;
let displayWidth, displayHeight, hasLinkDimensions;

View File

@@ -4,6 +4,13 @@ import extName from 'ext-name';
import { Comment } from '@plebbit/plebbit-react-hooks';
import { canEmbed } from '../components/embed/embed';
interface CommentMediaInfo {
url: string;
type: string;
thumbnail?: string;
patternThumbnailUrl?: string;
}
const getCommentMediaInfo = (comment: Comment) => {
if (!comment?.thumbnailUrl && !comment?.link) {
return;
@@ -58,7 +65,7 @@ const getCommentMediaInfo = (comment: Comment) => {
}
};
const getCommentMediaInfoMemo = memoize(getCommentMediaInfo, { max: 1000 });
const getCommentMediaInfoMemoized = memoize(getCommentMediaInfo, { max: 1000 });
const getFormattedTime = (unixTimestamp: number): string => {
const currentTime = Date.now() / 1000;
@@ -104,10 +111,23 @@ const getHostname = (url: string) => {
}
};
const hasThumbnail = (commentMediaInfo: CommentMediaInfo | undefined, link: string | undefined): boolean => {
const iframeThumbnail = commentMediaInfo?.patternThumbnailUrl || commentMediaInfo?.thumbnail;
return link &&
commentMediaInfo &&
(commentMediaInfo.type === 'image' ||
commentMediaInfo.type === 'video' ||
(commentMediaInfo.type === 'webpage' && commentMediaInfo.thumbnail) ||
(commentMediaInfo.type === 'iframe' && iframeThumbnail))
? true
: false;
};
const utils = {
getCommentMediaInfoMemo,
getCommentMediaInfoMemoized,
getFormattedTime,
getHostname,
hasThumbnail,
};
export default utils;