diff --git a/src/screens/PostThread/components/ThreadItemAnchor.tsx b/src/screens/PostThread/components/ThreadItemAnchor.tsx index e924ab492..c66cf8126 100644 --- a/src/screens/PostThread/components/ThreadItemAnchor.tsx +++ b/src/screens/PostThread/components/ThreadItemAnchor.tsx @@ -26,6 +26,7 @@ import {useDisableLikesMetrics} from '#/state/preferences/disable-likes-metrics' import {useDisableQuotesMetrics} from '#/state/preferences/disable-quotes-metrics' import {useDisableRepostsMetrics} from '#/state/preferences/disable-reposts-metrics' import {useDisableSavesMetrics} from '#/state/preferences/disable-saves-metrics' +import {useCompactPosts} from '#/state/preferences/compact-posts' import {useEnableSquareAvatars} from '#/state/preferences/enable-square-avatars' import {useEnableSquareButtons} from '#/state/preferences/enable-square-buttons' import {useHideScaryFollowButtons} from '#/state/preferences/hide-scary-follow-buttons' @@ -145,12 +146,28 @@ function ThreadItemAnchorDeleted({isRoot}: {isRoot: boolean}) { ) } -function ThreadItemAnchorParentReplyLine({isRoot}: {isRoot: boolean}) { +function ThreadItemAnchorParentReplyLine({ + isRoot, + compactPosts = false, +}: { + isRoot: boolean + compactPosts?: boolean +}) { const t = useTheme() + const avatarSize = compactPosts ? 34 : 42 + const sidePadding = compactPosts ? OUTER_SPACE - 2 : OUTER_SPACE return !isRoot ? ( - - + + - + - + @@ -361,20 +399,22 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({ emoji style={[ a.flex_shrink, - a.text_lg, + isCompactPosts ? a.text_md : a.text_lg, a.font_semi_bold, a.leading_snug, ]} numberOfLines={1}> - {sanitizeDisplayName( - post.author.displayName || - sanitizeHandle(post.author.handle), - moderation.ui('displayName'), - )} + {displayName} + style={[ + a.pl_xs, + a.flex_row, + a.gap_2xs, + a.align_center, + isCompactPosts && {marginTop: 1}, + ]}> - + - - + + + childContainerStyle={[isCompactPosts ? a.pt_2xs : a.pt_sm]}> {richText?.text ? ( @@ -419,14 +462,20 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({ enableTags selectable value={richText} - style={[a.flex_1, a.text_lg]} + style={[a.flex_1, isCompactPosts ? a.text_md : a.text_lg]} authorHandle={post.author.handle} shouldProxyLinks={true} /> ) : undefined} - + {post.embed && ( - + {(post.repostCount !== 0 && !disableRepostsMetrics) || (post.likeCount !== 0 && !disableLikesMetrics) || @@ -456,8 +506,8 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({ }, a.border_t, a.border_b, - a.mt_md, - a.py_md, + isCompactPosts ? a.mt_sm : a.mt_md, + isCompactPosts ? a.py_sm : a.py_md, t.atoms.border_contrast_low, ]}> {post.repostCount != null && @@ -546,15 +596,16 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({ ) : null} ['value']['post'] isThreadAuthor: boolean + compactPosts: boolean }) { const t = useTheme() const {i18n} = useLingui() @@ -589,7 +642,12 @@ function ExpandedPostDetails({ const via = post.record.via as string | undefined return ( - + diff --git a/src/screens/PostThread/components/ThreadItemPost.tsx b/src/screens/PostThread/components/ThreadItemPost.tsx index 303a8e9e2..3e70a9906 100644 --- a/src/screens/PostThread/components/ThreadItemPost.tsx +++ b/src/screens/PostThread/components/ThreadItemPost.tsx @@ -18,6 +18,7 @@ import { usePostShadow, } from '#/state/cache/post-shadow' import {useEnableSquareAvatars} from '#/state/preferences/enable-square-avatars' +import {useCompactPosts} from '#/state/preferences/compact-posts' import {type ThreadItem} from '#/state/queries/usePostThread/types' import {useSession} from '#/state/session' import {type OnPostSuccessData} from '#/state/shell/composer' @@ -92,7 +93,11 @@ function ThreadItemPostDeleted({ return ( - + {children} @@ -159,17 +165,23 @@ const ThreadItemPostOuterWrapper = memo(function ThreadItemPostOuterWrapper({ const ThreadItemPostParentReplyLine = memo( function ThreadItemPostParentReplyLine({ item, - }: Pick) { + avatarSize, + compactPosts, + }: { + item: Extract + avatarSize: number + compactPosts: boolean + }) { const t = useTheme() return ( - - + + {item.ui.showParentReplyLine && ( - + - + - + )} + setCompactPosts(value)}> + + + + Use compact view for posts + + + + diff --git a/src/state/persisted/schema.ts b/src/state/persisted/schema.ts index f1095652e..f5bf7171d 100644 --- a/src/state/persisted/schema.ts +++ b/src/state/persisted/schema.ts @@ -197,6 +197,7 @@ const schema = z.object({ hideScaryFollowButtons: z.boolean().optional(), confirmFollowUnfollow: z.boolean().optional(), discoverContextEnabled: z.boolean().optional(), + compactPosts: z.boolean().optional(), enableSquareAvatars: z.boolean().optional(), enableSquareButtons: z.boolean().optional(), useCompactAccountSwitcher: z.boolean().optional(), @@ -340,6 +341,7 @@ export const defaults: Schema = { hideScaryFollowButtons: false, confirmFollowUnfollow: true, discoverContextEnabled: false, + compactPosts: false, enableSquareAvatars: true, enableSquareButtons: true, useCompactAccountSwitcher: false, diff --git a/src/state/preferences/compact-posts.tsx b/src/state/preferences/compact-posts.tsx new file mode 100644 index 000000000..1062bc5f0 --- /dev/null +++ b/src/state/preferences/compact-posts.tsx @@ -0,0 +1,50 @@ +import { + createContext, + type PropsWithChildren, + useCallback, + useContext, + useEffect, + useState, +} from 'react' + +import * as persisted from '#/state/persisted' + +type StateContext = persisted.Schema['compactPosts'] +type SetContext = (v: persisted.Schema['compactPosts']) => void + +const stateContext = createContext(persisted.defaults.compactPosts) +const setContext = createContext( + (_: persisted.Schema['compactPosts']) => {}, +) + +export function Provider({children}: PropsWithChildren<{}>) { + const [state, setState] = useState(persisted.get('compactPosts')) + + const setStateWrapped = useCallback( + (value: persisted.Schema['compactPosts']) => { + setState(value) + persisted.write('compactPosts', value) + }, + [setState], + ) + + useEffect(() => { + return persisted.onUpdate('compactPosts', next => { + setState(next) + }) + }, [setStateWrapped]) + + return ( + + {children} + + ) +} + +export function useCompactPosts() { + return useContext(stateContext) +} + +export function useSetCompactPosts() { + return useContext(setContext) +} diff --git a/src/state/preferences/index.tsx b/src/state/preferences/index.tsx index 14887250c..6615228d5 100644 --- a/src/state/preferences/index.tsx +++ b/src/state/preferences/index.tsx @@ -7,6 +7,7 @@ import {Provider as AutoCompactAccountSwitcherProvider} from './auto-compact-acc import {Provider as AutoLikeOnRepostProvider} from './auto-like-on-repost' import {Provider as AutoplayProvider} from './autoplay' import {Provider as CompactAccountSwitcherProvider} from './compact-account-switcher' +import {Provider as CompactPostsProvider} from './compact-posts' import {Provider as ConfirmFollowUnfollowProvider} from './confirm-follow-unfollow' import {Provider as ConstellationProvider} from './constellation-enabled' import {Provider as ConstellationInstanceProvider} from './constellation-instance' @@ -81,6 +82,7 @@ export { useConfirmFollowUnfollow, useSetConfirmFollowUnfollow, } from './confirm-follow-unfollow' +export {useCompactPosts, useSetCompactPosts} from './compact-posts' export { useDisableComposerPrompt, useSetDisableComposerPrompt, @@ -191,7 +193,8 @@ export function Provider({children}: PropsWithChildren<{}>) { - + + @@ -219,7 +222,8 @@ export function Provider({children}: PropsWithChildren<{}>) { - + + diff --git a/src/state/preferences/settings-sync.tsx b/src/state/preferences/settings-sync.tsx index c8c38d9d4..6f1379670 100644 --- a/src/state/preferences/settings-sync.tsx +++ b/src/state/preferences/settings-sync.tsx @@ -65,6 +65,7 @@ export const SYNCED_PREFS_KEYS = [ 'hideScaryFollowButtons', 'confirmFollowUnfollow', 'discoverContextEnabled', + 'compactPosts', 'enableSquareAvatars', 'enableSquareButtons', 'useCompactAccountSwitcher', diff --git a/src/view/com/post/Post.tsx b/src/view/com/post/Post.tsx index d168bcddb..0acafea7e 100644 --- a/src/view/com/post/Post.tsx +++ b/src/view/com/post/Post.tsx @@ -20,6 +20,7 @@ import { usePostShadow, } from '#/state/cache/post-shadow' import {useModerationOpts} from '#/state/preferences/moderation-opts' +import {useCompactPosts} from '#/state/preferences/compact-posts' import {unstableCacheProfileView} from '#/state/queries/profile' import {Link} from '#/view/com/util/Link' import {PostMeta} from '#/view/com/util/PostMeta' @@ -120,6 +121,7 @@ function PostInner({ const queryClient = useQueryClient() const t = useTheme() const {openComposer} = useOpenComposer() + const compactPosts = useCompactPosts() const [limitLines, setLimitLines] = useState( () => countLines(richText?.text) >= MAX_POST_LINES, ) @@ -165,6 +167,7 @@ function PostInner({ styles.outer, t.atoms.border_contrast_low, !hideTopBorder && a.border_t, + compactPosts && styles.outerCompact, style, ]} onBeforePress={onBeforePress} @@ -193,7 +196,7 @@ function PostInner({ - + {isThreadChild && ( - + {reason && ( - + + childContainerStyle={[ + styles.contentHiderChild, + compactPosts && styles.contentHiderChildCompact, + ]}>