Hook up shadow and mutation
This commit is contained in:
@@ -1,28 +1,68 @@
|
||||
import {memo} from 'react'
|
||||
import {type AppBskyFeedDefs, type AppBskyFeedPost} from '@atproto/api'
|
||||
import {memo, useCallback} from 'react'
|
||||
import {type AppBskyFeedDefs} from '@atproto/api'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import type React from 'react'
|
||||
|
||||
import {useCleanError} from '#/lib/hooks/useCleanError'
|
||||
import {type Shadow} from '#/state/cache/post-shadow'
|
||||
import {Bookmark} from '#/components/icons/Bookmark'
|
||||
import {useBookmarkMutation} from '#/state/queries/bookmarks/useBookmarkMutation'
|
||||
import {useTheme} from '#/alf'
|
||||
import {Bookmark, BookmarkFilled} from '#/components/icons/Bookmark'
|
||||
import {PostControlButton, PostControlButtonIcon} from './PostControlButton'
|
||||
|
||||
export const BookmarkButton = memo(function BookmarkButton({
|
||||
post,
|
||||
big,
|
||||
}: {
|
||||
post: Shadow<AppBskyFeedDefs.PostView>
|
||||
big?: boolean
|
||||
record: AppBskyFeedPost.Record
|
||||
}): React.ReactNode {
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
const {mutateAsync: bookmark} = useBookmarkMutation()
|
||||
const cleanError = useCleanError()
|
||||
|
||||
const {uri, cid, viewer} = post
|
||||
const isBookmarked = !!viewer?.bookmarked
|
||||
|
||||
const onHandlePress = useCallback(async () => {
|
||||
try {
|
||||
if (isBookmarked) {
|
||||
await bookmark({
|
||||
action: 'delete',
|
||||
uri,
|
||||
})
|
||||
// TODO toast
|
||||
} else {
|
||||
await bookmark({
|
||||
action: 'create',
|
||||
uri,
|
||||
cid,
|
||||
})
|
||||
// TODO toast
|
||||
}
|
||||
} catch (e) {
|
||||
const {raw, clean} = cleanError(e)
|
||||
console.log(clean || raw || e)
|
||||
// TODO toast
|
||||
}
|
||||
}, [uri, cid, isBookmarked, bookmark, cleanError])
|
||||
|
||||
return (
|
||||
<PostControlButton
|
||||
testID="postBookmarkBtn"
|
||||
big={big}
|
||||
label={_(msg`Bookmark this post`)}>
|
||||
<PostControlButtonIcon icon={Bookmark} />
|
||||
label={
|
||||
isBookmarked
|
||||
? _(msg`Remove this post from your bookmarks`)
|
||||
: _(msg`Save this post to your bookmarks`)
|
||||
}
|
||||
onPress={onHandlePress}>
|
||||
<PostControlButtonIcon
|
||||
fill={isBookmarked ? t.palette.primary_500 : undefined}
|
||||
icon={isBookmarked ? BookmarkFilled : Bookmark}
|
||||
/>
|
||||
</PostControlButton>
|
||||
)
|
||||
})
|
||||
|
||||
@@ -102,12 +102,20 @@ export function PostControlButton({
|
||||
|
||||
export function PostControlButtonIcon({
|
||||
icon: Comp,
|
||||
}: {
|
||||
style,
|
||||
...rest
|
||||
}: SVGIconProps & {
|
||||
icon: React.ComponentType<SVGIconProps>
|
||||
}) {
|
||||
const {big, color} = useContext(PostControlContext)
|
||||
|
||||
return <Comp style={[color, a.pointer_events_none]} width={big ? 22 : 18} />
|
||||
return (
|
||||
<Comp
|
||||
style={[color, a.pointer_events_none, style]}
|
||||
{...rest}
|
||||
width={big ? 22 : 18}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export function PostControlButtonText({style, ...props}: TextProps) {
|
||||
|
||||
@@ -284,7 +284,7 @@ let PostControls = ({
|
||||
a.flex_1,
|
||||
big ? a.gap_sm : a.gap_xs,
|
||||
]}>
|
||||
<BookmarkButton post={post} big={big} record={record} />
|
||||
<BookmarkButton post={post} big={big} />
|
||||
<ShareMenuButton
|
||||
testID="postShareBtn"
|
||||
post={post}
|
||||
|
||||
Vendored
+3
@@ -25,6 +25,7 @@ export interface PostShadow {
|
||||
embed: AppBskyEmbedRecord.View | AppBskyEmbedRecordWithMedia.View | undefined
|
||||
pinned: boolean
|
||||
optimisticReplyCount: number | undefined
|
||||
bookmarked: boolean | undefined
|
||||
}
|
||||
|
||||
export const POST_TOMBSTONE = Symbol('PostTombstone')
|
||||
@@ -132,6 +133,8 @@ function mergeShadow(
|
||||
like: 'likeUri' in shadow ? shadow.likeUri : post.viewer?.like,
|
||||
repost: 'repostUri' in shadow ? shadow.repostUri : post.viewer?.repost,
|
||||
pinned: 'pinned' in shadow ? shadow.pinned : post.viewer?.pinned,
|
||||
bookmarked:
|
||||
'bookmarked' in shadow ? shadow.bookmarked : post.viewer?.bookmarked,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import {useMutation, useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {isNetworkError} from '#/lib/strings/errors'
|
||||
import {logger} from '#/logger'
|
||||
import {updatePostShadow} from '#/state/cache/post-shadow'
|
||||
import {useAgent} from '#/state/session'
|
||||
|
||||
type MutationArgs =
|
||||
| {action: 'create'; uri: string; cid: string}
|
||||
| {action: 'delete'; uri: string}
|
||||
|
||||
export function useBookmarkMutation() {
|
||||
const qc = useQueryClient()
|
||||
const agent = useAgent()
|
||||
|
||||
return useMutation({
|
||||
async mutationFn(args: MutationArgs) {
|
||||
if (args.action === 'create') {
|
||||
updatePostShadow(qc, args.uri, {bookmarked: true})
|
||||
await agent.app.bsky.bookmark.createBookmark({
|
||||
uri: args.uri,
|
||||
cid: args.cid,
|
||||
})
|
||||
} else if (args.action === 'delete') {
|
||||
updatePostShadow(qc, args.uri, {bookmarked: false})
|
||||
await agent.app.bsky.bookmark.deleteBookmark({
|
||||
uri: args.uri,
|
||||
})
|
||||
}
|
||||
},
|
||||
onError(e, args) {
|
||||
if (args.action === 'create') {
|
||||
updatePostShadow(qc, args.uri, {bookmarked: false})
|
||||
} else if (args.action === 'delete') {
|
||||
updatePostShadow(qc, args.uri, {bookmarked: true})
|
||||
}
|
||||
|
||||
if (!isNetworkError(e)) {
|
||||
logger.error('bookmark mutation failed', {
|
||||
bookmarkAction: args.action,
|
||||
safeMessage: e,
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user