Bluesky app fork with some witchin' additions 馃挮 witchsky.app
bluesky fork client
119
fork

Configure Feed

Select the types of activity you want to include in your feed.

at a876aae44ea07494ebea9727350aa060b81f317b 151 lines 4.0 kB view raw
1import {memo} from 'react' 2import {type Insets} from 'react-native' 3import {type AppBskyFeedDefs} from '@atproto/api' 4import {msg} from '@lingui/core/macro' 5import {useLingui} from '@lingui/react' 6import {Trans} from '@lingui/react/macro' 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 active={isBookmarked} 140 activeColor={t.palette.primary_500} 141 label={ 142 isBookmarked 143 ? _(msg`Remove from saved posts`) 144 : _(msg`Add to saved posts`) 145 } 146 onPress={onHandlePress} 147 hitSlop={hitSlop}> 148 <PostControlButtonIcon icon={isBookmarked ? BookmarkFilled : Bookmark} /> 149 </PostControlButton> 150 ) 151})