Bluesky app fork with some witchin' additions 馃挮
at post-text-option 158 lines 4.6 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 {getTerminology} from '#/lib/strings/terminology' 10import {useTerminologyPreference} from '#/state/preferences' 11import {logger} from '#/logger' 12import {type Shadow} from '#/state/cache/post-shadow' 13import {useBookmarkMutation} from '#/state/queries/bookmarks/useBookmarkMutation' 14import {useRequireAuth} from '#/state/session' 15import {useTheme} from '#/alf' 16import {Bookmark, BookmarkFilled} from '#/components/icons/Bookmark' 17import {Trash_Stroke2_Corner0_Rounded as TrashIcon} from '#/components/icons/Trash' 18import * as toast from '#/components/Toast' 19import {PostControlButton, PostControlButtonIcon} from './PostControlButton' 20 21export const BookmarkButton = memo(function BookmarkButton({ 22 post, 23 big, 24 logContext, 25 hitSlop, 26}: { 27 post: Shadow<AppBskyFeedDefs.PostView> 28 big?: boolean 29 logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo' 30 hitSlop?: Insets 31}): React.ReactNode { 32 const t = useTheme() 33 const {_} = useLingui() 34 const terminologyPreference = useTerminologyPreference() 35 const {mutateAsync: bookmark} = useBookmarkMutation() 36 const cleanError = useCleanError() 37 const requireAuth = useRequireAuth() 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 logger.metric('post:bookmark', {logContext}) 57 58 toast.show( 59 <toast.Outer> 60 <toast.Icon /> 61 <toast.Text> 62 <Trans>{_(getTerminology(terminologyPreference, { 63 skeet: msg`Skeet saved`, 64 post: msg`Post saved`, 65 spell: msg`Spell saved`, 66 }))}</Trans> 67 </toast.Text> 68 {!disableUndo && ( 69 <toast.Action 70 label={undoLabel} 71 onPress={() => remove({disableUndo: true})}> 72 {undoLabel} 73 </toast.Action> 74 )} 75 </toast.Outer>, 76 { 77 type: 'success', 78 }, 79 ) 80 } catch (e: any) { 81 const {raw, clean} = cleanError(e) 82 toast.show(clean || raw || e, { 83 type: 'error', 84 }) 85 } 86 } 87 88 const remove = async ({disableUndo}: {disableUndo?: boolean} = {}) => { 89 try { 90 await bookmark({ 91 action: 'delete', 92 uri: post.uri, 93 }) 94 95 logger.metric('post:unbookmark', {logContext}) 96 97 toast.show( 98 <toast.Outer> 99 <toast.Icon icon={TrashIcon} /> 100 <toast.Text> 101 <Trans>{_(getTerminology(terminologyPreference, { 102 skeet: msg`Removed from saved skeets`, 103 post: msg`Removed from saved posts`, 104 spell: msg`Removed from saved spells`, 105 }))}</Trans> 106 </toast.Text> 107 {!disableUndo && ( 108 <toast.Action 109 label={undoLabel} 110 onPress={() => save({disableUndo: true})}> 111 {undoLabel} 112 </toast.Action> 113 )} 114 </toast.Outer>, 115 ) 116 } catch (e: any) { 117 const {raw, clean} = cleanError(e) 118 toast.show(clean || raw || e, { 119 type: 'error', 120 }) 121 } 122 } 123 124 const onHandlePress = () => 125 requireAuth(async () => { 126 if (isBookmarked) { 127 await remove() 128 } else { 129 await save() 130 } 131 }) 132 133 return ( 134 <PostControlButton 135 testID="postBookmarkBtn" 136 big={big} 137 label={ 138 isBookmarked 139 ? _(getTerminology(terminologyPreference, { 140 skeet: msg`Remove from saved skeets`, 141 post: msg`Remove from saved posts`, 142 spell: msg`Remove from saved spells`, 143 })) 144 : _(getTerminology(terminologyPreference, { 145 skeet: msg`Add to saved skeets`, 146 post: msg`Add to saved posts`, 147 spell: msg`Add to saved spells`, 148 })) 149 } 150 onPress={onHandlePress} 151 hitSlop={hitSlop}> 152 <PostControlButtonIcon 153 fill={isBookmarked ? t.palette.primary_500 : undefined} 154 icon={isBookmarked ? BookmarkFilled : Bookmark} 155 /> 156 </PostControlButton> 157 ) 158})