Bluesky app fork with some witchin' additions 馃挮
1import {memo, useCallback} from 'react'
2import {View} from 'react-native'
3import {msg, plural} from '@lingui/core/macro'
4import {useLingui} from '@lingui/react'
5import {Trans} from '@lingui/react/macro'
6
7import {useHaptics} from '#/lib/haptics'
8import {useRequireAuth} from '#/state/session'
9import {atoms as a, useTheme} from '#/alf'
10import {Button, ButtonText} from '#/components/Button'
11import * as Dialog from '#/components/Dialog'
12import {CloseQuote_Stroke2_Corner1_Rounded as QuoteIcon} from '#/components/icons/Quote'
13import {Repost_Stroke2_Corner3_Rounded as RepostIcon} from '#/components/icons/Repost'
14import {useFormatPostStatCount} from '#/components/PostControls/util'
15import {Text} from '#/components/Typography'
16import {
17 PostControlButton,
18 PostControlButtonIcon,
19 PostControlButtonText,
20} from './PostControlButton'
21
22interface Props {
23 isReposted: boolean
24 repostCount?: number
25 onRepost: () => void
26 onQuote: () => void
27 onLongPress?: () => void
28 big?: boolean
29 embeddingDisabled: boolean
30}
31
32let RepostButton = ({
33 isReposted,
34 repostCount,
35 onRepost,
36 onQuote,
37 onLongPress,
38 big,
39 embeddingDisabled,
40}: Props): React.ReactNode => {
41 const t = useTheme()
42 const {_} = useLingui()
43 const requireAuth = useRequireAuth()
44 const dialogControl = Dialog.useDialogControl()
45 const formatPostStatCount = useFormatPostStatCount()
46
47 const onPress = () => requireAuth(() => dialogControl.open())
48
49 const onDefaultLongPress = () =>
50 requireAuth(() => {
51 if (embeddingDisabled) {
52 dialogControl.open()
53 } else {
54 onQuote()
55 }
56 })
57
58 return (
59 <View>
60 <PostControlButton
61 testID="repostBtn"
62 active={isReposted}
63 activeColor={t.palette.positive_500}
64 big={big}
65 onPress={onPress}
66 onLongPress={onLongPress ?? onDefaultLongPress}
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 </View>
109 )
110}
111RepostButton = memo(RepostButton)
112export {RepostButton}
113
114export const RepostButtonDialogInner = memo(function 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 playHaptic = useHaptics()
128 const control = Dialog.useDialogContext()
129
130 const onPressRepost = useCallback(() => {
131 if (!isReposted) playHaptic()
132
133 control.close(() => {
134 onRepost()
135 })
136 }, [control, isReposted, onRepost, playHaptic])
137
138 const onPressQuote = useCallback(() => {
139 playHaptic()
140 control.close(() => {
141 onQuote()
142 })
143 }, [control, onQuote, playHaptic])
144
145 const onPressClose = useCallback(() => control.close(), [control])
146
147 return (
148 <Dialog.ScrollableInner label={_(msg`Repost or quote post`)}>
149 <View style={a.gap_xl}>
150 <View style={a.gap_xs}>
151 <Button
152 style={[a.justify_start, a.px_md, a.gap_sm]}
153 label={
154 isReposted
155 ? _(msg`Remove repost`)
156 : _(msg({message: `Repost`, context: 'action'}))
157 }
158 onPress={onPressRepost}
159 size="large"
160 variant="ghost"
161 color="primary">
162 <RepostIcon size="lg" fill={t.palette.primary_500} />
163 <Text style={[a.font_semi_bold, a.text_xl]}>
164 {isReposted ? (
165 <Trans>Remove repost</Trans>
166 ) : (
167 <Trans context="action">Repost</Trans>
168 )}
169 </Text>
170 </Button>
171 <Button
172 disabled={embeddingDisabled}
173 testID="quoteBtn"
174 style={[a.justify_start, a.px_md, a.gap_sm]}
175 label={
176 embeddingDisabled
177 ? _(msg`Quote posts disabled`)
178 : _(msg`Quote post`)
179 }
180 onPress={onPressQuote}
181 size="large"
182 variant="ghost"
183 color="primary">
184 <QuoteIcon
185 size="lg"
186 fill={
187 embeddingDisabled
188 ? t.atoms.text_contrast_low.color
189 : t.palette.primary_500
190 }
191 />
192 <Text
193 style={[
194 a.font_semi_bold,
195 a.text_xl,
196 embeddingDisabled && t.atoms.text_contrast_low,
197 ]}>
198 {embeddingDisabled ? (
199 <Trans>Quote posts disabled</Trans>
200 ) : (
201 <Trans>Quote post</Trans>
202 )}
203 </Text>
204 </Button>
205 </View>
206 <Button
207 label={_(msg`Cancel quote post`)}
208 onPress={onPressClose}
209 size="large"
210 color="secondary">
211 <ButtonText>
212 <Trans>Cancel</Trans>
213 </ButtonText>
214 </Button>
215 </View>
216 </Dialog.ScrollableInner>
217 )
218})