forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
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}