import { useState, useEffect } from 'react' import { Link, useLocation, useParams, Navigate, useSearchParams } from 'react-router-dom' import dayjs from 'dayjs' import utc from 'dayjs/plugin/utc' dayjs.extend(utc) import Message from './Message' import Spinner from './Spinner' import UserInfo from './UserInfo' import { getMessages } from '../api' import { useVisitor } from './VisitorContext' import { Helmet } from 'react-helmet-async' /** * @typedef {object} Query * @property {string} baseUrl * @property {object=} search * @property {string} pageParam */ /** * @typedef {object} PageProps * @property {string=} search * @property {import('../api').Message[]=} msgs */ function RequireAuth({ children }) { let location = useLocation() let [visitor] = useVisitor() if (!visitor.hash) { // Redirect them to the /login page, but save the current location they were // trying to go to when they were redirected. This allows us to send them // along to that page after they login, which is a nicer user experience // than dropping them off on the home page. return } return children } /** * */ export function Discover() { const [search] = useSearchParams() const query = { baseUrl: '/api/messages', search: search, pageParam: search.search ? 'page' : 'before_mid' } return ( <> Discover ) } /** * */ export function Discussions() { const query = { baseUrl: '/api/messages/discussions', pageParam: 'to' } return ( <> Discussions ) } /** * */ export function Blog() { const { user } = useParams() const [params] = useSearchParams() const search = { ...params, uname: user } const query = { baseUrl: '/api/messages', search: search, pageParam: search.search ? 'page' : 'before_mid' } const blogTitle = `${user} blog` const pageTitle = search.tag ? `${blogTitle}: #${search.tag}` : blogTitle return ( <> {pageTitle}
) } /** * */ export function Tag() { const params = useParams() const { tag } = params const query = { baseUrl: '/api/messages', search: { tag: tag }, pageParam: 'before_mid' } return ( <> #{tag} ) } /** * */ export function Home() { const query = { baseUrl: '/api/home', pageParam: 'before_mid' } return ( ) } /** * @typedef {object} FeedState * @property { import('../api').Message[]= } msgs * @property { Query} query */ /** * @param {FeedState} props */ function Feed({ query }) { const location = useLocation() const [visitor] = useVisitor() const [state, setState] = useState({ msgs: [], nextpage: null, error: false, tag: '' }) const [loading, setLoading] = useState(true) const [filter] = useSearchParams() useEffect(() => { setLoading(true) let getPageParam = (pageParam, lastMessage, /** @type { URLSearchParams } */ filterParams) => { const pageValue = pageParam === 'before_mid' ? lastMessage.mid : pageParam === 'page' ? (Number(filterParams.page) || 0) + 1 : dayjs.utc(lastMessage.updated).valueOf() filterParams.append(pageParam, pageValue) return `?${filterParams.toString()}` } let params = { ...Object.fromEntries(filter), ...query.search } let url = query.baseUrl getMessages(url, params) .then(response => { const { data } = response const { pageParam } = query const lastMessage = data.slice(-1)[0] || {} const nextpage = getPageParam(pageParam, lastMessage, new URLSearchParams(params)) document.body.scrollTop = 0 document.documentElement.scrollTop = 0 setState((prevState) => { return { ...prevState, msgs: data, nextpage: nextpage, tag: filter['tag'] || '' } }) setLoading(false) }).catch(() => { setState((prevState) => { return { ...prevState, error: true } }) }) }, [query, filter]) return (state.msgs.length > 0 ? (
{ state.tag && (

← All posts with tag {state.tag}

) } { state.msgs.map(msg => ) } { state.msgs.length >= 20 && (

Next →

) }
) : state.error ?
error
: loading ?
:
No more messages
) }