diff --git a/src/components/Notes/NoteRender.tsx b/src/components/Notes/NoteRender.tsx index 94833f2..4d97a0f 100644 --- a/src/components/Notes/NoteRender.tsx +++ b/src/components/Notes/NoteRender.tsx @@ -1,7 +1,7 @@ import { NDKKind } from '@nostr-dev-kit/ndk' import { ProfileLink } from 'components/ProfileSection' import { nip19 } from 'nostr-tools' -import { useMemo } from 'react' +import { useCallback, useMemo, useState } from 'react' import { Fragment } from 'react/jsx-runtime' import { BlogPreview } from './internal/BlogPreview' import { ModPreview } from './internal/ModPreview' @@ -14,6 +14,8 @@ import { isValidVideoUrl, isYoutubeLink } from 'utils' +import FsLightbox from 'fslightbox-react' +import { createPortal } from 'react-dom' interface NoteRenderProps { content: string @@ -28,6 +30,44 @@ const nostrNip05Mention = /(?:nostr:|@)[^\s]{1,64}@[^\s]+\.[^\s]{2,}/i const nip05Entity = /(?:nostr:|@)([^\s]{1,64}@[^\s]+\.[^\s]{2,})/i export const NoteRender = ({ content }: NoteRenderProps) => { + const [lightBoxController, setLightBoxController] = useState({ + toggler: false, + slide: 1 + }) + const slides = useMemo(() => { + const urls: string[] = [] + const parts = content.matchAll(new RegExp(`${link.source}`, 'gui')) + ;[...parts] + .filter((p) => typeof p !== 'undefined') + .forEach((part) => { + const [href] = part + + if (href && isValidUrl(href)) { + if (isValidImageUrl(href) || isValidVideoUrl(href)) { + urls.push(href) + } else if (isYoutubeLink(href)) { + const id = getIdFromYoutubeLink(href) + if (id) { + urls.push(`https://www.youtube.com/embed/${id}`) + } + } + } + }) + + return urls + }, [content]) + + const openLightBoxOnSlide = useCallback( + (url: string) => { + slides.length && + setLightBoxController((prev) => ({ + toggler: !prev.toggler, + slide: slides.findIndex((s) => s === url) + 1 + })) + }, + [slides] + ) + const _content = useMemo(() => { if (!content) return @@ -49,7 +89,13 @@ export const NoteRender = ({ content }: NoteRenderProps) => { if (isValidImageUrl(href)) { // Image return ( - + openLightBoxOnSlide(href)} + /> ) } else if (isValidVideoUrl(href)) { // Video @@ -129,9 +175,22 @@ export const NoteRender = ({ content }: NoteRenderProps) => { } }) return _parts - }, [content]) + }, [content, openLightBoxOnSlide]) - return _content + return ( + <> + {_content} + {slides.length > 0 && + createPortal( + , + document.body + )} + + ) } function handleNaddr(data: nip19.AddressPointer, original: string) { diff --git a/src/styles/comments.css b/src/styles/comments.css index c0ab065..abb964b 100644 --- a/src/styles/comments.css +++ b/src/styles/comments.css @@ -639,3 +639,8 @@ hover { .IBMSMSMBSSCL_CBText > *:last-child { margin-bottom: 0; } + +/* Lightbox exception */ +.IBMSMSMBSSCL_CBText > .fslightbox-container { + margin: 0 +} diff --git a/src/styles/feed.css b/src/styles/feed.css index bac72cd..287af3e 100644 --- a/src/styles/feed.css +++ b/src/styles/feed.css @@ -170,6 +170,7 @@ width: 100%; border-radius: 10px; box-shadow: 0 0 8px 0 rgb(0,0,0,0.25); + cursor: pointer; } .videoFeedRender {