diff --git a/src/components/comment-media/comment-media.module.css b/src/components/comment-media/comment-media.module.css
index bc16045b..f3732cba 100644
--- a/src/components/comment-media/comment-media.module.css
+++ b/src/components/comment-media/comment-media.module.css
@@ -18,6 +18,15 @@
display: inline-block;
}
+@media (max-width: 640px) {
+ .subplebbitAvatar {
+ max-width: 125px;
+ max-height: 125px;
+ margin: 3px 10px 5px 5px;
+ display: inline-block;
+ }
+}
+
.thumbnailSmall {
background-color: var(--media-thumbnail-background-color);
width: var(--width);
@@ -103,4 +112,8 @@
.mediaDesktopOp img, .mediaDesktopOp video, .mediaDesktopOp iframe, .mediaDesktopOp audio {
padding: 3px 20px 5px 0;
+}
+
+.fileDeleted {
+ image-rendering: pixelated;
}
\ No newline at end of file
diff --git a/src/components/comment-media/comment-media.tsx b/src/components/comment-media/comment-media.tsx
index b147b1df..0df99bc7 100644
--- a/src/components/comment-media/comment-media.tsx
+++ b/src/components/comment-media/comment-media.tsx
@@ -1,10 +1,10 @@
-import React from 'react';
+import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import styles from './comment-media.module.css';
import { CommentMediaInfo, getDisplayMediaInfoType, getHasThumbnail } from '../../lib/utils/media-utils';
import { getHostname } from '../../lib/utils/url-utils';
import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame';
-import useWindowWidth from '../../hooks/use-window-width';
+import useIsMobile from '../../hooks/use-is-mobile';
import Embed, { canEmbed } from '../embed';
interface MediaProps {
@@ -18,29 +18,13 @@ interface MediaProps {
toggleExpanded?: () => void;
}
-interface ThumbnailProps {
- style: React.CSSProperties;
- children: React.ReactNode;
- thumbnailSmallPadding?: string;
-}
-
-const ThumbnailBig = ({ style, children }: ThumbnailProps) => (
-
- {children}
-
-);
-
-const ThumbnailSmall = ({ style, children, thumbnailSmallPadding }: ThumbnailProps) => (
-
- {children}
-
-);
-
const Thumbnail = ({ commentMediaInfo, isOutOfFeed, isReply, linkHeight, linkWidth, setShowThumbnail }: MediaProps) => {
const { patternThumbnailUrl, thumbnail, type, url } = commentMediaInfo || {};
+ const [hasError, setHasError] = useState(false);
+ const handleError = () => setHasError(true);
let displayWidth, displayHeight;
- const isMobile = useWindowWidth() < 640;
+ const isMobile = useIsMobile();
const maxThumbnailSize = isMobile || isReply ? 125 : 250;
if (linkWidth && linkHeight) {
@@ -63,7 +47,7 @@ const Thumbnail = ({ commentMediaInfo, isOutOfFeed, isReply, linkHeight, linkWid
const hasThumbnail = getHasThumbnail(commentMediaInfo, url);
if (type === 'image') {
- thumbnailComponent =

setShowThumbnail(false)} />;
+ thumbnailComponent =

setShowThumbnail(false)} />;
} else if (type === 'video') {
thumbnailComponent = thumbnail ?

:
);
};
diff --git a/src/components/post/post-desktop/post-menu-desktop/index.ts b/src/components/post/post-desktop/post-menu-desktop/index.ts
new file mode 100644
index 00000000..54cc808b
--- /dev/null
+++ b/src/components/post/post-desktop/post-menu-desktop/index.ts
@@ -0,0 +1 @@
+export { default } from './post-menu-desktop';
diff --git a/src/components/post/post-desktop/post-menu-desktop/post-menu-desktop.module.css b/src/components/post/post-desktop/post-menu-desktop/post-menu-desktop.module.css
new file mode 100644
index 00000000..0e585a4b
--- /dev/null
+++ b/src/components/post/post-desktop/post-menu-desktop/post-menu-desktop.module.css
@@ -0,0 +1,116 @@
+.postMenu {
+ position: absolute;
+ background-color: var(--post-menu-desktop-background-color);
+ border-bottom: var(--post-menu-desktop-border-bottom);
+ border-right: var(--post-menu-desktop-border-right);
+ outline: none;
+ z-index: 2;
+}
+
+.postMenuBtnWrapper {
+ position: relative;
+ display: inline-block;
+ padding-left: 15px;
+}
+
+.postMenuBtn {
+ color: var(--post-menu-desktop-button-color);
+ transition: transform 0.1s;
+ transform: rotate(90deg);
+ position: absolute;
+ bottom: -4px;
+}
+
+.postMenuBtn:hover {
+ cursor: pointer;
+ color: var(--post-menu-desktop-button-color-hover);
+}
+
+.postMenuItem {
+ cursor: pointer;
+ padding: 2px 4px;
+ border: 1px solid var(--post-menu-desktop-item-border-color);
+ background-color: var(--post-menu-desktop-item-background-color);
+ border-bottom: none;
+}
+
+.postMenuItem:hover {
+ background-color: var(--post-menu-desktop-item-hover-background-color);
+}
+
+.dropdown {
+ position: relative;
+}
+
+.dropdownMenu {
+ position: absolute;
+ left: 100%;
+ top: -1px;
+ display: none;
+ border-bottom: var(--post-menu-desktop-border-bottom);
+ border-right: var(--post-menu-desktop-border-right);
+}
+
+.dropdown a {
+ text-decoration: none;
+ color: inherit;
+}
+
+.dropdown:hover .dropdownMenu {
+ display: block;
+}
+
+.postMenuBtnCatalogWrapper {
+ left: 5px;
+ top: 5px;
+ position: relative;
+}
+
+.postMenuBtnCatalog {
+ position: absolute;
+ bottom: 5px;
+ line-height: 1em;
+ color: var(--catalog-post-menu-desktop-btn-color);
+ opacity: var(--catalog-post-menu-desktop-btn-opacity);
+ transition: transform 0.1s;
+}
+
+.postMenuBtnCatalog:hover {
+ cursor: pointer;
+ color: var(--catalog-post-menu-desktop-btn-hover-color);
+ opacity: var(--catalog-post-menu-desktop-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;
+ text-align: center;
+ font-weight: 700;
+ font-size: 16px;
+ }
+
+ .postMenuBtnWrapper {
+ position: relative;
+ padding-right: 12px;
+ }
+}
+
+@media (min-width: 640px) {
+ .postMenuBtn {
+ right: -2px;
+ opacity: var(--post-menu-desktop-btn-opacity);
+ }
+
+ .stickyIconWrapper {
+ padding-right: 18px;
+ }
+}
diff --git a/src/components/post/post-menu/post-menu.tsx b/src/components/post/post-desktop/post-menu-desktop/post-menu-desktop.tsx
similarity index 59%
rename from src/components/post/post-menu/post-menu.tsx
rename to src/components/post/post-desktop/post-menu-desktop/post-menu-desktop.tsx
index 241ff171..a363b952 100644
--- a/src/components/post/post-menu/post-menu.tsx
+++ b/src/components/post/post-desktop/post-menu-desktop/post-menu-desktop.tsx
@@ -1,11 +1,14 @@
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 styles from './post-menu-desktop.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 {
+interface PostMenuDesktopProps {
cid: string;
isDescription?: boolean;
isRules?: boolean;
@@ -13,7 +16,7 @@ interface PostMenuProps {
onClose: () => void;
}
-const CopyLinkButton = ({ cid, subplebbitAddress, onClose }: PostMenuProps) => {
+const CopyLinkButton = ({ cid, subplebbitAddress, onClose }: PostMenuDesktopProps) => {
return (
{
@@ -28,16 +31,23 @@ const CopyLinkButton = ({ cid, subplebbitAddress, onClose }: PostMenuProps) => {
const ImageSearchButton = ({ url, onClose }: { url: string; onClose: () => void }) => {
const [isImageSearchMenuOpen, setIsImageSearchMenuOpen] = useState(false);
+
+ const { refs, floatingStyles } = useFloating({
+ placement: 'right-start',
+ middleware: [flip(), shift({ padding: 10 })],
+ });
+
return (
setIsImageSearchMenuOpen(true)}
onMouseLeave={() => setIsImageSearchMenuOpen(false)}
+ ref={refs.setReference}
onClick={onClose}
>
Image search »
{isImageSearchMenuOpen && (
-
+
Google
@@ -53,19 +63,26 @@ const ImageSearchButton = ({ url, onClose }: { url: string; onClose: () => void
);
};
-const ViewOnButton = ({ cid, isDescription, isRules, subplebbitAddress, onClose }: PostMenuProps) => {
+const ViewOnButton = ({ cid, isDescription, isRules, subplebbitAddress, onClose }: PostMenuDesktopProps) => {
const [isClientRedirectMenuOpen, setIsClientRedirectMenuOpen] = useState(false);
const viewOnOtherClientLink = `p/${subplebbitAddress}${isDescription || isRules ? '' : `/c/${cid}`}`;
+
+ const { refs, floatingStyles } = useFloating({
+ placement: 'right-start',
+ middleware: [flip(), shift({ padding: 10 })],
+ });
+
return (
setIsClientRedirectMenuOpen(true)}
onMouseLeave={() => setIsClientRedirectMenuOpen(false)}
+ ref={refs.setReference}
onClick={onClose}
>
View on »
{isClientRedirectMenuOpen && (
-
+
Seedit
@@ -78,17 +95,19 @@ const ViewOnButton = ({ cid, isDescription, isRules, subplebbitAddress, onClose
);
};
-const PostMenu = ({ post }: { post: Comment }) => {
- const { cid, isDescription, isRules, link, subplebbitAddress } = post;
+const PostMenuDesktop = ({ post }: { post: Comment }) => {
+ const { cid, isDescription, isRules, link, postCid, subplebbitAddress } = post || {};
const commentMediaInfo = getCommentMediaInfo(post);
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 +120,9 @@ const PostMenu = ({ post }: { post: Comment }) => {
return (
<>
-
+
setMenuBtnRotated((prev) => !prev)}
style={{ transform: menuBtnRotated ? 'rotate(90deg)' : 'rotate(0deg)' }}
@@ -111,17 +130,19 @@ const PostMenu = ({ post }: { post: Comment }) => {
▶
- {menuBtnRotated && (
-
-
- {cid && subplebbitAddress && }
- {link && isValidURL(link) && (type === 'image' || type === 'gif' || thumbnail) && url && }
-
-
-
- )}
+ {menuBtnRotated &&
+ createPortal(
+
+
+ {cid && subplebbitAddress && }
+ {link && isValidURL(link) && (type === 'image' || type === 'gif' || thumbnail) && url && }
+
+
+ ,
+ document.body,
+ )}
>
);
};
-export default PostMenu;
+export default PostMenuDesktop;
diff --git a/src/components/post/post-menu/index.ts b/src/components/post/post-menu/index.ts
deleted file mode 100644
index 7c04c4cc..00000000
--- a/src/components/post/post-menu/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from './post-menu';
diff --git a/src/components/post/post-menu/post-menu.module.css b/src/components/post/post-menu/post-menu.module.css
deleted file mode 100644
index a97282c7..00000000
--- a/src/components/post/post-menu/post-menu.module.css
+++ /dev/null
@@ -1,86 +0,0 @@
-.postMenu {
- position: absolute;
- background-color: var(--post-menu-background-color);
- border-bottom: var(--post-menu-border-bottom);
- border-right: var(--post-menu-border-right);
- outline: none;
- z-index: 2;
-}
-
-.postMenuBtnWrapper {
- position: relative;
- display: inline-block;
- padding-left: 15px;
-}
-
-.postMenuBtn {
- color: var(--post-menu-button-color);
- transition: transform 0.1s;
- transform: rotate(90deg);
- position: absolute;
- bottom: -4px;
-}
-
-.postMenuBtn:hover {
- cursor: pointer;
- color: var(--post-menu-button-color-hover);
-}
-
-.postMenuItem {
- cursor: pointer;
- padding: 2px 4px;
- border: 1px solid var(--post-menu-item-border-color);
- background-color: var(--post-menu-item-background-color);
- border-bottom: none;
-}
-
-.postMenuItem:hover {
- background-color: var(--post-menu-item-hover-background-color);
-}
-
-.dropdown {
- position: relative;
-}
-
-.dropdownMenu {
- position: absolute;
- left: 100%;
- top: -1px;
- display: none;
- border-bottom: var(--post-menu-border-bottom);
- border-right: var(--post-menu-border-right);
-}
-
-.dropdown a {
- text-decoration: none;
- color: inherit;
-}
-
-.dropdown:hover .dropdownMenu {
- display: block;
-}
-
-@media (max-width: 640px) {
- .postMenuBtn {
- width: 1em;
- text-align: center;
- font-weight: 700;
- font-size: 16px;
- }
-
- .postMenuBtnWrapper {
- position: relative;
- padding-right: 12px;
- }
-}
-
-@media (min-width: 640px) {
- .postMenuBtn {
- right: -2px;
- opacity: var(--post-menu-btn-opacity);
- }
-
- .stickyIconWrapper {
- padding-right: 18px;
- }
-}
diff --git a/src/components/post/post-mobile/post-menu-mobile/index.ts b/src/components/post/post-mobile/post-menu-mobile/index.ts
new file mode 100644
index 00000000..6fccc4be
--- /dev/null
+++ b/src/components/post/post-mobile/post-menu-mobile/index.ts
@@ -0,0 +1 @@
+export { default } from './post-menu-mobile';
diff --git a/src/components/post/post-mobile/post-menu-mobile/post-menu-mobile.module.css b/src/components/post/post-mobile/post-menu-mobile/post-menu-mobile.module.css
new file mode 100644
index 00000000..95ce0b45
--- /dev/null
+++ b/src/components/post/post-mobile/post-menu-mobile/post-menu-mobile.module.css
@@ -0,0 +1,42 @@
+.postMenuBtn {
+ color: var(--post-mobile-menu-button-color);
+ line-height: 1em;
+ width: 1em;
+ text-align: center;
+ transition: transform 0.1s;
+ transform: rotate(90deg);
+ -webkit-transform: rotate(90deg);
+ float: left;
+ font-weight: 700;
+ font-size: 16px;
+ height: 100%;
+}
+
+.postMenu {
+ font-size: 16px;
+ line-height: 2.5em;
+ background-color: var(--post-menu-mobile-background-color);
+ border: var(--post-menu-mobile-border);
+ border-right-width: var(---post-menu-mobile-border-right-width);
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ white-space: nowrap;
+}
+
+.postMenu a {
+ text-decoration: none;
+ color: inherit;
+}
+
+.postMenuItem {
+ cursor: pointer;
+ position: relative;
+ padding: 2px 4px;
+ vertical-align: middle;
+ border-bottom: var(--post-menu-mobile-item-border-bottom);
+}
+
+.postMenuItem:hover {
+ background-color: var(--post-menu-mobile-item-hover-background-color);
+}
\ No newline at end of file
diff --git a/src/components/post/post-mobile/post-menu-mobile/post-menu-mobile.tsx b/src/components/post/post-mobile/post-menu-mobile/post-menu-mobile.tsx
new file mode 100644
index 00000000..85ad88bb
--- /dev/null
+++ b/src/components/post/post-mobile/post-menu-mobile/post-menu-mobile.tsx
@@ -0,0 +1,102 @@
+import { useState } from 'react';
+import { createPortal } from 'react-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-mobile.module.css';
+import { getCommentMediaInfo } from '../../../../lib/utils/media-utils';
+import { copyShareLinkToClipboard, isValidURL } from '../../../../lib/utils/url-utils';
+
+interface PostMenuMobileProps {
+ cid: string;
+ isDescription?: boolean;
+ isRules?: boolean;
+ subplebbitAddress: string;
+ onClose: () => void;
+}
+
+const CopyLinkButton = ({ cid, subplebbitAddress, onClose }: PostMenuMobileProps) => {
+ return (
+ {
+ copyShareLinkToClipboard(subplebbitAddress, cid);
+ onClose();
+ }}
+ >
+
Copy link
+
+ );
+};
+
+const ImageSearchButtons = ({ url, onClose }: { url: string; onClose: () => void }) => {
+ return (
+
+ );
+};
+
+const ViewOnButtons = ({ cid, isDescription, isRules, subplebbitAddress, onClose }: PostMenuMobileProps) => {
+ const viewOnOtherClientLink = `p/${subplebbitAddress}${isDescription || isRules ? '' : `/c/${cid}`}`;
+
+ return (
+
+ );
+};
+
+const PostMenuMobile = ({ post }: { post: Comment }) => {
+ const { cid, isDescription, isRules, link, subplebbitAddress } = post || {};
+ const commentMediaInfo = getCommentMediaInfo(post);
+ const { thumbnail, type, url } = commentMediaInfo || {};
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
+
+ const { refs, floatingStyles, context } = useFloating({
+ placement: 'bottom-start',
+ open: isMenuOpen,
+ onOpenChange: setIsMenuOpen,
+ middleware: [offset({ mainAxis: 3, crossAxis: 8 }), flip(), shift({ padding: 10 })],
+ whileElementsMounted: autoUpdate,
+ });
+ const click = useClick(context);
+ const dismiss = useDismiss(context);
+ const role = useRole(context);
+ const { getReferenceProps, getFloatingProps } = useInteractions([click, dismiss, role]);
+ const headingId = useId();
+
+ const handleClose = () => setIsMenuOpen(false);
+
+ return (
+ <>
+ setIsMenuOpen((prev) => !prev)} ref={refs.setReference} {...getReferenceProps()}>
+ ...
+
+ {isMenuOpen &&
+ createPortal(
+
+
+ {cid && subplebbitAddress && }
+ {link && isValidURL(link) && (type === 'image' || type === 'gif' || thumbnail) && url && }
+
+
+ ,
+ document.body,
+ )}
+ >
+ );
+};
+
+export default PostMenuMobile;
diff --git a/src/components/post/post-mobile/post-mobile.tsx b/src/components/post/post-mobile/post-mobile.tsx
index 3ad4c4ed..4c7128d9 100644
--- a/src/components/post/post-mobile/post-mobile.tsx
+++ b/src/components/post/post-mobile/post-mobile.tsx
@@ -3,6 +3,7 @@ import { Trans, useTranslation } from 'react-i18next';
import { Link, useLocation, useParams } from 'react-router-dom';
import { useAccount } from '@plebbit/plebbit-react-hooks';
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
+import styles from '../post.module.css';
import { getCommentMediaInfo, getHasThumbnail } from '../../../lib/utils/media-utils';
import { getFormattedDate } from '../../../lib/utils/time-utils';
import { isAllView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../../lib/utils/view-utils';
@@ -12,8 +13,8 @@ import useStateString from '../../../hooks/use-state-string';
import CommentMedia from '../../comment-media';
import LoadingEllipsis from '../../loading-ellipsis';
import Markdown from '../../markdown';
+import PostMenuMobile from './post-menu-mobile';
import { PostProps } from '../post';
-import styles from '../post.module.css';
import _ from 'lodash';
const PostInfoAndMedia = ({ openReplyModal, post, roles }: PostProps) => {
@@ -45,9 +46,7 @@ const PostInfoAndMedia = ({ openReplyModal, post, roles }: PostProps) => {
return (
<>
-
- ...
-
+
{shortDisplayName || _.capitalize(t('anonymous'))}
@@ -56,12 +55,12 @@ const PostInfoAndMedia = ({ openReplyModal, post, roles }: PostProps) => {
{!(isDescription || isRules) && (u/{shortAddress || accountShortAddress})}
{pinned && (
-
+
)}
{locked && (
-
+
)}
{title && (
diff --git a/src/components/post/post.tsx b/src/components/post/post.tsx
index ec65fdaa..2dc6569b 100644
--- a/src/components/post/post.tsx
+++ b/src/components/post/post.tsx
@@ -1,5 +1,5 @@
import { Role, useSubplebbit } from '@plebbit/plebbit-react-hooks';
-import useWindowWidth from '../../hooks/use-window-width';
+import useIsMobile from '../../hooks/use-is-mobile';
import styles from './post.module.css';
import PostDesktop from './post-desktop';
import PostMobile from './post-mobile';
@@ -15,7 +15,7 @@ export interface PostProps {
const Post = ({ post, showAllReplies = false, openReplyModal }: PostProps) => {
const subplebbit = useSubplebbit({ subplebbitAddress: post?.subplebbitAddress });
- const isMobile = useWindowWidth() < 640;
+ const isMobile = useIsMobile();
return (
diff --git a/src/components/reply-modal/reply-modal.tsx b/src/components/reply-modal/reply-modal.tsx
index 35d7eac7..17bed746 100644
--- a/src/components/reply-modal/reply-modal.tsx
+++ b/src/components/reply-modal/reply-modal.tsx
@@ -6,7 +6,7 @@ import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
import { setAccount, useAccount } from '@plebbit/plebbit-react-hooks';
import { isValidURL } from '../../lib/utils/url-utils';
import useReply from '../../hooks/use-reply';
-import useWindowWidth from '../../hooks/use-window-width';
+import useIsMobile from '../../hooks/use-is-mobile';
import styles from './reply-modal.module.css';
import _ from 'lodash';
@@ -51,7 +51,7 @@ const ReplyModal = ({ closeModal, parentCid, scrollY }: ReplyModalProps) => {
}, [replyIndex, resetContent, closeModal]);
const nodeRef = useRef
(null);
- const isMobile = useWindowWidth() < 640;
+ const isMobile = useIsMobile();
// on mobile, the position is absolute instead of fixed, so we need to calculate the top position
useEffect(() => {
diff --git a/src/hooks/use-is-mobile.ts b/src/hooks/use-is-mobile.ts
new file mode 100644
index 00000000..fc7cd9f6
--- /dev/null
+++ b/src/hooks/use-is-mobile.ts
@@ -0,0 +1,8 @@
+import useWindowWidth from './use-window-width';
+
+const useIsMobile = () => {
+ const windowWidth = useWindowWidth();
+ return windowWidth < 640;
+};
+
+export default useIsMobile;
diff --git a/src/hooks/use-reply-modal.ts b/src/hooks/use-reply-modal.ts
index acb4a8df..d15cc2b1 100644
--- a/src/hooks/use-reply-modal.ts
+++ b/src/hooks/use-reply-modal.ts
@@ -1,12 +1,12 @@
import { useState, useCallback } from 'react';
-import useWindowWidth from './use-window-width';
+import useIsMobile from './use-is-mobile';
const useReplyModal = () => {
const [showReplyModal, setShowReplyModal] = useState(false);
const [activeCid, setActiveCid] = useState(null);
// on mobile, the position is absolute instead of fixed, so we need to calculate the top position
- const isMobile = useWindowWidth() < 640;
+ const isMobile = useIsMobile();
const [scrollY, setScrollY] = useState(0);
const closeModal = useCallback(() => {
diff --git a/src/hooks/use-subplebbits-stats.ts b/src/hooks/use-subplebbits-stats.ts
index a03acdba..075a9e8f 100644
--- a/src/hooks/use-subplebbits-stats.ts
+++ b/src/hooks/use-subplebbits-stats.ts
@@ -2,6 +2,8 @@ import { useEffect, useMemo } from 'react';
import { useAccount, useSubplebbits } from '@plebbit/plebbit-react-hooks';
import { create } from 'zustand';
+const pendingFetchCid: { [cid: string]: boolean } = {};
+
const useSubplebbitsStats = (options: any) => {
const { subplebbitAddresses, accountName } = options || {};
const account = useAccount({ accountName });
@@ -15,13 +17,15 @@ const useSubplebbitsStats = (options: any) => {
}
subplebbits.forEach((subplebbit) => {
- if (subplebbit && subplebbit.statsCid && !subplebbitsStats[subplebbit.address]) {
+ if (subplebbit && subplebbit.statsCid && !subplebbitsStats[subplebbit.address] && !pendingFetchCid[subplebbit.statsCid]) {
+ pendingFetchCid[subplebbit.statsCid] = true;
account.plebbit
.fetchCid(subplebbit.statsCid)
.then((fetchedStats: any) => {
setSubplebbitStats(subplebbit.address, JSON.parse(fetchedStats));
})
.catch((error: any) => {
+ pendingFetchCid[subplebbit.statsCid] = false;
console.error('Fetching subplebbit stats failed', { subplebbitAddress: subplebbit.address, error });
});
}
diff --git a/src/themes.css b/src/themes.css
index 693243e0..92b938ea 100644
--- a/src/themes.css
+++ b/src/themes.css
@@ -25,10 +25,10 @@
--body-font-size: 13px;
/* catalog post */
- --catalog-post-menu-btn-color: navy;
- --catalog-post-menu-btn-hover-color: red;
- --catalog-post-menu-btn-opacity: 0.5;
- --catalog-post-menu-btn-hover-opacity: 1;
+ --catalog-post-menu-desktop-btn-color: navy;
+ --catalog-post-menu-desktop-btn-hover-color: red;
+ --catalog-post-menu-desktop-btn-opacity: 0.5;
+ --catalog-post-menu-desktop-btn-hover-opacity: 1;
/* challenge modal */
--challenge-modal-background-color: #f0e0d6;
@@ -97,9 +97,9 @@
--post-subject-font-weight: 700;
--post-name-text-color: #117743;
--post-name-font-weight: 700;
- --post-menu-button-color: #000080;
- --post-menu-btn-opacity: 0.8;
- --post-menu-button-color-hover: red;
+ --post-menu-desktop-button-color: #000080;
+ --post-menu-desktop-btn-opacity: 0.8;
+ --post-menu-desktop-button-color-hover: red;
--post-greentext-color: #789922;
--post-replies-expand-button-background-image: url("/public/assets/buttons/plus-red.png");
@@ -119,13 +119,20 @@
--post-form-field-input-focus-border: 1px solid #ea8;
--post-form-field-input-color: #000;
- /* post menu */
- --post-menu-border-bottom: 2px solid #d9bfb7;
- --post-menu-border-right: 1px solid #d9bfb7;
- --post-menu-background-color: #f0e0d6;
- --post-menu-item-border-color: #d9bfb7;
- --post-menu-item-background-color: #f0e0d6;
- --post-menu-item-hover-background-color: #ffe;
+ /* post menu desktop */
+ --post-menu-desktop-border-bottom: 2px solid #d9bfb7;
+ --post-menu-desktop-border-right: 1px solid #d9bfb7;
+ --post-menu-desktop-background-color: #f0e0d6;
+ --post-menu-desktop-item-border-color: #d9bfb7;
+ --post-menu-desktop-item-background-color: #f0e0d6;
+ --post-menu-desktop-item-hover-background-color: #ffe;
+
+ /* post menu mobile */
+ --post-menu-mobile-background-color: #f0e0d6;
+ --post-menu-mobile-border: 1px solid #d9bfb7;
+ --post-menu-mobile-border-right-width: 2px;
+ --post-menu-mobile-item-border-bottom: 1px solid #d9bfb7;
+ --post-menu-mobile-item-hover-background-color: #ffe;
/* post mobile */
--post-mobile-background-color: #f5e9e1;
@@ -204,10 +211,10 @@
--body-font-size: 13px;
/* catalog post */
- --catalog-post-menu-btn-color: navy;
- --catalog-post-menu-btn-hover-color: #d00;
- --catalog-post-menu-btn-opacity: 0.5;
- --catalog-post-menu-btn-hover-opacity: 1;
+ --catalog-post-menu-desktop-btn-color: navy;
+ --catalog-post-menu-desktop-btn-hover-color: #d00;
+ --catalog-post-menu-desktop-btn-opacity: 0.5;
+ --catalog-post-menu-desktop-btn-hover-opacity: 1;
/* challenge modal */
--challenge-modal-background-color: #d6daf0;
@@ -275,9 +282,9 @@
--post-subject-font-weight: 700;
--post-name-text-color: #117743;
--post-name-font-weight: 700;
- --post-menu-button-color: #34345c;
- --post-menu-btn-opacity: 0.8;
- --post-menu-button-color-hover: #d00;
+ --post-menu-desktop-button-color: #34345c;
+ --post-menu-desktop-btn-opacity: 0.8;
+ --post-menu-desktop-button-color-hover: #d00;
--post-greentext-color: #789922;
--post-replies-expand-button-background-image: url("/public/assets/buttons/plus-blue.png");
@@ -297,13 +304,20 @@
--post-form-field-input-focus-border: 1px solid #98e;
--post-form-field-input-color: #000;
- /* post menu */
- --post-menu-border-bottom: 2px solid #b7c5d9;
- --post-menu-border-right: 1px solid #b7c5d9;
- --post-menu-background-color: #d6daf0;
- --post-menu-item-border-color: #b7c5d9;
- --post-menu-item-background-color: #d6daf0;
- --post-menu-item-hover-background-color: #eef2ff;
+ /* post menu desktop */
+ --post-menu-desktop-border-bottom: 2px solid #b7c5d9;
+ --post-menu-desktop-border-right: 1px solid #b7c5d9;
+ --post-menu-desktop-background-color: #d6daf0;
+ --post-menu-desktop-item-border-color: #b7c5d9;
+ --post-menu-desktop-item-background-color: #d6daf0;
+ --post-menu-desktop-item-hover-background-color: #eef2ff;
+
+ /* post menu mobile */
+ --post-menu-mobile-background-color: #d6daf0;
+ --post-menu-mobile-border: 1px solid #b7c5d9;
+ --post-menu-mobile-border-right-width: 2px;
+ --post-menu-mobile-item-border-bottom: 1px solid #b7c5d9;
+ --post-menu-mobile-item-hover-background-color: #eef2ff;
/* post mobile */
--post-mobile-background-color: #d6daf0;
@@ -377,10 +391,10 @@
--board-address-font-size: 10pt;
/* catalog post */
- --catalog-post-menu-btn-color: navy;
- --catalog-post-menu-btn-hover-color: red;
- --catalog-post-menu-btn-opacity: 0.5;
- --catalog-post-menu-btn-hover-opacity: 1;
+ --catalog-post-menu-desktop-btn-color: navy;
+ --catalog-post-menu-desktop-btn-hover-color: red;
+ --catalog-post-menu-desktop-btn-opacity: 0.5;
+ --catalog-post-menu-desktop-btn-hover-opacity: 1;
/* challenge modal */
--challenge-modal-background-color: #f0e0d6;
@@ -429,9 +443,9 @@
--post-subject-font-weight: 700;
--post-name-text-color: #117743;
--post-name-font-weight: 700;
- --post-menu-button-color: #000080;
- --post-menu-btn-opacity: 0.8;
- --post-menu-button-color-hover: red;
+ --post-menu-desktop-button-color: #000080;
+ --post-menu-desktop-btn-opacity: 0.8;
+ --post-menu-desktop-button-color-hover: red;
--post-greentext-color: #789922;
--post-replies-expand-button-background-image: url("/public/assets/buttons/plus-red.png");
@@ -445,13 +459,20 @@
--post-form-field-title-font-size: 16px;
--post-form-field-input-color: #000;
- /* post menu */
- --post-menu-border-bottom: 1px solid #d9bfb7;
- --post-menu-border-right: unset;
- --post-menu-background-color: #f0e0d6;
- --post-menu-item-border-color: #d9bfb7;
- --post-menu-item-background-color: #f0e0d6;
- --post-menu-item-hover-background-color: #ffe;
+ /* post menu desktop */
+ --post-menu-desktop-border-bottom: 1px solid #d9bfb7;
+ --post-menu-desktop-border-right: unset;
+ --post-menu-desktop-background-color: #f0e0d6;
+ --post-menu-desktop-item-border-color: #d9bfb7;
+ --post-menu-desktop-item-background-color: #f0e0d6;
+ --post-menu-desktop-item-hover-background-color: #ffe;
+
+ /* post menu mobile */
+ --post-menu-mobile-background-color: #f0e0d6;
+ --post-menu-mobile-border: 1px solid #d9bfb7;
+ --post-menu-mobile-border-right-width: 2px;
+ --post-menu-mobile-item-border-bottom: 1px solid #d9bfb7;
+ --post-menu-mobile-item-hover-background-color: #ffe;
/* post mobile */
--post-mobile-background-color: #f5e9e1;
@@ -524,10 +545,10 @@
--body-font-size: 16px;
/* catalog post */
- --catalog-post-menu-btn-color: navy;
- --catalog-post-menu-btn-hover-color: red;
- --catalog-post-menu-btn-opacity: 0.5;
- --catalog-post-menu-btn-hover-opacity: 1;
+ --catalog-post-menu-desktop-btn-color: navy;
+ --catalog-post-menu-desktop-btn-hover-color: red;
+ --catalog-post-menu-desktop-btn-opacity: 0.5;
+ --catalog-post-menu-desktop-btn-hover-opacity: 1;
/* challenge modal */
--challenge-modal-background-color: #d6daf0;
@@ -586,9 +607,9 @@
--post-subject-font-weight: 700;
--post-name-text-color: #117743;
--post-name-font-weight: 700;
- --post-menu-button-color: #34345c;
- --post-menu-btn-opacity: 0.8;
- --post-menu-button-color-hover: #d00;
+ --post-menu-desktop-button-color: #34345c;
+ --post-menu-desktop-btn-opacity: 0.8;
+ --post-menu-desktop-button-color-hover: #d00;
--post-greentext-color: #789922;
--post-replies-expand-button-background-image: url("/public/assets/buttons/plus-blue.png");
@@ -602,13 +623,20 @@
--post-form-field-title-font-size: 16px;
--post-form-field-input-color: #000;
- /* post menu */
- --post-menu-border-bottom: 1px solid #b7c5d9;
- --post-menu-border-right: unset;
- --post-menu-background-color: #d6daf0;
- --post-menu-item-border-color: #b7c5d9;
- --post-menu-item-background-color: #d6daf0;
- --post-menu-item-hover-background-color: #eef2ff;
+ /* post menu desktop */
+ --post-menu-desktop-border-bottom: 1px solid #b7c5d9;
+ --post-menu-desktop-border-right: unset;
+ --post-menu-desktop-background-color: #d6daf0;
+ --post-menu-desktop-item-border-color: #b7c5d9;
+ --post-menu-desktop-item-background-color: #d6daf0;
+ --post-menu-desktop-item-hover-background-color: #eef2ff;
+
+ /* post menu mobile */
+ --post-menu-mobile-background-color: #d6daf0;
+ --post-menu-mobile-border: 1px solid #b7c5d9;
+ --post-menu-mobile-border-right-width: 2px;
+ --post-menu-mobile-item-border-bottom: 1px solid #b7c5d9;
+ --post-menu-mobile-item-hover-background-color: #eef2ff;
/* post mobile */
--post-mobile-background-color: #d6daf0;
@@ -678,10 +706,10 @@
--board-address-font-size: x-small;
/* catalog post */
- --catalog-post-menu-btn-color: #5f89ac;
- --catalog-post-menu-btn-hover-color: #5f89ac;
- --catalog-post-menu-btn-opacity: 0.5;
- --catalog-post-menu-btn-hover-opacity: 1;
+ --catalog-post-menu-desktop-btn-color: #5f89ac;
+ --catalog-post-menu-desktop-btn-hover-color: #5f89ac;
+ --catalog-post-menu-desktop-btn-opacity: 0.5;
+ --catalog-post-menu-desktop-btn-hover-opacity: 1;
/* challenge modal */
--challenge-modal-background-color: #282a2e;
@@ -744,9 +772,9 @@
--post-subject-font-weight: 700;
--post-name-text-color: #c5c8c6;
--post-name-font-weight: 700;
- --post-menu-button-color: #5F89AC;
- --post-menu-btn-opacity: 0.8;
- --post-menu-button-color-hover: #81a2be;
+ --post-menu-desktop-button-color: #5F89AC;
+ --post-menu-desktop-btn-opacity: 0.8;
+ --post-menu-desktop-button-color-hover: #81a2be;
--post-greentext-color: #b5bd68;
--post-replies-expand-button-background-image: url("/public/assets/buttons/plus-dark.png");
@@ -767,13 +795,20 @@
--post-form-field-input-appearance: none;
--post-form-field-input-outline: none;
- /* post menu */
- --post-menu-border-bottom: 1px solid #000;
- --post-menu-border-right: unset;
- --post-menu-background-color: #282a2e;
- --post-menu-item-border-color: #000;
- --post-menu-item-background-color: #282a2e;
- --post-menu-item-hover-background-color: #1d1f21;
+ /* post menu desktop */
+ --post-menu-desktop-border-bottom: 1px solid #000;
+ --post-menu-desktop-border-right: unset;
+ --post-menu-desktop-background-color: #282a2e;
+ --post-menu-desktop-item-border-color: #000;
+ --post-menu-desktop-item-background-color: #282a2e;
+ --post-menu-desktop-item-hover-background-color: #1d1f21;
+
+ /* post menu mobile */
+ --post-menu-mobile-background-color: #282a2e;
+ --post-menu-mobile-border: 1px solid #000;
+ --post-menu-mobile-border-right-width: 2px;
+ --post-menu-mobile-item-border-bottom: 1px solid #000;
+ --post-menu-mobile-item-hover-background-color: #1d1f21;
/* post mobile */
--post-mobile-background-color: #282A2E;
@@ -858,10 +893,10 @@
--board-address-font-size: x-small;
/* catalog post */
- --catalog-post-menu-btn-color: #f60;
- --catalog-post-menu-btn-hover-color: #f30;
- --catalog-post-menu-btn-opacity: 0.5;
- --catalog-post-menu-btn-hover-opacity: 1;
+ --catalog-post-menu-desktop-btn-color: #f60;
+ --catalog-post-menu-desktop-btn-hover-color: #f30;
+ --catalog-post-menu-desktop-btn-opacity: 0.5;
+ --catalog-post-menu-desktop-btn-hover-opacity: 1;
/* challenge modal */
--challenge-modal-background-color: #ddd;
@@ -910,9 +945,9 @@
--post-subject-font-weight: 700;
--post-name-text-color: #004a99;
--post-name-font-weight: 700;
- --post-menu-button-color: #FF6600;
- --post-menu-btn-opacity: 0.8;
- --post-menu-button-color-hover: #FF3300;
+ --post-menu-desktop-button-color: #FF6600;
+ --post-menu-desktop-btn-opacity: 0.8;
+ --post-menu-desktop-button-color-hover: #FF3300;
--post-greentext-color: #789922;
--post-replies-expand-button-background-image: url("/public/assets/buttons/plus-photon.png");
@@ -951,13 +986,20 @@
--post-form-field-input-focus-border: 1px solid #ea8;
--post-form-field-input-color: #000;
- /* post menu */
- --post-menu-border-bottom: 1px solid #ccc;
- --post-menu-border-right: unset;
- --post-menu-background-color: #ddd;
- --post-menu-item-border-color: #ccc;
- --post-menu-item-background-color: #ddd;
- --post-menu-item-hover-background-color: #eee;
+ /* post menu desktop */
+ --post-menu-desktop-border-bottom: 1px solid #ccc;
+ --post-menu-desktop-border-right: unset;
+ --post-menu-desktop-background-color: #ddd;
+ --post-menu-desktop-item-border-color: #ccc;
+ --post-menu-desktop-item-background-color: #ddd;
+ --post-menu-desktop-item-hover-background-color: #eee;
+
+ /* post menu mobile */
+ --post-menu-mobile-background-color: #ddd;
+ --post-menu-mobile-border: 1px solid #ccc;
+ --post-menu-mobile-border-right-width: 2px;
+ --post-menu-mobile-item-border-bottom: 1px solid #ccc;
+ --post-menu-mobile-item-hover-background-color: #eee;
/* quotelink */
--post-quotelink-text-color: #f60;
diff --git a/src/views/home/home.tsx b/src/views/home/home.tsx
index 92fdadd1..404caa65 100644
--- a/src/views/home/home.tsx
+++ b/src/views/home/home.tsx
@@ -202,7 +202,7 @@ const PopularThreadCard = ({ post, boardTitle, boardShortAddress }: PopularThrea
{boardTitle || boardShortAddress}
-
+
diff --git a/src/views/not-found/not-found.tsx b/src/views/not-found/not-found.tsx
index 150ef906..7e206fe8 100644
--- a/src/views/not-found/not-found.tsx
+++ b/src/views/not-found/not-found.tsx
@@ -10,8 +10,7 @@ const totalNotFoundImages = 2;
const NotFoundImage = () => {
const [imagePath] = useState(() => {
const randomBannerIndex = Math.floor(Math.random() * totalNotFoundImages) + 1;
- const cacheBuster = new Date().getTime();
- return `assets/not-found/not-found-${randomBannerIndex}.jpg?cacheBuster=${cacheBuster}`;
+ return `/assets/not-found/not-found-${randomBannerIndex}.jpg`;
});
return

;