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