📓 Bookmarks (#8976)

* Add button to controls, respace

* Hook up shadow and mutation

* Add Bookmarks screen

* Build out Bookmarks screen

* Handle removals via shadow

* Use truncateAndInvalidate strategy

* Add empty state

* Add toasts

* Add undo buttons to toasts

* Stage NUX, needs image

* Finesse post controls

* New reply icon

* Use curvier variant of repost icon

* Prevent layout shift with align_start

* Update api pkg

* Swap in new image

* Limit spacing on desktop

* Rm decimals over 10k

* Better optimistic adding/removing

* Add metrics

* Comment

* Remove unused code block

* Remove debug limit

* Fork shadow for web/native

* Tweak alt

* add preventExpansion: true

* Refine hitslop

* Add count to anchor

* Reduce space in compact mode

---------

Co-authored-by: Samuel Newman <mozzius@protonmail.com>
This commit is contained in:
Eric Bailey
2025-09-04 17:30:15 -05:00
committed by GitHub
parent 04b869714e
commit 535d4d6cf7
38 changed files with 1251 additions and 163 deletions
@@ -32,7 +32,6 @@ import {useSession} from '#/state/session'
import {type OnPostSuccessData} from '#/state/shell/composer'
import {useMergedThreadgateHiddenReplies} from '#/state/threadgate-hidden-replies'
import {type PostSource} from '#/state/unstable-post-source'
import {formatCount} from '#/view/com/util/numeric/format'
import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar'
import {ThreadItemAnchorFollowButton} from '#/screens/PostThread/components/ThreadItemAnchorFollowButton'
import {
@@ -53,6 +52,7 @@ import {PostAlerts} from '#/components/moderation/PostAlerts'
import {type AppModerationCause} from '#/components/Pills'
import {Embed, PostEmbedViewContext} from '#/components/Post/Embed'
import {PostControls} from '#/components/PostControls'
import {formatPostStatCount} from '#/components/PostControls/util'
import {ProfileHoverCard} from '#/components/ProfileHoverCard'
import * as Prompt from '#/components/Prompt'
import {RichText} from '#/components/RichText'
@@ -415,13 +415,18 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({
/>
{post.repostCount !== 0 ||
post.likeCount !== 0 ||
post.quoteCount !== 0 ? (
post.quoteCount !== 0 ||
post.bookmarkCount !== 0 ? (
// Show this section unless we're *sure* it has no engagement.
<View
style={[
a.flex_row,
a.flex_wrap,
a.align_center,
a.gap_lg,
{
rowGap: a.gap_sm.gap,
columnGap: a.gap_lg.gap,
},
a.border_t,
a.border_b,
a.mt_md,
@@ -434,7 +439,7 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({
testID="repostCount-expanded"
style={[a.text_md, t.atoms.text_contrast_medium]}>
<Text style={[a.text_md, a.font_bold, t.atoms.text]}>
{formatCount(i18n, post.repostCount)}
{formatPostStatCount(i18n, post.repostCount)}
</Text>{' '}
<Plural
value={post.repostCount}
@@ -452,7 +457,7 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({
testID="quoteCount-expanded"
style={[a.text_md, t.atoms.text_contrast_medium]}>
<Text style={[a.text_md, a.font_bold, t.atoms.text]}>
{formatCount(i18n, post.quoteCount)}
{formatPostStatCount(i18n, post.quoteCount)}
</Text>{' '}
<Plural
value={post.quoteCount}
@@ -468,12 +473,28 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({
testID="likeCount-expanded"
style={[a.text_md, t.atoms.text_contrast_medium]}>
<Text style={[a.text_md, a.font_bold, t.atoms.text]}>
{formatCount(i18n, post.likeCount)}
{formatPostStatCount(i18n, post.likeCount)}
</Text>{' '}
<Plural value={post.likeCount} one="like" other="likes" />
</Text>
</Link>
) : null}
{post.bookmarkCount != null && post.bookmarkCount !== 0 ? (
<Link to={likesHref} label={_(msg`Saves of this post`)}>
<Text
testID="bookmarkCount-expanded"
style={[a.text_md, t.atoms.text_contrast_medium]}>
<Text style={[a.text_md, a.font_bold, t.atoms.text]}>
{formatPostStatCount(i18n, post.bookmarkCount)}
</Text>{' '}
<Plural
value={post.bookmarkCount}
one="save"
other="saves"
/>
</Text>
</Link>
) : null}
</View>
) : null}
<View
@@ -368,6 +368,7 @@ const ThreadItemTreePostInner = memo(function ThreadItemTreePostInner({
</View>
)}
<PostControls
variant="compact"
post={postShadow}
record={record}
richText={richText}