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