Skip to content

Order carousel as images appear in items / markdown #2239

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
9 changes: 6 additions & 3 deletions components/carousel.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,16 +108,19 @@ export function CarouselProvider ({ children }) {
const showModal = useShowModal()

const showCarousel = useCallback(({ src }) => {
const sortedMedia = Array.from(media.current.entries())
.sort(([, a], [, b]) => a.imgIndex - b.imgIndex)

showModal((close, setOptions) => {
return <Carousel close={close} mediaArr={Array.from(media.current.entries())} src={src} setOptions={setOptions} />
return <Carousel close={close} mediaArr={sortedMedia} src={src} setOptions={setOptions} />
}, {
fullScreen: true,
overflow: <CarouselOverflow {...media.current.get(src)} />
})
}, [showModal, media.current])

const addMedia = useCallback(({ src, originalSrc, rel }) => {
media.current.set(src, { src, originalSrc, rel })
const addMedia = useCallback(({ src, originalSrc, rel, imgIndex }) => {
media.current.set(src, { src, originalSrc, rel, imgIndex })
}, [media.current])

const removeMedia = useCallback((src) => {
Expand Down
12 changes: 9 additions & 3 deletions components/item-full.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ function BioItem ({ item, handleClick }) {
)
}

function ItemEmbed ({ url, imgproxyUrls }) {
function ItemEmbed ({ url, imgproxyUrls, itemId }) {
const provider = parseEmbedUrl(url)
if (provider) {
return (
Expand All @@ -65,7 +65,13 @@ function ItemEmbed ({ url, imgproxyUrls }) {
const srcSet = imgproxyUrls?.[url]
return (
<div className='mt-3'>
<MediaOrLink src={src} srcSet={srcSet} topLevel linkFallback={false} />
<MediaOrLink
src={src}
srcSet={srcSet}
topLevel
linkFallback={false}
imgIndex={itemId * 100}
/>
</div>
)
}
Expand Down Expand Up @@ -110,7 +116,7 @@ function TopLevelItem ({ item, noReply, ...props }) {
>
<article className={classNames(styles.fullItemContainer, 'topLevel')} ref={textRef}>
{item.text && <ItemText item={item} />}
{item.url && !item.outlawed && <ItemEmbed url={item.url} imgproxyUrls={item.imgproxyUrls} />}
{item.url && !item.outlawed && <ItemEmbed url={item.url} imgproxyUrls={item.imgproxyUrls} itemId={item.id} />}
{item.poll && <Poll item={item} />}
{item.bounty &&
<div className='fw-bold mt-2'>
Expand Down
7 changes: 6 additions & 1 deletion components/media-or-link.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,12 @@ export default function MediaOrLink ({ linkFallback = true, ...props }) {

useEffect(() => {
if (!media.image) return
addMedia({ src: media.bestResSrc, originalSrc: media.originalSrc, rel: props.rel })
addMedia({
src: media.bestResSrc,
originalSrc: media.originalSrc,
rel: props.rel,
imgIndex: props.imgIndex
})
}, [media.image])

const handleClick = useCallback(() => showCarousel({ src: media.bestResSrc }),
Expand Down
22 changes: 17 additions & 5 deletions components/text.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,17 @@ export default memo(function Text ({ rel = UNKNOWN_LINK_REL, imgproxyUrls, child
}, [containerRef.current, setOverflowing])

const TextMediaOrLink = useCallback(props => {
return <MediaLink {...props} outlawed={outlawed} imgproxyUrls={imgproxyUrls} topLevel={topLevel} rel={rel} />
},
[outlawed, imgproxyUrls, topLevel, rel])
return (
<MediaLink
{...props}
outlawed={outlawed}
imgproxyUrls={imgproxyUrls}
topLevel={topLevel}
rel={rel}
itemId={itemId}
/>
)
}, [outlawed, imgproxyUrls, topLevel, rel, itemId])

const components = useMemo(() => ({
h1: ({ node, id, ...props }) => <h1 id={topLevel ? id : undefined} {...props} />,
Expand Down Expand Up @@ -220,8 +228,12 @@ function Footnote ({ children, node, ...props }) {
}

function MediaLink ({
node, src, outlawed, imgproxyUrls, rel = UNKNOWN_LINK_REL, ...props
node, src, outlawed, imgproxyUrls, itemId, imgIndex, rel = UNKNOWN_LINK_REL, ...props
}) {
// assumes less than 100 images in an item
// if more than 100 images, carousel sort order will be unpredictable
const globalImgIndex = itemId * 100 + imgIndex

const url = IMGPROXY_URL_REGEXP.test(src) ? decodeProxyUrl(src) : src
// if outlawed, render the media link as text
if (outlawed) {
Expand All @@ -230,7 +242,7 @@ function MediaLink ({

const srcSet = imgproxyUrls?.[url]

return <MediaOrLink srcSet={srcSet} src={src} rel={rel} {...props} />
return <MediaOrLink srcSet={srcSet} src={src} rel={rel} imgIndex={globalImgIndex} {...props} />
}

function Table ({ node, ...props }) {
Expand Down
16 changes: 15 additions & 1 deletion lib/rehype-sn.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,20 @@ export default function rehypeSN (options = {}) {
const { stylers = [] } = options

return function transformer (tree) {
let imgIndex = 0

try {
visit(tree, (node, index, parent) => {
if (parent?.tagName === 'code') {
// don't process code blocks
return
}

// If node is img, increment and assign imgIndex
if (node.tagName === 'img') {
node.properties.imgIndex ??= ++imgIndex
}

// Handle inline code property
if (node.tagName === 'code') {
node.properties.inline = !(parent && parent.tagName === 'pre')
Expand Down Expand Up @@ -83,6 +90,8 @@ export default function rehypeSN (options = {}) {
node.properties = { ...embed, src: node.properties.href }
} else {
node.tagName = 'autolink'
// since autolinks could be an image, increment imgIndex
node.properties.imgIndex ??= ++imgIndex
}
}

Expand Down Expand Up @@ -209,6 +218,10 @@ export default function rehypeSN (options = {}) {
const allImages = adjacentNodes.flatMap(n =>
n.tagName === 'img' ? [n] : (Array.isArray(n.children) ? n.children.filter(child => child.tagName === 'img') : [])
)
// increment imgIndex for each of the images
allImages.forEach(n => {
n.properties.imgIndex ??= ++imgIndex
})
const collageNode = {
type: 'element',
tagName: 'p',
Expand All @@ -234,7 +247,8 @@ export default function rehypeSN (options = {}) {
node.children.every(child =>
(child.tagName === 'img') ||
(child.type === 'text' && typeof child.value === 'string' && !child.value.trim())
)
) &&
node.children.some(child => child.tagName === 'img')
}

function replaceMention (value, username) {
Expand Down