fix: correct regressions from the list state component migration
247 unmodified linesExpand all248249250251252252253254255143 unmodified linesExpand all247 unmodified linesExpand all "common.list.description": "Lists allow you to see content from your favorite people.", "common.list.empty": "You have no lists.", "common.list.emptyUser": "No lists", "common.list.endOfList": "You have reached the end", "common.list.fetchError": "There was an issue fetching your lists. Tap here to try again.", "common.list.hidden": "Hidden list", "common.list.label": "Lists", "common.list.moderation.description": "Public, sharable lists of users to mute or block in bulk.", "common.list.moderationBy": "Moderation list by {handle}",143 unmodified linesExpand all13 unmodified lines1415161718181920212 unmodified lines13 unmodified lines "components.starterPack.create.another": "Create another", "components.starterPack.edit.feeds": "Edit feeds", "components.starterPack.edit.people": "Edit people", "components.starterPack.list.empty": "Starter packs let you share your favorite feeds and people with your friends.", "components.starterPack.list.error": "There was an issue fetching your starter packs. Tap here to try again.", "components.starterPack.membership.action.add": "Add {name} to starter pack", "components.starterPack.membership.action.remove": "Remove {name} from starter pack", "components.starterPack.share.a11yLabel": "Share link dialog", "components.starterPack.share.invitePrompt": "Invite people to this starter pack!",2 unmodified lines35 unmodified lines363738394040414243281 unmodified linesExpand all35 unmodified lines "screens.messages.chats.inboxZero": "Inbox zero!", "screens.messages.chats.markAllRead.action": "Mark all chats as read", "screens.messages.chats.markAllRead.error": "Failed to mark all chats as read", "screens.messages.chats.markAllRead.toast": "Marked all chats as read", "screens.messages.chats.reload.action": "Reload conversations", "screens.messages.chats.reload.error": "Failed to load conversations", "screens.messages.chats.title": "Chats", "screens.messages.composer.a11y": "Message input field", "screens.messages.composer.cancelReply": "Cancel reply",281 unmodified linesExpand all48 unmodified lines4950515253535455565 unmodified lines62636465676667686936 unmodified lines48 unmodified lines "cp=other": "{#t0}{formatted}{/t0} quotes" } } ], "screens.postThread.engagement.quote.empty": "No quotes yet", "screens.postThread.engagement.quote.emptyPrompt": "Nobody has quoted this yet. Maybe you should be the first!", "screens.postThread.engagement.quote.endOfFeed": "That's all, folks!", "screens.postThread.engagement.quote.title": "Quotes of this post", "screens.postThread.engagement.repost.count": [5 unmodified lines "cp=other": "{#t0}{formatted}{/t0} reposts" } } ], "screens.postThread.engagement.repost.empty": "No reposts yet", "screens.postThread.engagement.repost.emptyPrompt": "Nobody has reposted this yet. Maybe you should be the first!", "screens.postThread.engagement.repost.title": "Reposts of this post", "screens.postThread.engagement.save.count": [ {36 unmodified lines23 unmodified lines2425262728293031282930313233343533343536373839404112 unmodified lines54555657575859606162636465666723 unmodified lines const isError = !!(resolveError || error);
const likes = data?.pages ? data.pages.flatMap((page) => page.likes) : [];
if (likes.length < 1 || !moderationOpts) { if (isError) { return <ListError message={cleanError(resolveError || error)} />; } if (isError && likes.length < 1) { return <ListError message={cleanError(resolveError || error)} />; }
// the paged query stays pending while the uri resolves, so this covers both fetches if (isPending || !moderationOpts) { return <ProfileCard.LoadingPlaceholder count={initialCount} />; } if (isPending || !moderationOpts) { return <ProfileCard.LoadingPlaceholder count={initialCount} />; }
if (likes.length < 1) { return <ListEmpty icon={HeartIcon} message={m['common.like.emptyPrompt']()} />; }
return (12 unmodified lines } renderItem={({ index, item }) => ( <ProfileCard.Default moderationOpts={moderationOpts} profile={item.actor} topBorder={index !== 0} /> )} onEndReached={() => void fetchNextPage()} onEndReached={() => { if (isError) { return; } void fetchNextPage(); }} onEndReachedThreshold={2} /> );}42 unmodified lines434445464747484950518 unmodified lines42 unmodified lines if (isPending) { return <ListCard.LoadingPlaceholder count={10} />; }
return <ListEmpty icon={ListIcon} message={emptyText} />; return <ListEmpty icon={ListIcon} iconColor="textContrastMedium" message={emptyText} />; }
return ( <List8 unmodified lines12345678113 unmodified linesExpand all12212312412512412512612712812916 unmodified lines1461471481491501511521531541558 unmodified lines1641651661671661681691701711722 unmodified lines175176177178177179180181182183184185186187188import type { ActorIdentifier } from '@atcute/lexicons';
import { uniqueBy } from '@mary/array-fns';
import { cleanError } from '#/lib/errors';import { targetToShareUrl } from '#/lib/routes/app-links';import { enforceLen } from '#/lib/utils/text';
113 unmodified linesExpand all const { data, isFetchingNextPage, isPending, isError, error, refetch, fetchNextPage } = useSearchPostsQuery( { author, query: queryParam, sort }, );
const posts = data?.pages.flatMap((page) => page.posts) || [];
if (!hasSession) { return ( <SearchError title={m['common.search.loggedOutError']()}> <Text align="center" leading="snug" size="md">16 unmodified lines </SearchError> ); }
const posts = uniqueBy(data?.pages.flatMap((page) => page.posts) ?? [], (post) => post.uri);
if (posts.length < 1) { if (isError) { return <ListError message={cleanError(error)} onRetry={() => void refetch()} />; }8 unmodified lines return ( <List data={posts} estimateHeight={POST_ITEM_HEIGHT_ESTIMATE} keyExtractor={(item) => `${item.uri}|${item.cid}`} keyExtractor={(item) => item.uri} renderItem={({ index, item }) => <Post hideTopBorder={index === 0} post={item} />} ListFooterComponent={ <ListTail.Frame> {isFetchingNextPage ? (2 unmodified lines <ListTail.Error message={cleanError(error)} onRetry={() => void fetchNextPage()} /> ) : null} </ListTail.Frame> } onEndReached={() => void fetchNextPage()} onEndReached={() => { if (isError) { return; } void fetchNextPage(); }} onEndReachedThreshold={2} /> );}12345678 unmodified lines858687888789909192939 unmodified lines1031041051061051071081091101112 unmodified lines114115116117116118119120121122123124125126127import { uniqueBy } from '@mary/array-fns';
import { cleanError } from '#/lib/errors';import { targetToShareUrl } from '#/lib/routes/app-links';import { enforceLen } from '#/lib/utils/text';
78 unmodified lines sort, }, );
const posts = data?.pages.flatMap((page) => page.posts) || []; const posts = uniqueBy(data?.pages.flatMap((page) => page.posts) ?? [], (post) => post.uri);
if (posts.length < 1) { if (isError) { return <ListError message={cleanError(error)} onRetry={() => void refetch()} />;9 unmodified lines return ( <List data={posts} estimateHeight={POST_ITEM_HEIGHT_ESTIMATE} keyExtractor={(item) => `${item.uri}|${item.cid}`} keyExtractor={(item) => item.uri} renderItem={({ index, item }) => <Post hideTopBorder={index === 0} post={item} />} ListFooterComponent={ <ListTail.Frame> {isFetchingNextPage ? (2 unmodified lines <ListTail.Error message={cleanError(error)} onRetry={() => void fetchNextPage()} /> ) : null} </ListTail.Frame> } onEndReached={() => void fetchNextPage()} onEndReached={() => { if (isError) { return; } void fetchNextPage(); }} onEndReachedThreshold={2} /> );}112334565671011121389101617181920212223242526272829303132333435111213import type { ReactElement } from 'react';import type { ComponentProps } from 'react';
import { EmptyState, type EmptyStateButtonProps, type EmptyStateIcon } from '#/components/EmptyState';import { EmptyState } from '#/components/EmptyState';
import { m } from '#/paraglide/messages';
/** * renders an empty list state. * * @param button optional action * @param className container class * @param icon state icon * @param message empty-state message * @param props forwarded to {@link EmptyState} * @returns the empty state */export function ListEmpty({ button, className, icon, message,}: { button?: EmptyStateButtonProps; className?: string; icon?: EmptyStateIcon | ReactElement; message?: string;}) { return ( <EmptyState button={button} className={className} icon={icon} message={message ?? m['common.list.noResults']()} messageColor="textContrastMedium" /> );export function ListEmpty(props: Omit<ComponentProps<typeof EmptyState>, 'messageColor'>) { return <EmptyState {...props} messageColor="textContrastMedium" />;}16 unmodified lines1718192021212223242526272828293031321 unmodified line16 unmodified lines onRetry, title,}: { hideBackButton?: boolean; message?: string; message: string; onRetry?: () => void; title?: string;}) { return ( <Error hideBackButton={hideBackButton} message={message || m['common.error.generic']()} message={message} onRetry={onRetry} title={title ?? m['common.error.oops']()} /> );1 unmodified line112345678789102 unmodified lines1314151619171819202133 unmodified linesimport { createVar, fallbackVar, style } from '@vanilla-extract/css';import { style } from '@vanilla-extract/css';
import { colors } from '#/styles/colors';import { recipe } from '#/styles/recipe';import { borderRadius, space } from '#/styles/tokens.css';
export const minHeightVar = createVar();
export const frame = recipe( { base: { boxSizing: 'border-box',2 unmodified lines alignItems: 'center', paddingTop: 30, paddingBottom: space.lg, width: '100%', minHeight: fallbackVar(minHeightVar, '180px'), minHeight: 180, }, defaultVariants: { border: true }, variants: { border: {33 unmodified lines123434565 unmodified lines12131415181617212223242526272829303132333435363718192021222338 unmodified linesimport type { ReactNode } from 'react';
import { assignInlineVars } from '@vanilla-extract/dynamic';
import { Spinner } from '#/components/Spinner';import { Text } from '#/components/Text';import { Button, ButtonText } from '#/components/web/Button';
5 unmodified lines * reserves space for pagination state. * * @param border whether to show the divider * @param children pagination state * @param minHeight reserved height in pixels * @returns the list tail */export function Frame({ border = true, children, minHeight,}: { border?: boolean; children?: ReactNode; minHeight?: number;}) { return ( <div className={css.frame({ border })} style={minHeight != null ? assignInlineVars({ [css.minHeightVar]: `${minHeight}px` }) : undefined} > {children} </div> );export function Frame({ border = true, children }: { border?: boolean; children?: ReactNode }) { return <div className={css.frame({ border })}>{children}</div>;}
/** @returns a pagination spinner */export function Pending() {38 unmodified lines509 unmodified linesExpand all51051151251351451451551651751851952052152252315 unmodified lines509 unmodified linesExpand all ) : null} </ListTail.Frame> } onScrolledDownChange={handleScrolledDownChange} onEndReached={() => void fetchNextPage()} onEndReached={() => { if (isError) { return; } void fetchNextPage(); }} onEndReachedThreshold={2} onItemSeen={onItemSeen} /> );15 unmodified lines83 unmodified lines84858687888889909192939495969719 unmodified lines83 unmodified lines ) : null} </ListTail.Frame> ) } onEndReached={() => void fetchNextPage()} onEndReached={() => { if (isError) { return; } void fetchNextPage(); }} onEndReachedThreshold={2} /> );}19 unmodified lines125 unmodified linesExpand all12612712812913013013113213313413513613713813957 unmodified lines125 unmodified linesExpand all <ListTail.Error message={cleanError(error)} onRetry={() => void fetchNextPage()} /> ) : null} </ListTail.Frame> } onEndReached={() => void fetchNextPage()} onEndReached={() => { if (isError) { return; } void fetchNextPage(); }} onEndReachedThreshold={2} /> );}57 unmodified lines