[APP-1750] Add the ability to report livestreams (#9654)
* Add report dialog to LiveStatus dialog * Mr Worldwide™ * Bug fix bug fix * Copy update * Simplify parsing * Bump api sdk * update dev-env * Update yarn.lock * Bump api sdk * Refactor useActorStatus, add disablement and appeal dialog * Fix types * Guard against chat profile views * Go live (disabled) * Fix types * Status reports should only go to bsky * Ah yeah let's not override types --------- Co-authored-by: Samuel Newman <mozzius@protonmail.com>
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
import {useCallback, useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {type AppBskyActorDefs, ToolsOzoneReportDefs} from '@atproto/api'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useMutation} from '@tanstack/react-query'
|
||||
|
||||
import {BLUESKY_MOD_SERVICE_HEADERS} from '#/lib/constants'
|
||||
import {logger} from '#/logger'
|
||||
import {useAgent} from '#/state/session'
|
||||
import {atoms as a, web} from '#/alf'
|
||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import * as Toast from '#/components/Toast'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
||||
export function GoLiveDisabledDialog({
|
||||
control,
|
||||
status,
|
||||
}: {
|
||||
control: Dialog.DialogControlProps
|
||||
status: AppBskyActorDefs.StatusView
|
||||
}) {
|
||||
return (
|
||||
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
|
||||
<Dialog.Handle />
|
||||
<DialogInner control={control} status={status} />
|
||||
</Dialog.Outer>
|
||||
)
|
||||
}
|
||||
|
||||
export function DialogInner({
|
||||
control,
|
||||
status,
|
||||
}: {
|
||||
control: Dialog.DialogControlProps
|
||||
status: AppBskyActorDefs.StatusView
|
||||
}) {
|
||||
const {_} = useLingui()
|
||||
const agent = useAgent()
|
||||
const [details, setDetails] = useState('')
|
||||
|
||||
const {mutate, isPending} = useMutation({
|
||||
mutationFn: async () => {
|
||||
if (!agent.session?.did) {
|
||||
throw new Error('Not logged in')
|
||||
}
|
||||
if (!status.uri || !status.cid) {
|
||||
throw new Error('Status is missing uri or cid')
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
logger.info('Submitting go live appeal', {
|
||||
details,
|
||||
})
|
||||
} else {
|
||||
await agent.createModerationReport(
|
||||
{
|
||||
reasonType: ToolsOzoneReportDefs.REASONAPPEAL,
|
||||
subject: {
|
||||
$type: 'com.atproto.repo.strongRef',
|
||||
uri: status.uri,
|
||||
cid: status.cid,
|
||||
},
|
||||
reason: details,
|
||||
},
|
||||
{
|
||||
encoding: 'application/json',
|
||||
headers: BLUESKY_MOD_SERVICE_HEADERS,
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
onError: () => {
|
||||
Toast.show(_(msg`Failed to submit appeal, please try again.`), {
|
||||
type: 'error',
|
||||
})
|
||||
},
|
||||
onSuccess: () => {
|
||||
control.close()
|
||||
Toast.show(_(msg({message: 'Appeal submitted', context: 'toast'})), {
|
||||
type: 'success',
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
const onSubmit = useCallback(() => mutate(), [mutate])
|
||||
|
||||
return (
|
||||
<Dialog.ScrollableInner
|
||||
label={_(msg`Appeal livestream suspension`)}
|
||||
style={[web({maxWidth: 400})]}>
|
||||
<View style={[a.gap_lg]}>
|
||||
<View style={[a.gap_md]}>
|
||||
<Text
|
||||
style={[
|
||||
a.flex_1,
|
||||
a.text_2xl,
|
||||
a.font_semi_bold,
|
||||
a.leading_snug,
|
||||
a.pr_4xl,
|
||||
]}>
|
||||
<Trans>Going live is currently disabled for your account</Trans>
|
||||
</Text>
|
||||
<Text style={[a.text_md, a.leading_snug]}>
|
||||
<Trans>
|
||||
You are currently blocked from using the Go Live feature. To
|
||||
appeal this moderation decision, please submit the form below.
|
||||
</Trans>
|
||||
</Text>
|
||||
<Text style={[a.text_md, a.leading_snug]}>
|
||||
<Trans>
|
||||
This appeal will be sent to Bluesky's moderation service.
|
||||
</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<View style={[a.gap_md]}>
|
||||
<Dialog.Input
|
||||
label={_(msg`Text input field`)}
|
||||
placeholder={_(
|
||||
msg`Please explain why you think your Go Live access was incorrectly disabled.`,
|
||||
)}
|
||||
value={details}
|
||||
onChangeText={setDetails}
|
||||
autoFocus={true}
|
||||
numberOfLines={3}
|
||||
multiline
|
||||
maxLength={300}
|
||||
/>
|
||||
<Button
|
||||
testID="submitBtn"
|
||||
variant="solid"
|
||||
color="primary"
|
||||
size="large"
|
||||
onPress={onSubmit}
|
||||
label={_(msg`Submit`)}>
|
||||
<ButtonText>{_(msg`Submit`)}</ButtonText>
|
||||
{isPending && <ButtonIcon icon={Loader} />}
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
<Dialog.Close />
|
||||
</Dialog.ScrollableInner>
|
||||
)
|
||||
}
|
||||
@@ -17,6 +17,9 @@ import {unstableCacheProfileView} from '#/state/queries/profile'
|
||||
import {android, atoms as a, platform, tokens, useTheme, web} from '#/alf'
|
||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfoIcon} from '#/components/icons/CircleInfo'
|
||||
import {createStaticClick, SimpleInlineLinkText} from '#/components/Link'
|
||||
import {useGlobalReportDialogControl} from '#/components/moderation/ReportDialog'
|
||||
import * as ProfileCard from '#/components/ProfileCard'
|
||||
import {Text} from '#/components/Typography'
|
||||
import type * as bsky from '#/types/bsky'
|
||||
@@ -28,6 +31,7 @@ export function LiveStatusDialog({
|
||||
control,
|
||||
profile,
|
||||
embed,
|
||||
status,
|
||||
}: {
|
||||
control: Dialog.DialogControlProps
|
||||
profile: bsky.profile.AnyProfileView
|
||||
@@ -38,7 +42,12 @@ export function LiveStatusDialog({
|
||||
return (
|
||||
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
|
||||
<Dialog.Handle difference={!!embed.external.thumb} />
|
||||
<DialogInner profile={profile} embed={embed} navigation={navigation} />
|
||||
<DialogInner
|
||||
status={status}
|
||||
profile={profile}
|
||||
embed={embed}
|
||||
navigation={navigation}
|
||||
/>
|
||||
</Dialog.Outer>
|
||||
)
|
||||
}
|
||||
@@ -47,10 +56,12 @@ function DialogInner({
|
||||
profile,
|
||||
embed,
|
||||
navigation,
|
||||
status,
|
||||
}: {
|
||||
profile: bsky.profile.AnyProfileView
|
||||
embed: AppBskyEmbedExternal.View
|
||||
navigation: NavigationProp
|
||||
status: AppBskyActorDefs.StatusView
|
||||
}) {
|
||||
const {_} = useLingui()
|
||||
const control = Dialog.useDialogContext()
|
||||
@@ -69,6 +80,7 @@ function DialogInner({
|
||||
contentContainerStyle={[a.pt_0, a.px_0]}
|
||||
style={[web({maxWidth: 420}), a.overflow_hidden]}>
|
||||
<LiveStatus
|
||||
status={status}
|
||||
profile={profile}
|
||||
embed={embed}
|
||||
onPressOpenProfile={onPressOpenProfile}
|
||||
@@ -79,11 +91,13 @@ function DialogInner({
|
||||
}
|
||||
|
||||
export function LiveStatus({
|
||||
status,
|
||||
profile,
|
||||
embed,
|
||||
padding = 'xl',
|
||||
onPressOpenProfile,
|
||||
}: {
|
||||
status: AppBskyActorDefs.StatusView
|
||||
profile: bsky.profile.AnyProfileView
|
||||
embed: AppBskyEmbedExternal.View
|
||||
padding?: 'lg' | 'xl'
|
||||
@@ -94,6 +108,8 @@ export function LiveStatus({
|
||||
const queryClient = useQueryClient()
|
||||
const openLink = useOpenLink()
|
||||
const moderationOpts = useModerationOpts()
|
||||
const reportDialogControl = useGlobalReportDialogControl()
|
||||
const dialogContext = Dialog.useDialogContext()
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -205,15 +221,43 @@ export function LiveStatus({
|
||||
</Button>
|
||||
</ProfileCard.Header>
|
||||
)}
|
||||
<Text
|
||||
<View
|
||||
style={[
|
||||
a.w_full,
|
||||
a.text_center,
|
||||
t.atoms.text_contrast_low,
|
||||
a.text_sm,
|
||||
a.flex_row,
|
||||
a.align_center,
|
||||
a.justify_between,
|
||||
a.flex_1,
|
||||
a.pt_sm,
|
||||
]}>
|
||||
<Trans>Live feature is in beta testing</Trans>
|
||||
</Text>
|
||||
<View style={[a.flex_row, a.align_center, a.gap_xs, a.flex_1]}>
|
||||
<CircleInfoIcon size="sm" fill={t.atoms.text_contrast_low.color} />
|
||||
<Text style={[t.atoms.text_contrast_low, a.text_sm]}>
|
||||
<Trans>Live feature is in beta</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
{status && (
|
||||
<SimpleInlineLinkText
|
||||
label={_(msg`Report this livestream`)}
|
||||
{...createStaticClick(() => {
|
||||
function open() {
|
||||
reportDialogControl.open({
|
||||
subject: {
|
||||
...status,
|
||||
$type: 'app.bsky.actor.defs#statusView',
|
||||
},
|
||||
})
|
||||
}
|
||||
if (dialogContext.isWithinDialog) {
|
||||
dialogContext.close(open)
|
||||
} else {
|
||||
open()
|
||||
}
|
||||
})}
|
||||
style={[a.text_sm, a.underline, t.atoms.text_contrast_medium]}>
|
||||
<Trans>Report</Trans>
|
||||
</SimpleInlineLinkText>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user