@@ -28,17 +28,10 @@ import { useNavigate, useParams } from "react-router-dom";
2828import SegmentedControl from "./SegmentedControl" ;
2929import BackToTopButton from "./BackToTopButton" ;
3030import TopPosterStatusBar from "./TopPosterStatusBar" ;
31+ import { useMobileDetection } from "../utils/useMobileDetection" ;
3132
3233const NUM_TOP_POSTERS = 6 ;
3334
34- const columnClasses : { [ key : number ] : string } = {
35- 1 : "lg:columns-1" ,
36- 2 : "lg:columns-2" ,
37- 3 : "lg:columns-3" ,
38- 4 : "lg:columns-4" ,
39- 5 : "lg:columns-5" ,
40- } ;
41-
4235export interface FeedProps {
4336 subreddit : string ;
4437 initialTime : string ;
@@ -65,13 +58,24 @@ const Feed: React.FC<FeedProps> = memo(({ subreddit, initialTime, initialSort })
6558 ) ;
6659 const [ topPosters , setTopPosters ] = useState < [ string , number ] [ ] > ( [ ] ) ;
6760
61+ const isMobile = useMobileDetection ( ) ;
62+
6863 const filteredPosts = useMemo ( ( ) => {
6964 if ( postTypeFilter === "all" ) {
7065 return posts ;
7166 }
7267 return posts . filter ( ( post ) => getPostType ( post ) === postTypeFilter ) ;
7368 } , [ posts , postTypeFilter ] ) ;
7469
70+ const columns = useMemo ( ( ) => {
71+ const columnsToUse = isMobile ? 1 : numColumns ;
72+ const newColumns : Post [ ] [ ] = Array . from ( { length : columnsToUse } , ( ) => [ ] ) ;
73+ filteredPosts . forEach ( ( post , i ) => {
74+ newColumns [ i % columnsToUse ] . push ( post ) ;
75+ } ) ;
76+ return newColumns ;
77+ } , [ filteredPosts , numColumns , isMobile ] ) ;
78+
7579 useEffect ( ( ) => {
7680 const authorCounts : { [ key : string ] : number } = { } ;
7781 filteredPosts . forEach ( ( post ) => {
@@ -241,106 +245,114 @@ const Feed: React.FC<FeedProps> = memo(({ subreddit, initialTime, initialSort })
241245 />
242246 </ div >
243247 </ div >
244- < div className = "hidden md:flex items-center gap-2.5 shrink-0 mb-4 w-full justify-start" >
245- < label htmlFor = "columns" className = "text-[11px] font-medium text-zinc-600 uppercase tracking-wide" >
246- Cols
247- </ label >
248- < input
249- id = "columns"
250- type = "range"
251- min = "1"
252- max = "5"
253- value = { numColumns }
254- onChange = { ( e ) => setNumColumns ( Number ( e . target . value ) ) }
255- className = "w-20 h-0.75 bg-zinc-800 rounded-full appearance-none cursor-pointer [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-3 [&::-webkit-slider-thumb]:h-3 [&::-webkit-slider-thumb]:bg-blue-400 [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:cursor-pointer [&::-webkit-slider-thumb]:shadow-[0_0_6px_rgba(96,165,250,0.5)]"
256- />
257- < span className = "text-xs font-mono text-zinc-400 w-4 text-center tabular-nums" > { numColumns } </ span >
258- </ div >
259- < div className = { `columns-1 ${ columnClasses [ numColumns ] } gap-4` } >
260- { filteredPosts . map ( ( post ) => (
261- < div
262- key = { post . id }
263- className = "bg-slate-200 dark:bg-neutral-800 shadow-md rounded-xl p-2 mb-4 w-full mx-auto prose prose-sm text-gray-700 dark:text-gray-300 prose-headings:font-semibold prose-headings:text-xl break-inside-avoid"
264- >
265- < div >
266- < div className = "flex items-center space-x-2 mb-2" >
267- < img
268- src = {
269- post . sr_detail ?. community_icon ?. length ! > 1
270- ? post . sr_detail ?. community_icon . replace ( / & a m p ; / g, "&" )
271- : post . sr_detail ?. icon_img ?. length ! > 1
272- ? post . sr_detail ?. icon_img . replace ( / & a m p ; / g, "&" )
273- : post . sr_detail ?. header_img ?. length ! > 1
274- ? post . sr_detail ?. header_img . replace ( / & a m p ; / g, "&" )
275- : "/fallback_reddit_icon.png"
276- }
277- alt = { post . author }
278- className = "w-6 h-6 rounded-full bg-gray-300 dark:bg-gray-600"
279- />
280- < a href = { `/user/${ post . author } ` } >
281- < h3 className = "text-blue-500 font-semibold whitespace-nowrap hover:underline" > { post . author } </ h3 >
282- </ a >
283- < AuthorFlairText
284- author_flair_richtext = { post . author_flair_richtext }
285- author_flair_text = { post . author_flair_text }
286- author_flair_background_color = { post . author_flair_background_color }
287- />
288- </ div >
289- < CreatedEditedLabel created = { post . created } edited = { post . edited } />
290-
291- < a href = { parsePermalink ( post . permalink ) } className = "block mt-2 group" >
292- < h2 className = "text-lg font-bold dark:text-white group-hover:underline" > { he . decode ( post . title ) } </ h2 >
293- < LinkFlairText
294- link_flair_richtext = { post . link_flair_richtext }
295- link_flair_text = { post . link_flair_text }
296- link_flair_background_color = { post . link_flair_background_color }
297- />
298- < div
299- className = { `mt-3 ${
300- post . thumbnail === "spoiler" || post . thumbnail === "nsfw" || post . over_18 ? "blur-sm" : ""
301- } `}
302- >
303- { post . secure_media_embed ?. media_domain_url ? (
304- < SecureMediaEmbed
305- url_overridden_by_dest = { post . url_overridden_by_dest }
306- { ...post . secure_media_embed }
248+ { ! isMobile && (
249+ < div className = "flex items-center gap-2.5 shrink-0 mb-4 w-full justify-start" >
250+ < label htmlFor = "columns" className = "text-[11px] font-medium text-zinc-600 uppercase tracking-wide" >
251+ Cols
252+ </ label >
253+ < input
254+ id = "columns"
255+ type = "range"
256+ min = "1"
257+ max = "5"
258+ value = { numColumns }
259+ onChange = { ( e ) => setNumColumns ( Number ( e . target . value ) ) }
260+ className = "w-20 h-0.75 bg-zinc-800 rounded-full appearance-none cursor-pointer [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-3 [&::-webkit-slider-thumb]:h-3 [&::-webkit-slider-thumb]:bg-blue-400 [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:cursor-pointer [&::-webkit-slider-thumb]:shadow-[0_0_6px_rgba(96,165,250,0.5)]"
261+ />
262+ < span className = "text-xs font-mono text-zinc-400 w-4 text-center tabular-nums" > { numColumns } </ span >
263+ </ div >
264+ ) }
265+ < div className = "flex justify-center gap-4" >
266+ { columns . map ( ( columnPosts : Post [ ] , columnIndex ) => (
267+ < div key = { columnIndex } className = "flex flex-1 flex-col gap-4 min-w-0" >
268+ { columnPosts . map ( ( post ) => (
269+ < div
270+ key = { post . id }
271+ className = "bg-slate-200 dark:bg-neutral-800 shadow-md rounded-xl p-2 w-full mx-auto prose prose-sm text-gray-700 dark:text-gray-300 prose-headings:font-semibold prose-headings:text-xl"
272+ >
273+ < div >
274+ < div className = "flex items-center space-x-2 mb-2" >
275+ < img
276+ src = {
277+ post . sr_detail ?. community_icon ?. length ! > 1
278+ ? post . sr_detail ?. community_icon . replace ( / & a m p ; / g, "&" )
279+ : post . sr_detail ?. icon_img ?. length ! > 1
280+ ? post . sr_detail ?. icon_img . replace ( / & a m p ; / g, "&" )
281+ : post . sr_detail ?. header_img ?. length ! > 1
282+ ? post . sr_detail ?. header_img . replace ( / & a m p ; / g, "&" )
283+ : "/fallback_reddit_icon.png"
284+ }
285+ alt = { post . author }
286+ className = "w-6 h-6 rounded-full bg-gray-300 dark:bg-gray-600"
287+ />
288+ < a href = { `/user/${ post . author } ` } >
289+ < h3 className = "text-blue-500 font-semibold whitespace-nowrap hover:underline" > { post . author } </ h3 >
290+ </ a >
291+ < AuthorFlairText
292+ author_flair_richtext = { post . author_flair_richtext }
293+ author_flair_text = { post . author_flair_text }
294+ author_flair_background_color = { post . author_flair_background_color }
307295 />
308- ) : post . secure_media ? (
309- < SecureMedia { ...post . secure_media } />
310- ) : post . media_metadata ? (
311- < div className = "relative mt-2" >
312- { post . gallery_data ? (
313- < PostGallery galleryData = { post . gallery_data } mediaMetadata = { post . media_metadata } />
314- ) : null }
296+ </ div >
297+ < CreatedEditedLabel created = { post . created } edited = { post . edited } />
298+
299+ < a href = { parsePermalink ( post . permalink ) } className = "block mt-2 group" >
300+ < h2 className = "text-lg font-bold dark:text-white group-hover:underline" >
301+ { he . decode ( post . title ) }
302+ </ h2 >
303+ < LinkFlairText
304+ link_flair_richtext = { post . link_flair_richtext }
305+ link_flair_text = { post . link_flair_text }
306+ link_flair_background_color = { post . link_flair_background_color }
307+ />
308+ < div
309+ className = { `mt-3 ${
310+ post . thumbnail === "spoiler" || post . thumbnail === "nsfw" || post . over_18 ? "blur-sm" : ""
311+ } `}
312+ >
313+ { post . secure_media_embed ?. media_domain_url ? (
314+ < SecureMediaEmbed
315+ url_overridden_by_dest = { post . url_overridden_by_dest }
316+ { ...post . secure_media_embed }
317+ />
318+ ) : post . secure_media ? (
319+ < SecureMedia { ...post . secure_media } />
320+ ) : post . media_metadata ? (
321+ < div className = "relative mt-2" >
322+ { post . gallery_data ? (
323+ < PostGallery galleryData = { post . gallery_data } mediaMetadata = { post . media_metadata } />
324+ ) : null }
325+ </ div >
326+ ) : post . preview ? (
327+ < PostPreview preview = { post . preview } />
328+ ) : post . url_overridden_by_dest ? (
329+ isImage ( post . url_overridden_by_dest ) ? (
330+ < img
331+ src = { post . url_overridden_by_dest }
332+ alt = "url_overridden_by_dest"
333+ className = "mt-4 max-w-full max-h-125 mx-auto border rounded-md p-2 object-contain"
334+ />
335+ ) : (
336+ < FetchImage url = { post . url_overridden_by_dest } />
337+ )
338+ ) : (
339+ < Thumbnail thumbnail = { post . thumbnail || "" } />
340+ ) }
341+
342+ { post . poll_data && < PollData poll_data = { post . poll_data } /> }
343+
344+ { post . selftext_html && < SelfTextHtml selftext_html = { post . selftext_html } truncateLines = { 10 } /> }
345+
346+ { post . url_overridden_by_dest && post . post_hint === "link" && (
347+ < ExternalLink url_overridden_by_dest = { post . url_overridden_by_dest } />
348+ ) }
349+
350+ < PostStats score = { post . score } num_comments = { post . num_comments } />
315351 </ div >
316- ) : post . preview ? (
317- < PostPreview preview = { post . preview } />
318- ) : post . url_overridden_by_dest ? (
319- isImage ( post . url_overridden_by_dest ) ? (
320- < img
321- src = { post . url_overridden_by_dest }
322- alt = "url_overridden_by_dest"
323- className = "mt-4 max-w-full max-h-125 mx-auto border rounded-md p-2 object-contain"
324- />
325- ) : (
326- < FetchImage url = { post . url_overridden_by_dest } />
327- )
328- ) : (
329- < Thumbnail thumbnail = { post . thumbnail || "" } />
330- ) }
331-
332- { post . poll_data && < PollData poll_data = { post . poll_data } /> }
333-
334- { post . selftext_html && < SelfTextHtml selftext_html = { post . selftext_html } truncateLines = { 10 } /> }
335-
336- { post . url_overridden_by_dest && post . post_hint === "link" && (
337- < ExternalLink url_overridden_by_dest = { post . url_overridden_by_dest } />
338- ) }
339-
340- < PostStats score = { post . score } num_comments = { post . num_comments } />
352+ </ a >
341353 </ div >
342- </ a >
343- </ div >
354+ </ div >
355+ ) ) }
344356 </ div >
345357 ) ) }
346358 </ div >
0 commit comments