forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import React from 'react'
2import {View} from 'react-native'
3import {ToolsOzoneReportDefs} from '@atproto/api'
4import {msg, Trans} from '@lingui/macro'
5import {useLingui} from '@lingui/react'
6import {useMutation} from '@tanstack/react-query'
7
8import {BLUESKY_MOD_SERVICE_HEADERS} from '#/lib/constants'
9import {useAgent, useSession} from '#/state/session'
10import * as Toast from '#/view/com/util/Toast'
11import {atoms as a, useBreakpoints, web} from '#/alf'
12import {AgeAssuranceBadge} from '#/components/ageAssurance/AgeAssuranceBadge'
13import {Button, ButtonIcon, ButtonText} from '#/components/Button'
14import * as Dialog from '#/components/Dialog'
15import {Loader} from '#/components/Loader'
16import {Text} from '#/components/Typography'
17import {logger} from '#/ageAssurance'
18import {useAnalytics} from '#/analytics'
19
20export function AgeAssuranceAppealDialog({
21 control,
22}: {
23 control: Dialog.DialogControlProps
24}) {
25 const {_} = useLingui()
26 return (
27 <Dialog.Outer control={control}>
28 <Dialog.Handle />
29 <Dialog.ScrollableInner
30 label={_(msg`Contact our moderation team`)}
31 style={[web({maxWidth: 400})]}>
32 <Inner control={control} />
33 <Dialog.Close />
34 </Dialog.ScrollableInner>
35 </Dialog.Outer>
36 )
37}
38
39function Inner({control}: {control: Dialog.DialogControlProps}) {
40 const {_} = useLingui()
41 const ax = useAnalytics()
42 const {currentAccount} = useSession()
43 const {gtPhone} = useBreakpoints()
44 const agent = useAgent()
45
46 const [details, setDetails] = React.useState('')
47 const isInvalid = details.length > 1000
48
49 const {mutate, isPending} = useMutation({
50 mutationFn: async () => {
51 ax.metric('ageAssurance:appealDialogSubmit', {})
52
53 await agent.createModerationReport(
54 {
55 reasonType: ToolsOzoneReportDefs.REASONAPPEAL,
56 subject: {
57 $type: 'com.atproto.admin.defs#repoRef',
58 did: currentAccount?.did,
59 },
60 reason: `AGE_ASSURANCE_INQUIRY: ` + details,
61 },
62 {
63 encoding: 'application/json',
64 headers: BLUESKY_MOD_SERVICE_HEADERS,
65 },
66 )
67 },
68 onError: err => {
69 logger.error('AgeAssuranceAppealDialog failed', {safeMessage: err})
70 Toast.show(
71 _(msg`Age assurance inquiry failed to send, please try again.`),
72 'xmark',
73 )
74 },
75 onSuccess: () => {
76 control.close()
77 Toast.show(
78 _(
79 msg({
80 message: 'Age assurance inquiry was submitted',
81 context: 'toast',
82 }),
83 ),
84 )
85 },
86 })
87
88 return (
89 <View>
90 <View style={[a.align_start]}>
91 <AgeAssuranceBadge />
92 </View>
93
94 <Text style={[a.text_2xl, a.font_bold, a.pt_md, a.leading_tight]}>
95 <Trans>Contact us</Trans>
96 </Text>
97
98 <Text style={[a.text_sm, a.pt_sm, a.leading_snug]}>
99 <Trans>
100 Please provide any additional details you feel moderators may need in
101 order to properly assess your Age Assurance status.
102 </Trans>
103 </Text>
104
105 <View style={[a.pt_md]}>
106 <Dialog.Input
107 multiline
108 isInvalid={isInvalid}
109 value={details}
110 onChangeText={details => {
111 setDetails(details)
112 }}
113 label={_(msg`Additional details (limit 1000 characters)`)}
114 numberOfLines={4}
115 onSubmitEditing={() => mutate()}
116 />
117 <View style={[a.pt_md, a.gap_sm, gtPhone && [a.flex_row_reverse]]}>
118 <Button
119 label={_(msg`Submit`)}
120 size="small"
121 variant="solid"
122 color="primary"
123 onPress={() => mutate()}>
124 <ButtonText>
125 <Trans>Submit</Trans>
126 </ButtonText>
127 {isPending && <ButtonIcon icon={Loader} position="right" />}
128 </Button>
129 <Button
130 label={_(msg`Cancel`)}
131 size="small"
132 variant="solid"
133 color="secondary"
134 onPress={() => control.close()}>
135 <ButtonText>
136 <Trans>Cancel</Trans>
137 </ButtonText>
138 </Button>
139 </View>
140 </View>
141 </View>
142 )
143}