import { useEffect, useRef, useState } from 'preact/hooks'; import { useDebouncedCallback } from 'use-debounce'; import useScroll from '../utils/useScroll'; import Icon from './icon'; import Link from './link'; import Loader from './loader'; import Status from './status'; function Timeline({ title, titleComponent, id, emptyText, errorText, boostsCarousel, fetchItems = () => {}, }) { const [items, setItems] = useState([]); const [uiState, setUIState] = useState('default'); const [showMore, setShowMore] = useState(false); const scrollableRef = useRef(null); const { nearReachEnd, reachStart, reachEnd } = useScroll({ scrollableElement: scrollableRef.current, distanceFromEnd: 1, }); const loadItems = useDebouncedCallback( (firstLoad) => { if (uiState === 'loading') return; setUIState('loading'); (async () => { try { let { done, value } = await fetchItems(firstLoad); if (value?.length) { if (boostsCarousel) { value = groupBoosts(value); } console.log(value); if (firstLoad) { setItems(value); } else { setItems([...items, ...value]); } setShowMore(!done); } else { setShowMore(false); } setUIState('default'); } catch (e) { console.error(e); setUIState('error'); } })(); }, 1500, { leading: true, trailing: false, }, ); useEffect(() => { scrollableRef.current?.scrollTo({ top: 0 }); loadItems(true); }, []); useEffect(() => { if (reachStart) { loadItems(true); } }, [reachStart]); useEffect(() => { if (nearReachEnd || (reachEnd && showMore)) { loadItems(); } }, [nearReachEnd, showMore]); return (
{ if (e.target === e.currentTarget) { scrollableRef.current?.scrollTo({ top: 0, behavior: 'smooth', }); } }} >
{title && (titleComponent ? titleComponent :

{title}

)}
{!!items.length ? ( <> {uiState === 'default' && (showMore ? ( ) : (

The end.

))} ) : uiState === 'loading' ? ( ) : ( uiState !== 'error' &&

{emptyText}

)} {uiState === 'error' && (

{errorText}

)}
); } function groupBoosts(values) { let newValues = []; let boostStash = []; let serialBoosts = 0; for (let i = 0; i < values.length; i++) { const item = values[i]; if (item.reblog) { boostStash.push(item); serialBoosts++; } else { newValues.push(item); if (serialBoosts < 3) { serialBoosts = 0; } } } // if boostStash is more than quarter of values // or if there are 3 or more boosts in a row if (boostStash.length > values.length / 4 || serialBoosts >= 3) { // if boostStash is more than 3 quarter of values const boostStashID = boostStash.map((status) => status.id); if (boostStash.length > (values.length * 3) / 4) { // insert boost array at the end of specialHome list newValues = [...newValues, { id: boostStashID, boosts: boostStash }]; } else { // insert boosts array in the middle of specialHome list const half = Math.floor(newValues.length / 2); newValues = [ ...newValues.slice(0, half), { id: boostStashID, boosts: boostStash, }, ...newValues.slice(half), ]; } return newValues; } else { return values; } } function BoostsCarousel({ boosts }) { const carouselRef = useRef(); const { reachStart, reachEnd, init } = useScroll({ scrollableElement: carouselRef.current, direction: 'horizontal', }); useEffect(() => { init?.(); }, []); return ( ); } export default Timeline;