Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

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

at cope-settings-sync 154 lines 4.1 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 onLongPress, 26}: { 27 post: Shadow<AppBskyFeedDefs.PostView> 28 big?: boolean 29 logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo' 30 hitSlop?: Insets 31 onLongPress?: () => void 32}): React.ReactNode { 33 const t = useTheme() 34 const ax = useAnalytics() 35 const {_} = useLingui() 36 const {mutateAsync: bookmark} = useBookmarkMutation() 37 const cleanError = useCleanError() 38 const requireAuth = useRequireAuth() 39 const {feedDescriptor} = useFeedFeedbackContext() 40 41 const {viewer} = post 42 const isBookmarked = !!viewer?.bookmarked 43 44 const undoLabel = _( 45 msg({ 46 message: `Undo`, 47 context: `Button label to undo saving/removing a post from saved posts.`, 48 }), 49 ) 50 51 const save = async ({disableUndo}: {disableUndo?: boolean} = {}) => { 52 try { 53 await bookmark({ 54 action: 'create', 55 post, 56 }) 57 58 ax.metric('post:bookmark', { 59 uri: post.uri, 60 authorDid: post.author.did, 61 logContext, 62 feedDescriptor, 63 }) 64 65 toast.show( 66 <toast.Outer> 67 <toast.Icon /> 68 <toast.Text> 69 <Trans>Post saved</Trans> 70 </toast.Text> 71 {!disableUndo && ( 72 <toast.Action 73 label={undoLabel} 74 onPress={() => remove({disableUndo: true})}> 75 {undoLabel} 76 </toast.Action> 77 )} 78 </toast.Outer>, 79 { 80 type: 'success', 81 }, 82 ) 83 } catch (e: any) { 84 const {raw, clean} = cleanError(e) 85 toast.show(clean || raw || e, { 86 type: 'error', 87 }) 88 } 89 } 90 91 const remove = async ({disableUndo}: {disableUndo?: boolean} = {}) => { 92 try { 93 await bookmark({ 94 action: 'delete', 95 uri: post.uri, 96 }) 97 98 ax.metric('post:unbookmark', { 99 uri: post.uri, 100 authorDid: post.author.did, 101 logContext, 102 feedDescriptor, 103 }) 104 105 toast.show( 106 <toast.Outer> 107 <toast.Icon icon={TrashIcon} /> 108 <toast.Text> 109 <Trans>Removed from saved posts</Trans> 110 </toast.Text> 111 {!disableUndo && ( 112 <toast.Action 113 label={undoLabel} 114 onPress={() => save({disableUndo: true})}> 115 {undoLabel} 116 </toast.Action> 117 )} 118 </toast.Outer>, 119 ) 120 } catch (e: any) { 121 const {raw, clean} = cleanError(e) 122 toast.show(clean || raw || e, { 123 type: 'error', 124 }) 125 } 126 } 127 128 const onHandlePress = () => 129 requireAuth(async () => { 130 if (isBookmarked) { 131 await remove() 132 } else { 133 await save() 134 } 135 }) 136 137 return ( 138 <PostControlButton 139 testID="postBookmarkBtn" 140 big={big} 141 active={isBookmarked} 142 activeColor={t.palette.primary_500} 143 label={ 144 isBookmarked 145 ? _(msg`Remove from saved posts`) 146 : _(msg`Add to saved posts`) 147 } 148 onPress={onHandlePress} 149 onLongPress={onLongPress} 150 hitSlop={hitSlop}> 151 <PostControlButtonIcon icon={isBookmarked ? BookmarkFilled : Bookmark} /> 152 </PostControlButton> 153 ) 154})