import { useEffect, useState, useCallback } from 'react'; import { useIntl, defineMessages } from 'react-intl'; import { fetchContext, completeContextRefresh, } from 'flavours/glitch/actions/statuses'; import type { AsyncRefreshHeader } from 'flavours/glitch/api'; import { apiGetAsyncRefresh } from 'flavours/glitch/api/async_refreshes'; import { Alert } from 'flavours/glitch/components/alert'; import { ExitAnimationWrapper } from 'flavours/glitch/components/exit_animation_wrapper'; import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator'; import { useAppSelector, useAppDispatch } from 'flavours/glitch/store'; const AnimatedAlert: React.FC< React.ComponentPropsWithoutRef & { withEntryDelay?: boolean } > = ({ isActive = false, withEntryDelay, ...props }) => ( {(delayedIsActive) => } ); const messages = defineMessages({ moreFound: { id: 'status.context.more_replies_found', defaultMessage: 'More replies found', }, show: { id: 'status.context.show', defaultMessage: 'Show', }, loadingInitial: { id: 'status.context.loading', defaultMessage: 'Loading', }, loadingMore: { id: 'status.context.loading_more', defaultMessage: 'Loading more replies', }, success: { id: 'status.context.loading_success', defaultMessage: 'All replies loaded', }, error: { id: 'status.context.loading_error', defaultMessage: "Couldn't load new replies", }, retry: { id: 'status.context.retry', defaultMessage: 'Retry', }, }); type LoadingState = | 'idle' | 'more-available' | 'loading-initial' | 'loading-more' | 'success' | 'error'; export const RefreshController: React.FC<{ statusId: string; }> = ({ statusId }) => { const refresh = useAppSelector( (state) => state.contexts.refreshing[statusId], ); const currentReplyCount = useAppSelector( (state) => state.contexts.replies[statusId]?.length ?? 0, ); const autoRefresh = !currentReplyCount; const dispatch = useAppDispatch(); const intl = useIntl(); const [loadingState, setLoadingState] = useState( refresh && autoRefresh ? 'loading-initial' : 'idle', ); const [wasDismissed, setWasDismissed] = useState(false); const dismissPrompt = useCallback(() => { setWasDismissed(true); setLoadingState('idle'); }, []); useEffect(() => { let timeoutId: ReturnType; const scheduleRefresh = (refresh: AsyncRefreshHeader) => { timeoutId = setTimeout(() => { void apiGetAsyncRefresh(refresh.id).then((result) => { if (result.async_refresh.status === 'finished') { dispatch(completeContextRefresh({ statusId })); if (result.async_refresh.result_count > 0) { if (autoRefresh) { void dispatch(fetchContext({ statusId })).then(() => { setLoadingState('idle'); }); } else { setLoadingState('more-available'); } } else { setLoadingState('idle'); } } else { scheduleRefresh(refresh); } }); }, refresh.retry * 1000); }; if (refresh && !wasDismissed) { scheduleRefresh(refresh); setLoadingState('loading-initial'); } return () => { clearTimeout(timeoutId); }; }, [dispatch, statusId, refresh, autoRefresh, wasDismissed]); useEffect(() => { // Hide success message after a short delay if (loadingState === 'success') { const timeoutId = setTimeout(() => { setLoadingState('idle'); }, 3000); return () => { clearTimeout(timeoutId); }; } return () => ''; }, [loadingState]); const handleClick = useCallback(() => { setLoadingState('loading-more'); dispatch(fetchContext({ statusId })) .then(() => { setLoadingState('success'); return ''; }) .catch(() => { setLoadingState('error'); }); }, [dispatch, statusId]); if (loadingState === 'loading-initial') { return (
); } return (
); };