Bluesky app fork with some witchin' additions 馃挮
at main 152 lines 4.0 kB view raw
1import {memo} from 'react' 2import {type Insets} from 'react-native' 3import {type AppBskyFeedDefs} from '@atproto/api' 4import {msg, Trans} from '@lingui/macro' 5import {useLingui} from '@lingui/react' 6import type React from 'react' 7 8import {useCleanError} from '#/lib/hooks/useCleanError' 9import {type Shadow} from '#/state/cache/post-shadow' 10import {useFeedFeedbackContext} from '#/state/feed-feedback' 11import {useBookmarkMutation} from '#/state/queries/bookmarks/useBookmarkMutation' 12import {useRequireAuth} from '#/state/session' 13import {useTheme} from '#/alf' 14import {Bookmark, BookmarkFilled} from '#/components/icons/Bookmark' 15import {Trash_Stroke2_Corner0_Rounded as TrashIcon} from '#/components/icons/Trash' 16import * as toast from '#/components/Toast' 17import {useAnalytics} from '#/analytics' 18import {PostControlButton, PostControlButtonIcon} from './PostControlButton' 19 20export const BookmarkButton = memo(function BookmarkButton({ 21 post, 22 big, 23 logContext, 24 hitSlop, 25}: { 26 post: Shadow<AppBskyFeedDefs.PostView> 27 big?: boolean 28 logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo' 29 hitSlop?: Insets 30}): React.ReactNode { 31 const t = useTheme() 32 const ax = useAnalytics() 33 const {_} = useLingui() 34 const {mutateAsync: bookmark} = useBookmarkMutation() 35 const cleanError = useCleanError() 36 const requireAuth = useRequireAuth() 37 const {feedDescriptor} = useFeedFeedbackContext() 38 39 const {viewer} = post 40 const isBookmarked = !!viewer?.bookmarked 41 42 const undoLabel = _( 43 msg({ 44 message: `Undo`, 45 context: `Button label to undo saving/removing a post from saved posts.`, 46 }), 47 ) 48 49 const save = async ({disableUndo}: {disableUndo?: boolean} = {}) => { 50 try { 51 await bookmark({ 52 action: 'create', 53 post, 54 }) 55 56 ax.metric('post:bookmark', { 57 uri: post.uri, 58 authorDid: post.author.did, 59 logContext, 60 feedDescriptor, 61 }) 62 63 toast.show( 64 <toast.Outer> 65 <toast.Icon /> 66 <toast.Text> 67 <Trans>Post saved</Trans> 68 </toast.Text> 69 {!disableUndo && ( 70 <toast.Action 71 label={undoLabel} 72 onPress={() => remove({disableUndo: true})}> 73 {undoLabel} 74 </toast.Action> 75 )} 76 </toast.Outer>, 77 { 78 type: 'success', 79 }, 80 ) 81 } catch (e: any) { 82 const {raw, clean} = cleanError(e) 83 toast.show(clean || raw || e, { 84 type: 'error', 85 }) 86 } 87 } 88 89 const remove = async ({disableUndo}: {disableUndo?: boolean} = {}) => { 90 try { 91 await bookmark({ 92 action: 'delete', 93 uri: post.uri, 94 }) 95 96 ax.metric('post:unbookmark', { 97 uri: post.uri, 98 authorDid: post.author.did, 99 logContext, 100 feedDescriptor, 101 }) 102 103 toast.show( 104 <toast.Outer> 105 <toast.Icon icon={TrashIcon} /> 106 <toast.Text> 107 <Trans>Removed from saved posts</Trans> 108 </toast.Text> 109 {!disableUndo && ( 110 <toast.Action 111 label={undoLabel} 112 onPress={() => save({disableUndo: true})}> 113 {undoLabel} 114 </toast.Action> 115 )} 116 </toast.Outer>, 117 ) 118 } catch (e: any) { 119 const {raw, clean} = cleanError(e) 120 toast.show(clean || raw || e, { 121 type: 'error', 122 }) 123 } 124 } 125 126 const onHandlePress = () => 127 requireAuth(async () => { 128 if (isBookmarked) { 129 await remove() 130 } else { 131 await save() 132 } 133 }) 134 135 return ( 136 <PostControlButton 137 testID="postBookmarkBtn" 138 big={big} 139 label={ 140 isBookmarked 141 ? _(msg`Remove from saved posts`) 142 : _(msg`Add to saved posts`) 143 } 144 onPress={onHandlePress} 145 hitSlop={hitSlop}> 146 <PostControlButtonIcon 147 fill={isBookmarked ? t.palette.primary_500 : undefined} 148 icon={isBookmarked ? BookmarkFilled : Bookmark} 149 /> 150 </PostControlButton> 151 ) 152})