From a1b29661884f99f2a79d0f8f43909d44e93e9056 Mon Sep 17 00:00:00 2001 From: Martti Malmi Date: Thu, 7 Dec 2023 13:33:36 +0200 Subject: [PATCH] trending notes showable as grid --- .../app/src/Element/Feed/ImageGridItem.tsx | 41 +++++++++++++++++ packages/app/src/Element/Feed/RootTabs.tsx | 2 +- .../app/src/Element/Feed/TimelineRenderer.tsx | 45 +++---------------- .../src/Element/Trending/TrendingPosts.tsx | 39 +++++++++++----- 4 files changed, 77 insertions(+), 50 deletions(-) create mode 100644 packages/app/src/Element/Feed/ImageGridItem.tsx diff --git a/packages/app/src/Element/Feed/ImageGridItem.tsx b/packages/app/src/Element/Feed/ImageGridItem.tsx new file mode 100644 index 00000000..68e01e74 --- /dev/null +++ b/packages/app/src/Element/Feed/ImageGridItem.tsx @@ -0,0 +1,41 @@ +import { NostrLink, TaggedNostrEvent } from "@snort/system"; +import { MouseEvent } from "react"; +import useImgProxy from "@/Hooks/useImgProxy"; +import { transformTextCached } from "@/Hooks/useTextTransformCache"; +import { Link } from "react-router-dom"; +import Icon from "@/Icons/Icon"; + +const ImageGridItem = (props: { event: TaggedNostrEvent; onClick: (e: MouseEvent) => void }) => { + const { event, onClick } = props; + const { proxy } = useImgProxy(); + + const parsed = transformTextCached(event.id, event.content, event.tags); + const media = parsed.filter( + a => a.type === "media" && (a.mimeType?.startsWith("image/") || a.mimeType?.startsWith("video/")), + ); + + if (media.length === 0) return null; + + const multiple = media.length > 1; + const isVideo = media[0].mimeType?.startsWith("video/"); + const noteId = NostrLink.fromEvent(event).encode(CONFIG.eventLinkPrefix); + + const myOnClick = (clickEvent: MouseEvent) => { + if (onClick) { + onClick(clickEvent); + clickEvent.preventDefault(); + } + }; + + return ( + + Note Media +
+ {multiple && } + {isVideo && } +
+ + ); +}; + +export default ImageGridItem; diff --git a/packages/app/src/Element/Feed/RootTabs.tsx b/packages/app/src/Element/Feed/RootTabs.tsx index 4a4f7bd3..20e24ec6 100644 --- a/packages/app/src/Element/Feed/RootTabs.tsx +++ b/packages/app/src/Element/Feed/RootTabs.tsx @@ -142,7 +142,7 @@ export function RootTabs({ base = "/" }) { if (currentTab && currentTab !== rootType) { setRootType(currentTab); } - }, [location, menuItems, rootType]); + }, [location.pathname, menuItems, rootType]); function currentMenuItem() { if (location.pathname.startsWith(`${base}/t/`)) { diff --git a/packages/app/src/Element/Feed/TimelineRenderer.tsx b/packages/app/src/Element/Feed/TimelineRenderer.tsx index d6ef7960..a8edfe23 100644 --- a/packages/app/src/Element/Feed/TimelineRenderer.tsx +++ b/packages/app/src/Element/Feed/TimelineRenderer.tsx @@ -5,11 +5,9 @@ import Icon from "@/Icons/Icon"; import { NostrLink, TaggedNostrEvent } from "@snort/system"; import { ReactNode, useState } from "react"; import { TimelineFragment } from "@/Element/Feed/TimelineFragment"; -import { transformTextCached } from "@/Hooks/useTextTransformCache"; -import useImgProxy from "@/Hooks/useImgProxy"; -import { Link } from "react-router-dom"; import { DisplayAs } from "@/Element/Feed/DisplayAsSelector"; import { SpotlightThreadModal } from "@/Element/Spotlight/SpotlightThreadModal"; +import ImageGridItem from "@/Element/Feed/ImageGridItem"; export interface TimelineRendererProps { frags: Array; @@ -27,7 +25,6 @@ export interface TimelineRendererProps { export function TimelineRenderer(props: TimelineRendererProps) { const { ref, inView } = useInView(); - const { proxy } = useImgProxy(); const [modalThread, setModalThread] = useState(undefined); const renderNotes = () => { @@ -44,43 +41,13 @@ export function TimelineRenderer(props: TimelineRendererProps) { const renderGrid = () => { // TODO Hide images from notes with a content warning, unless otherwise configured - const noteImageRenderer = (e: TaggedNostrEvent) => { - const parsed = transformTextCached(e.id, e.content, e.tags); - const media = parsed.filter( - a => a.type === "media" && (a.mimeType?.startsWith("image/") || a.mimeType?.startsWith("video/")), - ); - - if (media.length === 0) return null; - - const multiple = media.length > 1; - const isVideo = media[0].mimeType?.startsWith("video/"); - const noteId = NostrLink.fromEvent(e).encode(CONFIG.eventLinkPrefix); - - const onClick = (clickEvent: React.MouseEvent) => { - if (props.noteOnClick) { - props.noteOnClick(e); - clickEvent.preventDefault(); - } else if (window.innerWidth >= 768) { - setModalThread(NostrLink.fromEvent(e)); - clickEvent.preventDefault(); - } - }; - - return ( - - Note Media -
- {multiple && } - {isVideo && } -
- - ); - }; - - const noteRenderer = props.noteRenderer || noteImageRenderer; return props.frags.map(frag => ( -
{frag.events.map(event => noteRenderer(event))}
+
+ {frag.events.map(event => ( + setModalThread(NostrLink.fromEvent(event))} /> + ))} +
)); }; diff --git a/packages/app/src/Element/Trending/TrendingPosts.tsx b/packages/app/src/Element/Trending/TrendingPosts.tsx index 4213bffa..24468bbf 100644 --- a/packages/app/src/Element/Trending/TrendingPosts.tsx +++ b/packages/app/src/Element/Trending/TrendingPosts.tsx @@ -10,6 +10,8 @@ import { useLocale } from "@/IntlProvider"; import useModeration from "@/Hooks/useModeration"; import ShortNote from "@/Element/Trending/ShortNote"; import classNames from "classnames"; +import { DisplayAs, DisplayAsSelector } from "@/Element/Feed/DisplayAsSelector"; +import ImageGridItem from "@/Element/Feed/ImageGridItem"; export default function TrendingNotes({ count = Infinity, small = false }) { // Added count prop with a default value @@ -17,6 +19,7 @@ export default function TrendingNotes({ count = Infinity, small = false }) { const [error, setError] = useState(); const { lang } = useLocale(); const { isEventMuted } = useModeration(); + const [displayAs, setDisplayAs] = useState("list"); const related = useReactions("trending", posts?.map(a => NostrLink.fromEvent(a)) ?? [], undefined, true); async function loadTrendingNotes() { @@ -46,18 +49,34 @@ export default function TrendingNotes({ count = Infinity, small = false }) { showContextMenu: !small, }; + const filteredAndLimitedPosts = () => { + return posts.filter(a => !isEventMuted(a)).slice(0, count); + }; + + const renderGrid = () => { + return ( +
+ {filteredAndLimitedPosts().map(e => ( + {}} /> + ))} +
+ ); + }; + + const renderList = () => { + return filteredAndLimitedPosts().map(e => + small ? ( + + ) : ( + + ), + ); + }; + return (
- {posts - .filter(a => !isEventMuted(a)) - .slice(0, count) // Limit the number of posts displayed - .map(e => - small ? ( - - ) : ( - - ), - )} + {!small && setDisplayAs(a)} />} + {displayAs === "grid" ? renderGrid() : renderList()}
); }