refactor: migrate list-replacing screens to the new list state components
3 unmodified lines4567889101112131415161718192018192021222324252621222324252627323334282930313637323940413334353643444546474849505152535437383940414243446263456566676869464748495051525354555657585960613 unmodified linesimport { useLikedByQuery } from '#/state/queries/post-liked-by';import { useResolveUriQuery } from '#/state/queries/resolve-uri';
import { List } from '#/components/List/List';import { ListFooter, ListMaybePlaceholder } from '#/components/Lists';import { ListEmpty } from '#/components/List/ListEmpty';import { ListError } from '#/components/List/ListError';import * as ListTail from '#/components/List/ListTail';import * as ProfileCard from '#/components/web/ProfileCard';
import HeartIcon from '#/icons/central/Heart_round_outlined_radius1_stroke1.svg';import { m } from '#/paraglide/messages';
const PROFILE_ITEM_HEIGHT_ESTIMATE = 130;
export function LikedByList({ uri, initialCount }: { uri: string; initialCount?: number }) { const moderationOpts = useModerationOpts();
const { data: resolvedUri, error: resolveError, isLoading: isLoadingUri } = useResolveUriQuery(uri); const { data, isLoading: isLoadingLikes, isFetchingNextPage, hasNextPage, fetchNextPage, error, } = useLikedByQuery(resolvedUri?.uri); const { data: resolvedUri, error: resolveError } = useResolveUriQuery(uri); const { data, error, fetchNextPage, isFetchingNextPage, isPending } = useLikedByQuery(resolvedUri?.uri);
const isError = !!(resolveError || error);
const likes = data?.pages ? data.pages.flatMap((page) => page.likes) : [];
const onEndReached = () => { if (isFetchingNextPage || !hasNextPage || isError) { return; if (likes.length < 1 || !moderationOpts) { if (isError) { return <ListError message={cleanError(resolveError || error)} />; } void fetchNextPage(); };
if (!moderationOpts || ((isLoadingUri || isLoadingLikes) && likes.length < 1 && !isError)) { return <ProfileCard.LoadingPlaceholder count={initialCount} />; } if (isPending || !moderationOpts) { return <ProfileCard.LoadingPlaceholder count={initialCount} />; }
if (likes.length < 1) { return ( <ListMaybePlaceholder isLoading={false} isError={isError} emptyType="results" emptyTitle={m['common.like.empty']()} emptyMessage={m['common.like.emptyPrompt']()} errorMessage={cleanError(resolveError || error)} topBorder={false} /> ); return <ListEmpty icon={HeartIcon} message={m['common.like.emptyPrompt']()} />; }
return ( <List data={likes} estimateHeight={PROFILE_ITEM_HEIGHT_ESTIMATE} keyExtractor={(item) => item.actor.did} onEndReached={onEndReached} onEndReachedThreshold={2} ListFooterComponent={ <ListFooter isFetchingNextPage={isFetchingNextPage} error={cleanError(error)} onRetry={fetchNextPage} /> <ListTail.Frame> {isFetchingNextPage ? ( <ListTail.Pending /> ) : isError ? ( <ListTail.Error message={cleanError(error)} onRetry={() => void fetchNextPage()} /> ) : null} </ListTail.Frame> } renderItem={({ index, item }) => ( <ProfileCard.Default moderationOpts={moderationOpts} profile={item.actor} topBorder={index !== 0} /> )} onEndReached={() => void fetchNextPage()} onEndReachedThreshold={2} /> );}10 unmodified lines11121314151516171819202122231 unmodified line25262728293031323382 unmodified lines11611711811911611711811912012112212312412512612012112212312412513013113213313413513612612712812916 unmodified lines14614714814915015115215315415515615715815916016116216216316416516616716816917017117217317417517617717817918018118218318418518618718816316416516616716816917017117217317417517617717817918018110 unmodified linesimport { Trans } from '#/locale/Trans';
import { signinDialogHandle } from '#/components/dialogs/handles';import { List } from '#/components/List/List';import { ListFooter, ListMaybePlaceholder } from '#/components/Lists';import { ListEmpty } from '#/components/List/ListEmpty';import { ListError } from '#/components/List/ListError';import * as ListTail from '#/components/List/ListTail';import { Post } from '#/components/Post/Post';import { PostFeedLoadingPlaceholder } from '#/components/PostFeed/PostFeedLoadingPlaceholder';import { SearchError } from '#/components/SearchError';import { shareUrl } from '#/components/sharing';import { type Section, Tabs } from '#/components/Tabs';import { Text } from '#/components/Text';1 unmodified lineimport * as Layout from '#/components/web/Layout';import { InlineButton } from '#/components/web/Link';
import Share from '#/icons/central/ArrowOutOfBox_round_outlined_radius1_stroke2.svg';import MagnifyingGlassIcon from '#/icons/central/MagnifyingGlass_round_outlined_radius1_stroke2.svg';import { m } from '#/paraglide/messages';import { useParams } from '#/router';
export default function HashtagScreen() {82 unmodified lines
// Cashtags need # prefix for search: "#$BTC" const queryParam = isCashtag ? `#${fullTag}` : fullTag;
const { data, isFetched, isFetchingNextPage, isLoading, isError, error, refetch, fetchNextPage, hasNextPage, } = useSearchPostsQuery({ author, query: queryParam, sort }); const { data, isFetchingNextPage, isPending, isError, error, refetch, fetchNextPage } = useSearchPostsQuery( { author, query: queryParam, sort }, );
const posts = data?.pages.flatMap((page) => page.posts) || [];
const onEndReached = () => { if (isFetchingNextPage || !hasNextPage || error) { return; } void fetchNextPage(); };
if (!hasSession) { return ( <SearchError title={m['common.search.loggedOutError']()}> <Text align="center" leading="snug" size="md">16 unmodified lines </SearchError> ); }
if (posts.length < 1) { if (isError) { return <ListError message={cleanError(error)} onRetry={() => void refetch()} />; }
if (isPending) { return <PostFeedLoadingPlaceholder />; }
return <ListEmpty icon={MagnifyingGlassIcon} message={m['screens.hashtag.empty']()} />; }
return ( <> {posts.length < 1 ? ( <ListMaybePlaceholder isLoading={isLoading || !isFetched} isError={isError} onRetry={refetch} emptyType="results" emptyMessage={m['screens.hashtag.empty']()} /> ) : ( <List data={posts} estimateHeight={POST_ITEM_HEIGHT_ESTIMATE} keyExtractor={(item, index) => `${item.uri}-${index}`} renderItem={({ index, item }) => <Post hideTopBorder={index === 0} post={item} />} onEndReached={onEndReached} onEndReachedThreshold={4} ListFooterComponent={ <ListFooter isFetchingNextPage={isFetchingNextPage} error={cleanError(error)} onRetry={fetchNextPage} /> } /> )} </> <List data={posts} estimateHeight={POST_ITEM_HEIGHT_ESTIMATE} keyExtractor={(item) => `${item.uri}|${item.cid}`} renderItem={({ index, item }) => <Post hideTopBorder={index === 0} post={item} />} ListFooterComponent={ <ListTail.Frame> {isFetchingNextPage ? ( <ListTail.Pending /> ) : isError ? ( <ListTail.Error message={cleanError(error)} onRetry={() => void fetchNextPage()} /> ) : null} </ListTail.Frame> } onEndReached={() => void fetchNextPage()} onEndReachedThreshold={2} /> );}4 unmodified lines56789910111213141516171819202122232451 unmodified lines767778797677787980818283848586878889808182838485868788939495899091929798939495969798991001011011021031041051061071081091101111121131141151161171181191201211221231241251261271021031041051061071081091101111121131141151161171181191204 unmodified linesimport { useSearchPostsQuery } from '#/state/queries/search-posts';import { useTitle } from '#/state/use-title';
import { List } from '#/components/List/List';import { ListFooter, ListMaybePlaceholder } from '#/components/Lists';import { ListEmpty } from '#/components/List/ListEmpty';import { ListError } from '#/components/List/ListError';import * as ListTail from '#/components/List/ListTail';import { Post } from '#/components/Post/Post';import { PostFeedLoadingPlaceholder } from '#/components/PostFeed/PostFeedLoadingPlaceholder';import { shareUrl } from '#/components/sharing';import { type Section, Tabs } from '#/components/Tabs';import { Button, ButtonIcon } from '#/components/web/Button';import * as Layout from '#/components/web/Layout';
import Share from '#/icons/central/ArrowOutOfBox_round_outlined_radius1_stroke2.svg';import MagnifyingGlassIcon from '#/icons/central/MagnifyingGlass_round_outlined_radius1_stroke2.svg';import { m } from '#/paraglide/messages';import { useParams } from '#/router';
export default function TopicScreen() {51 unmodified lines
const POST_ITEM_HEIGHT_ESTIMATE = 300;
function TopicScreenTab({ topic, sort }: { topic: string; sort: 'top' | 'latest' }) { const { data, isFetched, isFetchingNextPage, isLoading, isError, error, refetch, fetchNextPage, hasNextPage, } = useSearchPostsQuery({ query: topic, sort, }); const { data, isFetchingNextPage, isPending, isError, error, refetch, fetchNextPage } = useSearchPostsQuery( { query: topic, sort, }, );
const posts = data?.pages.flatMap((page) => page.posts) || [];
const onEndReached = () => { if (isFetchingNextPage || !hasNextPage || error) { return; if (posts.length < 1) { if (isError) { return <ListError message={cleanError(error)} onRetry={() => void refetch()} />; } void fetchNextPage(); };
if (isPending) { return <PostFeedLoadingPlaceholder />; }
return <ListEmpty icon={MagnifyingGlassIcon} message={m['screens.topic.empty']()} />; }
return ( <> {posts.length < 1 ? ( <ListMaybePlaceholder isLoading={isLoading || !isFetched} isError={isError} onRetry={refetch} emptyType="results" emptyMessage={m['screens.topic.empty']()} /> ) : ( <List data={posts} estimateHeight={POST_ITEM_HEIGHT_ESTIMATE} keyExtractor={(item, index) => `${item.uri}-${index}`} renderItem={({ index, item }) => <Post hideTopBorder={index === 0} post={item} />} onEndReached={onEndReached} onEndReachedThreshold={4} ListFooterComponent={ <ListFooter isFetchingNextPage={isFetchingNextPage} error={cleanError(error)} onRetry={fetchNextPage} /> } /> )} </> <List data={posts} estimateHeight={POST_ITEM_HEIGHT_ESTIMATE} keyExtractor={(item) => `${item.uri}|${item.cid}`} renderItem={({ index, item }) => <Post hideTopBorder={index === 0} post={item} />} ListFooterComponent={ <ListTail.Frame> {isFetchingNextPage ? ( <ListTail.Pending /> ) : isError ? ( <ListTail.Error message={cleanError(error)} onRetry={() => void fetchNextPage()} /> ) : null} </ListTail.Frame> } onEndReached={() => void fetchNextPage()} onEndReachedThreshold={2} /> );}1234567891011import { style } from '@vanilla-extract/css';
export const container = style({ boxSizing: 'border-box', display: 'flex', flexDirection: 'column', alignItems: 'center', paddingTop: 175, paddingBottom: 110, height: '100vh',});1234567891011121314import { Spinner } from '#/components/Spinner';
import { m } from '#/paraglide/messages';
import * as css from './ListLoading.css';
/** @returns a fallback loading state for lists without row skeletons */export function ListLoading() { return ( <div className={css.container}> <Spinner color="default" label={m['common.status.loading']()} size="_2xl" /> </div> );}61 unmodified lines62636465666768697071727374757677787961 unmodified lines </div> </div> );}
/** * renders an end of list message. * * @param children end-of-list message * @returns the end marker */export function End({ children }: { children: ReactNode }) { return ( <Text color="textContrastLow" size="sm"> {children} </Text> );}10 unmodified lines111213141515161718192021222324252640 unmodified lines6768697067686970717273747571727374757677787 unmodified lines868788899192939091929395969498959697989910010110210310410510610710010110210311111210410510610710810911912011012212312412512612712811111211311411511611711811912012112212312412510 unmodified linesimport { useResolveUriQuery } from '#/state/queries/resolve-uri';import { useTitle } from '#/state/use-title';
import { List } from '#/components/List/List';import { ListFooter, ListMaybePlaceholder } from '#/components/Lists';import { ListEmpty } from '#/components/List/ListEmpty';import { ListError } from '#/components/List/ListError';import * as ListTail from '#/components/List/ListTail';import { Post } from '#/components/Post/Post';import { PostFeedLoadingPlaceholder } from '#/components/PostFeed/PostFeedLoadingPlaceholder';import * as Layout from '#/components/web/Layout';
import CloseQuoteIcon from '#/icons/central/CloseQuote2_round_outlined_radius1_stroke2.svg';import { m } from '#/paraglide/messages';import { useParams } from '#/router';
export const PostQuotesScreen = () => {40 unmodified lines return item.post.uri;}
function PostQuotes({ uri }: { uri: string }) { const { data: resolvedUri, error: resolveError, isLoading: isLoadingUri } = useResolveUriQuery(uri); const { data, isLoading: isLoadingQuotes, isFetchingNextPage, hasNextPage, fetchNextPage, error, } = usePostQuotesQuery(resolvedUri?.uri); const { data: resolvedUri, error: resolveError } = useResolveUriQuery(uri); const { data, isPending, isFetchingNextPage, hasNextPage, fetchNextPage, error } = usePostQuotesQuery( resolvedUri?.uri, );
const moderationOpts = useModerationOpts();
const isError = !!(resolveError || error);7 unmodified lines })), ) ?? []) : [];
const onEndReached = () => { if (isFetchingNextPage || !hasNextPage || isError) { return; if (quotes.length < 1) { if (isError) { return <ListError message={cleanError(resolveError || error)} />; } void fetchNextPage(); };
if (quotes.length < 1) { if (isPending || !moderationOpts) { return <PostFeedLoadingPlaceholder />; }
return ( <ListMaybePlaceholder isLoading={isLoadingUri || isLoadingQuotes} isError={isError} emptyType="results" emptyTitle={m['screens.postThread.engagement.quote.empty']()} emptyMessage={m['screens.postThread.engagement.quote.emptyPrompt']()} errorMessage={cleanError(resolveError || error)} /> <ListEmpty icon={CloseQuoteIcon} message={m['screens.postThread.engagement.quote.emptyPrompt']()} /> ); }
// loaded // = return ( <List data={quotes} estimateHeight={POST_ITEM_HEIGHT_ESTIMATE} renderItem={renderItem} keyExtractor={keyExtractor} onEndReached={onEndReached} onEndReachedThreshold={4} ListFooterComponent={ <ListFooter isFetchingNextPage={isFetchingNextPage} error={cleanError(error)} onRetry={fetchNextPage} showEndMessage endMessageText={m['screens.postThread.engagement.quote.endOfFeed']()} /> <ListTail.Frame> {isFetchingNextPage ? ( <ListTail.Pending /> ) : isError ? ( <ListTail.Error message={cleanError(error)} onRetry={() => void fetchNextPage()} /> ) : !hasNextPage ? ( <ListTail.End>{m['screens.postThread.engagement.quote.endOfFeed']()}</ListTail.End> ) : null} </ListTail.Frame> } onEndReached={() => void fetchNextPage()} onEndReachedThreshold={2} /> );}8 unmodified lines910111213131415161718192021222335 unmodified lines5960616260616263646566676863646566676869707174757672737475787976778182837879808185868788899091929394958283848586878889103104901061071081091109192939495969798991001011021031041051068 unmodified linesimport { useResolveUriQuery } from '#/state/queries/resolve-uri';import { useTitle } from '#/state/use-title';
import { List } from '#/components/List/List';import { ListFooter, ListMaybePlaceholder } from '#/components/Lists';import { ListEmpty } from '#/components/List/ListEmpty';import { ListError } from '#/components/List/ListError';import * as ListTail from '#/components/List/ListTail';import * as Layout from '#/components/web/Layout';import * as ProfileCard from '#/components/web/ProfileCard';
import RepostIcon from '#/icons/central/ArrowsRepeatRightLeft_round_outlined_radius1_stroke2.svg';import { m } from '#/paraglide/messages';import { useParams } from '#/router';
export const PostRepostedByScreen = () => {35 unmodified lines
function PostRepostedBy({ uri, initialCount }: { uri: string; initialCount?: number }) { const moderationOpts = useModerationOpts();
const { data: resolvedUri, error: resolveError, isLoading: isLoadingUri } = useResolveUriQuery(uri); const { data, isLoading: isLoadingRepostedBy, isFetchingNextPage, hasNextPage, fetchNextPage, error, } = usePostRepostedByQuery(resolvedUri?.uri); const { data: resolvedUri, error: resolveError } = useResolveUriQuery(uri); const { data, isPending, isFetchingNextPage, fetchNextPage, error } = usePostRepostedByQuery( resolvedUri?.uri, );
const isError = !!(resolveError || error);
const repostedBy = data?.pages ? data.pages.flatMap((page) => page.repostedBy) : [];
const onEndReached = () => { if (isFetchingNextPage || !hasNextPage || isError) { return; if (repostedBy.length < 1 || !moderationOpts) { if (isError) { return <ListError message={cleanError(resolveError || error)} />; } void fetchNextPage(); };
// the paged query stays pending while the uri resolves, so this covers both fetches if (!moderationOpts || ((isLoadingUri || isLoadingRepostedBy) && repostedBy.length < 1 && !isError)) { return <ProfileCard.LoadingPlaceholder count={initialCount} />; } if (isPending || !moderationOpts) { return <ProfileCard.LoadingPlaceholder count={initialCount} />; }
if (repostedBy.length < 1) { return ( <ListMaybePlaceholder isLoading={false} isError={isError} emptyType="results" emptyTitle={m['screens.postThread.engagement.repost.empty']()} emptyMessage={m['screens.postThread.engagement.repost.emptyPrompt']()} errorMessage={cleanError(resolveError || error)} /> ); return <ListEmpty icon={RepostIcon} message={m['screens.postThread.engagement.repost.emptyPrompt']()} />; }
return ( <List data={repostedBy} estimateHeight={PROFILE_ITEM_HEIGHT_ESTIMATE} keyExtractor={keyExtractor} onEndReached={onEndReached} onEndReachedThreshold={2} ListFooterComponent={ <ListFooter isFetchingNextPage={isFetchingNextPage} error={cleanError(error)} onRetry={fetchNextPage} /> <ListTail.Frame> {isFetchingNextPage ? ( <ListTail.Pending /> ) : isError ? ( <ListTail.Error message={cleanError(error)} onRetry={() => void fetchNextPage()} /> ) : null} </ListTail.Frame> } renderItem={({ index, item }) => ( <ProfileCard.Default moderationOpts={moderationOpts} profile={item} topBorder={index !== 0} /> )} onEndReached={() => void fetchNextPage()} onEndReachedThreshold={2} /> );}7 unmodified lines89101112121314151617181920212227 unmodified lines50515253515253545556575859605455565758596065666761626364697065667273746768697076717879808182838485867273747576777879809596819899100101102828384858687888990919293949596977 unmodified linesimport { useResolveDidQuery } from '#/state/queries/resolve-uri';import { useTitle } from '#/state/use-title';
import { List } from '#/components/List/List';import { ListFooter, ListMaybePlaceholder } from '#/components/Lists';import { ListEmpty } from '#/components/List/ListEmpty';import { ListError } from '#/components/List/ListError';import * as ListTail from '#/components/List/ListTail';import * as Layout from '#/components/web/Layout';import * as ProfileCard from '#/components/web/ProfileCard';
import PeopleIcon from '#/icons/central/People_round_outlined_radius1_stroke2.svg';import { m } from '#/paraglide/messages';import { useParams } from '#/router';
export const ProfileKnownFollowersScreen = () => {27 unmodified lines}
function ProfileKnownFollowers({ name }: { name: string }) { const moderationOpts = useModerationOpts(); const { data: resolvedDid, isLoading: isDidLoading, error: resolveError } = useResolveDidQuery(name); const { data, isLoading: isFollowersLoading, isFetchingNextPage, hasNextPage, fetchNextPage, error, refetch, } = useProfileKnownFollowersQuery(resolvedDid); const { data: resolvedDid, error: resolveError } = useResolveDidQuery(name); const { data, isPending, isFetchingNextPage, fetchNextPage, error, refetch } = useProfileKnownFollowersQuery(resolvedDid);
const isError = !!(resolveError || error); const followers = data?.pages ? data.pages.flatMap((page) => page.followers) : [];
const onEndReached = () => { if (isFetchingNextPage || !hasNextPage || isError) { return; if (followers.length < 1 || !moderationOpts) { if (isError) { return <ListError message={cleanError(resolveError || error)} onRetry={() => void refetch()} />; } void fetchNextPage(); };
// the paged query stays pending while the did resolves, so this covers both fetches if (!moderationOpts || ((isDidLoading || isFollowersLoading) && followers.length < 1 && !isError)) { return <ProfileCard.LoadingPlaceholder />; } if (isPending || !moderationOpts) { return <ProfileCard.LoadingPlaceholder />; }
if (followers.length < 1) { return ( <ListMaybePlaceholder isLoading={false} isError={isError} emptyType="results" emptyMessage={m['screens.profile.follow.knownFollowers.empty']({ name })} errorMessage={cleanError(resolveError || error)} onRetry={isError ? refetch : undefined} topBorder={false} /> <ListEmpty icon={PeopleIcon} message={m['screens.profile.follow.knownFollowers.empty']({ name })} /> ); }
return ( <List data={followers} estimateHeight={PROFILE_ITEM_HEIGHT_ESTIMATE} keyExtractor={keyExtractor} onEndReached={onEndReached} onEndReachedThreshold={2} ListFooterComponent={ <ListFooter isFetchingNextPage={isFetchingNextPage} error={cleanError(error)} onRetry={fetchNextPage} /> <ListTail.Frame> {isFetchingNextPage ? ( <ListTail.Pending /> ) : isError ? ( <ListTail.Error message={cleanError(error)} onRetry={() => void fetchNextPage()} /> ) : null} </ListTail.Frame> } renderItem={({ index, item }) => ( <ProfileCard.Default moderationOpts={moderationOpts} profile={item} topBorder={index !== 0} /> )} onEndReached={() => void fetchNextPage()} onEndReachedThreshold={2} /> );}9 unmodified lines10111213141415161718192047 unmodified lines686970717072737273747575767778798081828376777879808182838489909185868788939489909697989192939410095102103104105106969798991001011021111121131141151031041051061079 unmodified lines1171181191201331341211361371381391401221231241251261271281291301311321331341351361379 unmodified linesimport { useSession } from '#/state/session';import { useTitle } from '#/state/use-title';
import { List } from '#/components/List/List';import { ListFooter, ListMaybePlaceholder } from '#/components/Lists';import { ListEmpty } from '#/components/List/ListEmpty';import { ListError } from '#/components/List/ListError';import * as ListTail from '#/components/List/ListTail';import * as Layout from '#/components/web/Layout';import * as ProfileCard from '#/components/web/ProfileCard';
import PeopleRemoveIcon from '#/icons/central/PeopleRemove_round_outlined_radius3_stroke1.svg';47 unmodified lines const router = useRouter(); const { currentAccount } = useSession(); const moderationOpts = useModerationOpts();
const { data: resolvedDid, isLoading: isDidLoading, error: resolveError } = useResolveDidQuery(name); const { data: resolvedDid, error: resolveError } = useResolveDidQuery(name); const isMe = resolvedDid === currentAccount?.did; // your own followers read as a log of who arrived, so keep them chronological; on someone else's profile // the interesting question is who matters, which is what `top` answers.
const sort = isMe ? 'latest' : 'top'; const { data, isLoading: isFollowersLoading, isFetchingNextPage, hasNextPage, fetchNextPage, error, refetch, } = useProfileFollowersQuery(resolvedDid, { sort }); const { data, isPending, isFetchingNextPage, fetchNextPage, error, refetch } = useProfileFollowersQuery( resolvedDid, { sort }, );
const isError = !!resolveError || !!error;
const followers = data?.pages ? data.pages.flatMap((page) => page.followers) : [];
const onEndReached = () => { if (isFetchingNextPage || !hasNextPage || !!error) { return; if (followers.length < 1 || !moderationOpts) { if (isError) { return <ListError message={cleanError(resolveError || error)} onRetry={() => void refetch()} />; } void fetchNextPage(); };
// the paged query stays pending while the did resolves, so this covers both fetches if (!moderationOpts || ((isDidLoading || isFollowersLoading) && followers.length < 1 && !isError)) { return <ProfileCard.LoadingPlaceholder count={initialCount} />; } if (isPending || !moderationOpts) { return <ProfileCard.LoadingPlaceholder count={initialCount} />; }
if (followers.length < 1) { return ( <ListMaybePlaceholder isLoading={false} isError={isError} emptyType="results" emptyMessage={ <ListEmpty icon={PeopleRemoveIcon} message={ isMe ? m['view.profile.followers.followersEmpty']() : m['view.profile.followers.followersEmptyUser']() } errorMessage={cleanError(resolveError || error)} onRetry={isError ? refetch : undefined} useEmptyState={true} emptyStateIcon={PeopleRemoveIcon} emptyStateButton={{ button={{ label: m['common.action.goBack'](), text: m['common.action.goBack'](), color: 'secondary', size: 'small',9 unmodified lines <List data={followers} estimateHeight={PROFILE_ITEM_HEIGHT_ESTIMATE} keyExtractor={keyExtractor} onEndReached={onEndReached} onEndReachedThreshold={2} ListFooterComponent={ <ListFooter isFetchingNextPage={isFetchingNextPage} error={cleanError(error)} onRetry={fetchNextPage} /> <ListTail.Frame> {isFetchingNextPage ? ( <ListTail.Pending /> ) : isError ? ( <ListTail.Error message={cleanError(error)} onRetry={() => void fetchNextPage()} /> ) : null} </ListTail.Frame> } renderItem={({ index, item }) => ( <ProfileCard.Default moderationOpts={moderationOpts} profile={item} topBorder={index !== 0} /> )} onEndReached={() => void fetchNextPage()} onEndReachedThreshold={2} /> );}8 unmodified lines9101112131415161718198 unmodified lines paddingBlock: space.md, paddingInline: space.lg, borderBottom: `1px solid ${colors.borderContrastLow}`,});
export const emptyState = style({ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: space.xl,});