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