Bluesky app fork with some witchin' additions 馃挮
fork

Configure Feed

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

at cb54cd87bb36c92ebcd59cd0b2edb77245b1b999 217 lines 6.3 kB view raw
1import {memo, useCallback} from 'react' 2import {View} from 'react-native' 3import {msg, plural, Trans} from '@lingui/macro' 4import {useLingui} from '@lingui/react' 5 6import {useHaptics} from '#/lib/haptics' 7import {useRequireAuth} from '#/state/session' 8import {atoms as a, useTheme} from '#/alf' 9import {Button, ButtonText} from '#/components/Button' 10import * as Dialog from '#/components/Dialog' 11import {CloseQuote_Stroke2_Corner1_Rounded as QuoteIcon} from '#/components/icons/Quote' 12import {Repost_Stroke2_Corner3_Rounded as RepostIcon} from '#/components/icons/Repost' 13import {useFormatPostStatCount} from '#/components/PostControls/util' 14import {Text} from '#/components/Typography' 15import { 16 PostControlButton, 17 PostControlButtonIcon, 18 PostControlButtonText, 19} from './PostControlButton' 20 21interface Props { 22 isReposted: boolean 23 repostCount?: number 24 onRepost: () => void 25 onQuote: () => void 26 big?: boolean 27 embeddingDisabled: boolean 28} 29 30let RepostButton = ({ 31 isReposted, 32 repostCount, 33 onRepost, 34 onQuote, 35 big, 36 embeddingDisabled, 37}: Props): React.ReactNode => { 38 const t = useTheme() 39 const {_} = useLingui() 40 const requireAuth = useRequireAuth() 41 const dialogControl = Dialog.useDialogControl() 42 const formatPostStatCount = useFormatPostStatCount() 43 44 const onPress = () => requireAuth(() => dialogControl.open()) 45 46 const onLongPress = () => 47 requireAuth(() => { 48 if (embeddingDisabled) { 49 dialogControl.open() 50 } else { 51 onQuote() 52 } 53 }) 54 55 return ( 56 <> 57 <PostControlButton 58 testID="repostBtn" 59 active={isReposted} 60 activeColor={t.palette.positive_500} 61 big={big} 62 onPress={onPress} 63 onLongPress={onLongPress} 64 label={ 65 isReposted 66 ? _( 67 msg({ 68 message: `Undo repost (${plural(repostCount || 0, { 69 one: '# repost', 70 other: '# reposts', 71 })})`, 72 comment: 73 'Accessibility label for the repost button when the post has been reposted, verb followed by number of reposts and noun', 74 }), 75 ) 76 : _( 77 msg({ 78 message: `Repost (${plural(repostCount || 0, { 79 one: '# repost', 80 other: '# reposts', 81 })})`, 82 comment: 83 'Accessibility label for the repost button when the post has not been reposted, verb form followed by number of reposts and noun form', 84 }), 85 ) 86 }> 87 <PostControlButtonIcon icon={RepostIcon} /> 88 {typeof repostCount !== 'undefined' && repostCount > 0 && ( 89 <PostControlButtonText testID="repostCount"> 90 {formatPostStatCount(repostCount)} 91 </PostControlButtonText> 92 )} 93 </PostControlButton> 94 <Dialog.Outer 95 control={dialogControl} 96 nativeOptions={{preventExpansion: true}}> 97 <Dialog.Handle /> 98 <RepostButtonDialogInner 99 isReposted={isReposted} 100 onRepost={onRepost} 101 onQuote={onQuote} 102 embeddingDisabled={embeddingDisabled} 103 /> 104 </Dialog.Outer> 105 </> 106 ) 107} 108RepostButton = memo(RepostButton) 109export {RepostButton} 110 111let RepostButtonDialogInner = ({ 112 isReposted, 113 onRepost, 114 onQuote, 115 embeddingDisabled, 116}: { 117 isReposted: boolean 118 onRepost: () => void 119 onQuote: () => void 120 embeddingDisabled: boolean 121}): React.ReactNode => { 122 const t = useTheme() 123 const {_} = useLingui() 124 const playHaptic = useHaptics() 125 const control = Dialog.useDialogContext() 126 127 const onPressRepost = useCallback(() => { 128 if (!isReposted) playHaptic() 129 130 control.close(() => { 131 onRepost() 132 }) 133 }, [control, isReposted, onRepost, playHaptic]) 134 135 const onPressQuote = useCallback(() => { 136 playHaptic() 137 control.close(() => { 138 onQuote() 139 }) 140 }, [control, onQuote, playHaptic]) 141 142 const onPressClose = useCallback(() => control.close(), [control]) 143 144 return ( 145 <Dialog.ScrollableInner label={_(msg`Reskeet or quote skeet`)}> 146 <View style={a.gap_xl}> 147 <View style={a.gap_xs}> 148 <Button 149 style={[a.justify_start, a.px_md, a.gap_sm]} 150 label={ 151 isReposted 152 ? _(msg`Remove reskeet`) 153 : _(msg({message: `Reskeet`, context: 'action'})) 154 } 155 onPress={onPressRepost} 156 size="large" 157 variant="ghost" 158 color="primary"> 159 <RepostIcon size="lg" fill={t.palette.primary_500} /> 160 <Text style={[a.font_semi_bold, a.text_xl]}> 161 {isReposted ? ( 162 <Trans>Remove reskeet</Trans> 163 ) : ( 164 <Trans context="action">Reskeet</Trans> 165 )} 166 </Text> 167 </Button> 168 <Button 169 disabled={embeddingDisabled} 170 testID="quoteBtn" 171 style={[a.justify_start, a.px_md, a.gap_sm]} 172 label={ 173 embeddingDisabled 174 ? _(msg`Quote skeets disabled`) 175 : _(msg`Quote skeet`) 176 } 177 onPress={onPressQuote} 178 size="large" 179 variant="ghost" 180 color="primary"> 181 <QuoteIcon 182 size="lg" 183 fill={ 184 embeddingDisabled 185 ? t.atoms.text_contrast_low.color 186 : t.palette.primary_500 187 } 188 /> 189 <Text 190 style={[ 191 a.font_semi_bold, 192 a.text_xl, 193 embeddingDisabled && t.atoms.text_contrast_low, 194 ]}> 195 {embeddingDisabled ? ( 196 <Trans>Quote skeets disabled</Trans> 197 ) : ( 198 <Trans>Quote skeet</Trans> 199 )} 200 </Text> 201 </Button> 202 </View> 203 <Button 204 label={_(msg`Cancel quote skeet`)} 205 onPress={onPressClose} 206 size="large" 207 color="secondary"> 208 <ButtonText> 209 <Trans>Cancel</Trans> 210 </ButtonText> 211 </Button> 212 </View> 213 </Dialog.ScrollableInner> 214 ) 215} 216RepostButtonDialogInner = memo(RepostButtonDialogInner) 217export {RepostButtonDialogInner}