Create moderation inbox menu item and feature gate (#11552)
This commit is contained in:
@@ -60,11 +60,6 @@
|
|||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"src/Navigation.tsx": {
|
|
||||||
"typescript/no-floating-promises": {
|
|
||||||
"count": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"src/Splash.android.tsx": {
|
"src/Splash.android.tsx": {
|
||||||
"typescript/no-floating-promises": {
|
"typescript/no-floating-promises": {
|
||||||
"count": 1
|
"count": 1
|
||||||
@@ -790,20 +785,6 @@
|
|||||||
"count": 2
|
"count": 2
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"src/screens/Moderation/index.tsx": {
|
|
||||||
"typescript/no-explicit-any": {
|
|
||||||
"count": 2
|
|
||||||
},
|
|
||||||
"typescript/no-floating-promises": {
|
|
||||||
"count": 1
|
|
||||||
},
|
|
||||||
"typescript/no-misused-promises": {
|
|
||||||
"count": 1
|
|
||||||
},
|
|
||||||
"typescript/no-unsafe-member-access": {
|
|
||||||
"count": 2
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"src/screens/ModerationInteractionSettings/index.tsx": {
|
"src/screens/ModerationInteractionSettings/index.tsx": {
|
||||||
"typescript/no-explicit-any": {
|
"typescript/no-explicit-any": {
|
||||||
"count": 1
|
"count": 1
|
||||||
|
|||||||
+7
-1
@@ -87,6 +87,7 @@ import {MessagesJoinRequestsScreen} from '#/screens/Messages/JoinRequests'
|
|||||||
import {MessagesSettingsScreen} from '#/screens/Messages/Settings'
|
import {MessagesSettingsScreen} from '#/screens/Messages/Settings'
|
||||||
import {ModerationScreen} from '#/screens/Moderation'
|
import {ModerationScreen} from '#/screens/Moderation'
|
||||||
import {Screen as ModerationVerificationSettings} from '#/screens/Moderation/VerificationSettings'
|
import {Screen as ModerationVerificationSettings} from '#/screens/Moderation/VerificationSettings'
|
||||||
|
import {ModerationInboxScreen} from '#/screens/ModerationInbox'
|
||||||
import {Screen as ModerationInteractionSettings} from '#/screens/ModerationInteractionSettings'
|
import {Screen as ModerationInteractionSettings} from '#/screens/ModerationInteractionSettings'
|
||||||
import {NotificationsActivityListScreen} from '#/screens/Notifications/ActivityList'
|
import {NotificationsActivityListScreen} from '#/screens/Notifications/ActivityList'
|
||||||
import {PostLikedByScreen} from '#/screens/Post/PostLikedBy'
|
import {PostLikedByScreen} from '#/screens/Post/PostLikedBy'
|
||||||
@@ -178,6 +179,11 @@ function commonScreens(Stack: typeof Flat, unreadCountLabel?: string) {
|
|||||||
getComponent={() => ModerationScreen}
|
getComponent={() => ModerationScreen}
|
||||||
options={{title: title(msg`Moderation`), requireAuth: true}}
|
options={{title: title(msg`Moderation`), requireAuth: true}}
|
||||||
/>
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name="ModerationInbox"
|
||||||
|
getComponent={() => ModerationInboxScreen}
|
||||||
|
options={{title: title(msg`Moderation inbox`), requireAuth: true}}
|
||||||
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="ModerationModlists"
|
name="ModerationModlists"
|
||||||
getComponent={() => ModerationModlistsScreen}
|
getComponent={() => ModerationModlistsScreen}
|
||||||
@@ -919,7 +925,7 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
|
|||||||
// chat-removed-from-group, chat-join-request-rejected: the convo is
|
// chat-removed-from-group, chat-join-request-rejected: the convo is
|
||||||
// no longer accessible to the recipient, so just open the list.
|
// no longer accessible to the recipient, so just open the list.
|
||||||
// @ts-expect-error nested navigators aren't typed -sfn
|
// @ts-expect-error nested navigators aren't typed -sfn
|
||||||
navigate('MessagesTab', {screen: 'Messages'})
|
void navigate('MessagesTab', {screen: 'Messages'})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export enum Features {
|
|||||||
OnboardingInterestsRequiredEnable = 'onboarding:interests:required:enable',
|
OnboardingInterestsRequiredEnable = 'onboarding:interests:required:enable',
|
||||||
CanonicalPostNumberingEnable = 'canonical_post_numbering:enable',
|
CanonicalPostNumberingEnable = 'canonical_post_numbering:enable',
|
||||||
ContentVisibilitySettingsEnable = 'content_visibility_settings:enable',
|
ContentVisibilitySettingsEnable = 'content_visibility_settings:enable',
|
||||||
|
ModerationInboxEnable = 'moderation_inbox:enable',
|
||||||
|
|
||||||
// values
|
// values
|
||||||
TrendingDiscoverValues = 'trending_discover:values',
|
TrendingDiscoverValues = 'trending_discover:values',
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export type CommonNavigatorParams = {
|
|||||||
NotFound: undefined
|
NotFound: undefined
|
||||||
Lists: undefined
|
Lists: undefined
|
||||||
Moderation: undefined
|
Moderation: undefined
|
||||||
|
ModerationInbox: undefined
|
||||||
ModerationModlists: undefined
|
ModerationModlists: undefined
|
||||||
ModerationMutedAccounts: undefined
|
ModerationMutedAccounts: undefined
|
||||||
ModerationBlockedAccounts: undefined
|
ModerationBlockedAccounts: undefined
|
||||||
|
|||||||
+291
-281
File diff suppressed because it is too large
Load Diff
@@ -17,6 +17,7 @@ export const router = new Router<AllNavigatableRoutes>({
|
|||||||
Lists: '/lists',
|
Lists: '/lists',
|
||||||
// moderation
|
// moderation
|
||||||
Moderation: '/moderation',
|
Moderation: '/moderation',
|
||||||
|
ModerationInbox: '/moderation/inbox',
|
||||||
ModerationModlists: '/moderation/modlists',
|
ModerationModlists: '/moderation/modlists',
|
||||||
ModerationMutedAccounts: '/moderation/muted-accounts',
|
ModerationMutedAccounts: '/moderation/muted-accounts',
|
||||||
ModerationBlockedAccounts: '/moderation/blocked-accounts',
|
ModerationBlockedAccounts: '/moderation/blocked-accounts',
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import {Fragment, useCallback} from 'react'
|
import {Fragment, useCallback} from 'react'
|
||||||
import {Linking, View} from 'react-native'
|
import {Linking, View} from 'react-native'
|
||||||
import {LABELS} from '@bsky/sdk/moderation'
|
import {LABELS} from '@bsky/sdk/moderation'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {Trans, useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
|
||||||
|
|
||||||
import {getLabelingServiceTitle, isAppLabeler} from '#/lib/moderation'
|
import {getLabelingServiceTitle, isAppLabeler} from '#/lib/moderation'
|
||||||
import {
|
import {
|
||||||
@@ -35,6 +33,7 @@ import {type Props as SVGIconProps} from '#/components/icons/common'
|
|||||||
import {EditBig_Stroke2_Corner2_Rounded as EditBig} from '#/components/icons/EditBig'
|
import {EditBig_Stroke2_Corner2_Rounded as EditBig} from '#/components/icons/EditBig'
|
||||||
import {Filter_Stroke2_Corner0_Rounded as Filter} from '#/components/icons/Filter'
|
import {Filter_Stroke2_Corner0_Rounded as Filter} from '#/components/icons/Filter'
|
||||||
import {Group3_Stroke2_Corner0_Rounded as Group} from '#/components/icons/Group'
|
import {Group3_Stroke2_Corner0_Rounded as Group} from '#/components/icons/Group'
|
||||||
|
import {Inbox_Stroke2_Corner2_Rounded as Inbox} from '#/components/icons/Inbox'
|
||||||
import {Person_Stroke2_Corner0_Rounded as Person} from '#/components/icons/Person'
|
import {Person_Stroke2_Corner0_Rounded as Person} from '#/components/icons/Person'
|
||||||
import * as LabelingService from '#/components/LabelingServiceCard'
|
import * as LabelingService from '#/components/LabelingServiceCard'
|
||||||
import * as Layout from '#/components/Layout'
|
import * as Layout from '#/components/Layout'
|
||||||
@@ -45,8 +44,11 @@ import {GlobalLabelPreference} from '#/components/moderation/LabelPreference'
|
|||||||
import * as Toast from '#/components/Toast'
|
import * as Toast from '#/components/Toast'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
import {useAgeAssurance} from '#/ageAssurance'
|
import {useAgeAssurance} from '#/ageAssurance'
|
||||||
|
import {useAnalytics} from '#/analytics'
|
||||||
import {IS_IOS} from '#/env'
|
import {IS_IOS} from '#/env'
|
||||||
|
|
||||||
|
const UNREAD_NOTIFICATION_CAP = 100
|
||||||
|
|
||||||
function ErrorState({error}: {error: string}) {
|
function ErrorState({error}: {error: string}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
return (
|
return (
|
||||||
@@ -81,7 +83,7 @@ function ErrorState({error}: {error: string}) {
|
|||||||
export function ModerationScreen(
|
export function ModerationScreen(
|
||||||
_props: NativeStackScreenProps<CommonNavigatorParams, 'Moderation'>,
|
_props: NativeStackScreenProps<CommonNavigatorParams, 'Moderation'>,
|
||||||
) {
|
) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {
|
const {
|
||||||
isLoading: isPreferencesLoading,
|
isLoading: isPreferencesLoading,
|
||||||
error: preferencesError,
|
error: preferencesError,
|
||||||
@@ -109,7 +111,7 @@ export function ModerationScreen(
|
|||||||
<ErrorState
|
<ErrorState
|
||||||
error={
|
error={
|
||||||
preferencesError?.toString() ||
|
preferencesError?.toString() ||
|
||||||
_(msg`Something went wrong, please try again.`)
|
l`Something went wrong, please try again.`
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
@@ -123,12 +125,15 @@ export function ModerationScreen(
|
|||||||
function SubItem({
|
function SubItem({
|
||||||
title,
|
title,
|
||||||
icon: Icon,
|
icon: Icon,
|
||||||
|
badge,
|
||||||
style,
|
style,
|
||||||
}: ViewStyleProp & {
|
}: ViewStyleProp & {
|
||||||
title: string
|
title: string
|
||||||
icon: React.ComponentType<SVGIconProps>
|
icon: React.ComponentType<SVGIconProps>
|
||||||
|
badge?: number
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
|
const {t: l, i18n} = useLingui()
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
@@ -144,10 +149,38 @@ function SubItem({
|
|||||||
<Icon size="md" style={[t.atoms.text_contrast_medium]} />
|
<Icon size="md" style={[t.atoms.text_contrast_medium]} />
|
||||||
<Text style={[a.text_sm, a.font_semi_bold]}>{title}</Text>
|
<Text style={[a.text_sm, a.font_semi_bold]}>{title}</Text>
|
||||||
</View>
|
</View>
|
||||||
<ChevronRight
|
<View style={[a.flex_row, a.align_center, a.gap_md]}>
|
||||||
size="sm"
|
{badge ? (
|
||||||
style={[t.atoms.text_contrast_low, a.self_end, {paddingBottom: 2}]}
|
<View
|
||||||
/>
|
style={[
|
||||||
|
a.rounded_lg,
|
||||||
|
a.align_center,
|
||||||
|
a.justify_center,
|
||||||
|
a.px_sm,
|
||||||
|
{
|
||||||
|
minWidth: 24,
|
||||||
|
height: 24,
|
||||||
|
backgroundColor: t.palette.primary_500,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
a.text_xs,
|
||||||
|
a.font_semi_bold,
|
||||||
|
{color: t.palette.white, fontVariant: ['tabular-nums']},
|
||||||
|
]}>
|
||||||
|
{badge >= UNREAD_NOTIFICATION_CAP
|
||||||
|
? l({
|
||||||
|
message: `${i18n.number(UNREAD_NOTIFICATION_CAP - 1)}+`,
|
||||||
|
comment:
|
||||||
|
'Displayed when the number of notifications exceeds the cap – for example, 99+ notifications',
|
||||||
|
})
|
||||||
|
: i18n.number(badge)}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
) : null}
|
||||||
|
<ChevronRight size="sm" style={[t.atoms.text_contrast_low]} />
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -157,10 +190,14 @@ export function ModerationScreenInner({
|
|||||||
}: {
|
}: {
|
||||||
preferences: UsePreferencesQueryResponse
|
preferences: UsePreferencesQueryResponse
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {gtMobile} = useBreakpoints()
|
const {gtMobile} = useBreakpoints()
|
||||||
const {mutedWordsDialogControl} = useGlobalDialogsControlContext()
|
const {mutedWordsDialogControl} = useGlobalDialogsControlContext()
|
||||||
|
const ax = useAnalytics()
|
||||||
|
const isModerationInboxEnabled = ax.features.enabled(
|
||||||
|
ax.features.ModerationInboxEnable,
|
||||||
|
)
|
||||||
const {
|
const {
|
||||||
isLoading: isLabelersLoading,
|
isLoading: isLabelersLoading,
|
||||||
data: labelers,
|
data: labelers,
|
||||||
@@ -184,10 +221,11 @@ export function ModerationScreenInner({
|
|||||||
const handleCleanup = async () => {
|
const handleCleanup = async () => {
|
||||||
try {
|
try {
|
||||||
await removeLabelers({dids: unavailableDids})
|
await removeLabelers({dids: unavailableDids})
|
||||||
Toast.show(_(msg`Removed unavailable services`), {
|
Toast.show(l`Removed unavailable services`, {
|
||||||
type: 'success',
|
type: 'success',
|
||||||
})
|
})
|
||||||
} catch (e: any) {
|
} catch (error) {
|
||||||
|
const e = error as Error
|
||||||
logger.error('Failed to remove unavailable labelers', {
|
logger.error('Failed to remove unavailable labelers', {
|
||||||
safeMessage: e.message,
|
safeMessage: e.message,
|
||||||
})
|
})
|
||||||
@@ -214,7 +252,8 @@ export function ModerationScreenInner({
|
|||||||
await setAdultContentPref({
|
await setAdultContentPref({
|
||||||
enabled: selected,
|
enabled: selected,
|
||||||
})
|
})
|
||||||
} catch (e: any) {
|
} catch (error) {
|
||||||
|
const e = error as Error
|
||||||
logger.error(`Failed to set adult content pref`, {
|
logger.error(`Failed to set adult content pref`, {
|
||||||
message: e.message,
|
message: e.message,
|
||||||
})
|
})
|
||||||
@@ -234,7 +273,7 @@ export function ModerationScreenInner({
|
|||||||
your{' '}
|
your{' '}
|
||||||
<InlineLinkText
|
<InlineLinkText
|
||||||
to="/settings/account"
|
to="/settings/account"
|
||||||
label={_(msg`Go to account settings`)}>
|
label={l`Go to account settings`}>
|
||||||
account settings
|
account settings
|
||||||
</InlineLinkText>
|
</InlineLinkText>
|
||||||
.
|
.
|
||||||
@@ -242,7 +281,25 @@ export function ModerationScreenInner({
|
|||||||
</Admonition.Admonition>
|
</Admonition.Admonition>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
{isModerationInboxEnabled && (
|
||||||
|
<Link
|
||||||
|
label={l`View your moderation inbox`}
|
||||||
|
testID="moderationInboxBtn"
|
||||||
|
to="/moderation/inbox"
|
||||||
|
style={[a.mb_2xl, a.rounded_md, a.overflow_hidden]}>
|
||||||
|
{state => (
|
||||||
|
<SubItem
|
||||||
|
title={l`Moderation inbox`}
|
||||||
|
icon={Inbox}
|
||||||
|
badge={3} // TODO This is a placeholder value. -dsb
|
||||||
|
style={[
|
||||||
|
t.atoms.bg_contrast_25,
|
||||||
|
(state.hovered || state.pressed) && [t.atoms.bg_contrast_50],
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
a.text_md,
|
a.text_md,
|
||||||
@@ -252,7 +309,6 @@ export function ModerationScreenInner({
|
|||||||
]}>
|
]}>
|
||||||
<Trans>Moderation tools</Trans>
|
<Trans>Moderation tools</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
a.w_full,
|
a.w_full,
|
||||||
@@ -261,12 +317,12 @@ export function ModerationScreenInner({
|
|||||||
t.atoms.bg_contrast_25,
|
t.atoms.bg_contrast_25,
|
||||||
]}>
|
]}>
|
||||||
<Link
|
<Link
|
||||||
label={_(msg`View your default post interaction settings`)}
|
label={l`View your default post interaction settings`}
|
||||||
testID="interactionSettingsBtn"
|
testID="interactionSettingsBtn"
|
||||||
to="/moderation/interaction-settings">
|
to="/moderation/interaction-settings">
|
||||||
{state => (
|
{state => (
|
||||||
<SubItem
|
<SubItem
|
||||||
title={_(msg`Interaction settings`)}
|
title={l`Interaction settings`}
|
||||||
icon={EditBig}
|
icon={EditBig}
|
||||||
style={[
|
style={[
|
||||||
(state.hovered || state.pressed) && [t.atoms.bg_contrast_50],
|
(state.hovered || state.pressed) && [t.atoms.bg_contrast_50],
|
||||||
@@ -277,11 +333,11 @@ export function ModerationScreenInner({
|
|||||||
<Divider />
|
<Divider />
|
||||||
<Button
|
<Button
|
||||||
testID="mutedWordsBtn"
|
testID="mutedWordsBtn"
|
||||||
label={_(msg`Open muted words and tags settings`)}
|
label={l`Open muted words and tags settings`}
|
||||||
onPress={() => mutedWordsDialogControl.open()}>
|
onPress={() => mutedWordsDialogControl.open()}>
|
||||||
{state => (
|
{state => (
|
||||||
<SubItem
|
<SubItem
|
||||||
title={_(msg`Muted words & tags`)}
|
title={l`Muted words & tags`}
|
||||||
icon={Filter}
|
icon={Filter}
|
||||||
style={[
|
style={[
|
||||||
(state.hovered || state.pressed) && [t.atoms.bg_contrast_50],
|
(state.hovered || state.pressed) && [t.atoms.bg_contrast_50],
|
||||||
@@ -291,12 +347,12 @@ export function ModerationScreenInner({
|
|||||||
</Button>
|
</Button>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Link
|
<Link
|
||||||
label={_(msg`View your moderation lists`)}
|
label={l`View your moderation lists`}
|
||||||
testID="moderationlistsBtn"
|
testID="moderationlistsBtn"
|
||||||
to="/moderation/modlists">
|
to="/moderation/modlists">
|
||||||
{state => (
|
{state => (
|
||||||
<SubItem
|
<SubItem
|
||||||
title={_(msg`Moderation lists`)}
|
title={l`Moderation lists`}
|
||||||
icon={Group}
|
icon={Group}
|
||||||
style={[
|
style={[
|
||||||
(state.hovered || state.pressed) && [t.atoms.bg_contrast_50],
|
(state.hovered || state.pressed) && [t.atoms.bg_contrast_50],
|
||||||
@@ -306,12 +362,12 @@ export function ModerationScreenInner({
|
|||||||
</Link>
|
</Link>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Link
|
<Link
|
||||||
label={_(msg`View your muted accounts`)}
|
label={l`View your muted accounts`}
|
||||||
testID="mutedAccountsBtn"
|
testID="mutedAccountsBtn"
|
||||||
to="/moderation/muted-accounts">
|
to="/moderation/muted-accounts">
|
||||||
{state => (
|
{state => (
|
||||||
<SubItem
|
<SubItem
|
||||||
title={_(msg`Muted accounts`)}
|
title={l`Muted accounts`}
|
||||||
icon={Person}
|
icon={Person}
|
||||||
style={[
|
style={[
|
||||||
(state.hovered || state.pressed) && [t.atoms.bg_contrast_50],
|
(state.hovered || state.pressed) && [t.atoms.bg_contrast_50],
|
||||||
@@ -321,12 +377,12 @@ export function ModerationScreenInner({
|
|||||||
</Link>
|
</Link>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Link
|
<Link
|
||||||
label={_(msg`View your blocked accounts`)}
|
label={l`View your blocked accounts`}
|
||||||
testID="blockedAccountsBtn"
|
testID="blockedAccountsBtn"
|
||||||
to="/moderation/blocked-accounts">
|
to="/moderation/blocked-accounts">
|
||||||
{state => (
|
{state => (
|
||||||
<SubItem
|
<SubItem
|
||||||
title={_(msg`Blocked accounts`)}
|
title={l`Blocked accounts`}
|
||||||
icon={CircleBanSign}
|
icon={CircleBanSign}
|
||||||
style={[
|
style={[
|
||||||
(state.hovered || state.pressed) && [t.atoms.bg_contrast_50],
|
(state.hovered || state.pressed) && [t.atoms.bg_contrast_50],
|
||||||
@@ -336,12 +392,12 @@ export function ModerationScreenInner({
|
|||||||
</Link>
|
</Link>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Link
|
<Link
|
||||||
label={_(msg`Manage verification settings`)}
|
label={l`Manage verification settings`}
|
||||||
testID="verificationSettingsBtn"
|
testID="verificationSettingsBtn"
|
||||||
to="/moderation/verification-settings">
|
to="/moderation/verification-settings">
|
||||||
{state => (
|
{state => (
|
||||||
<SubItem
|
<SubItem
|
||||||
title={_(msg`Verification settings`)}
|
title={l`Verification settings`}
|
||||||
icon={CircleCheck}
|
icon={CircleCheck}
|
||||||
style={[
|
style={[
|
||||||
(state.hovered || state.pressed) && [t.atoms.bg_contrast_50],
|
(state.hovered || state.pressed) && [t.atoms.bg_contrast_50],
|
||||||
@@ -350,7 +406,6 @@ export function ModerationScreenInner({
|
|||||||
)}
|
)}
|
||||||
</Link>
|
</Link>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
a.pt_2xl,
|
a.pt_2xl,
|
||||||
@@ -361,11 +416,9 @@ export function ModerationScreenInner({
|
|||||||
]}>
|
]}>
|
||||||
<Trans>Content filters</Trans>
|
<Trans>Content filters</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<AgeAssuranceAdmonition style={[a.pb_md]}>
|
<AgeAssuranceAdmonition style={[a.pb_md]}>
|
||||||
{aaCopy.notice}
|
{aaCopy.notice}
|
||||||
</AgeAssuranceAdmonition>
|
</AgeAssuranceAdmonition>
|
||||||
|
|
||||||
<View style={[a.gap_md]}>
|
<View style={[a.gap_md]}>
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
@@ -389,11 +442,13 @@ export function ModerationScreenInner({
|
|||||||
<Trans>Enable adult content</Trans>
|
<Trans>Enable adult content</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
<Toggle.Item
|
<Toggle.Item
|
||||||
label={_(msg`Toggle to enable or disable adult content`)}
|
label={l`Toggle to enable or disable adult content`}
|
||||||
disabled={adultContentUIDisabled}
|
disabled={adultContentUIDisabled}
|
||||||
name="adultContent"
|
name="adultContent"
|
||||||
value={adultContentEnabled}
|
value={adultContentEnabled}
|
||||||
onChange={onToggleAdultContentEnabled}>
|
onChange={(selected: boolean) =>
|
||||||
|
void onToggleAdultContentEnabled(selected)
|
||||||
|
}>
|
||||||
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
|
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
|
||||||
<Text style={[t.atoms.text_contrast_medium]}>
|
<Text style={[t.atoms.text_contrast_medium]}>
|
||||||
{adultContentEnabled ? (
|
{adultContentEnabled ? (
|
||||||
@@ -412,11 +467,11 @@ export function ModerationScreenInner({
|
|||||||
<Trans>
|
<Trans>
|
||||||
Adult content can only be enabled via the Web at{' '}
|
Adult content can only be enabled via the Web at{' '}
|
||||||
<InlineLinkText
|
<InlineLinkText
|
||||||
label={_(msg`The Bluesky web application`)}
|
label={l`The Bluesky web application`}
|
||||||
to=""
|
to=""
|
||||||
onPress={evt => {
|
onPress={evt => {
|
||||||
evt.preventDefault()
|
evt.preventDefault()
|
||||||
Linking.openURL('https://bsky.app/')
|
void Linking.openURL('https://bsky.app/')
|
||||||
return false
|
return false
|
||||||
}}>
|
}}>
|
||||||
bsky.app
|
bsky.app
|
||||||
@@ -445,7 +500,6 @@ export function ModerationScreenInner({
|
|||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
a.text_md,
|
a.text_md,
|
||||||
@@ -456,7 +510,6 @@ export function ModerationScreenInner({
|
|||||||
]}>
|
]}>
|
||||||
<Trans>Advanced</Trans>
|
<Trans>Advanced</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{unavailableDids.length > 0 && (
|
{unavailableDids.length > 0 && (
|
||||||
<Admonition.Outer type="tip" style={[a.mb_md]}>
|
<Admonition.Outer type="tip" style={[a.mb_md]}>
|
||||||
<Admonition.Row>
|
<Admonition.Row>
|
||||||
@@ -470,7 +523,7 @@ export function ModerationScreenInner({
|
|||||||
</Admonition.Content>
|
</Admonition.Content>
|
||||||
<Admonition.Button
|
<Admonition.Button
|
||||||
color="primary_subtle"
|
color="primary_subtle"
|
||||||
label={_(msg`Remove unavailable moderation services`)}
|
label={l`Remove unavailable moderation services`}
|
||||||
onPress={handleCleanup}
|
onPress={handleCleanup}
|
||||||
disabled={isRemovingLabelers}>
|
disabled={isRemovingLabelers}>
|
||||||
<ButtonText>
|
<ButtonText>
|
||||||
@@ -481,7 +534,6 @@ export function ModerationScreenInner({
|
|||||||
</Admonition.Row>
|
</Admonition.Row>
|
||||||
</Admonition.Outer>
|
</Admonition.Outer>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isLabelersLoading ? (
|
{isLabelersLoading ? (
|
||||||
<View style={[a.w_full, a.align_center, a.p_lg]}>
|
<View style={[a.w_full, a.align_center, a.p_lg]}>
|
||||||
<Loader size="xl" />
|
<Loader size="xl" />
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
|
import {NotFoundScreen} from '#/view/screens/NotFound'
|
||||||
|
import * as Layout from '#/components/Layout'
|
||||||
|
import {useAnalytics} from '#/analytics'
|
||||||
|
|
||||||
|
export function ModerationInboxScreen() {
|
||||||
|
const ax = useAnalytics()
|
||||||
|
const isEnabled = ax.features.enabled(ax.features.ModerationInboxEnable)
|
||||||
|
|
||||||
|
if (!isEnabled) {
|
||||||
|
return <NotFoundScreen />
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout.Screen testID="moderationInboxScreen">
|
||||||
|
<Layout.Header.Outer>
|
||||||
|
<Layout.Header.BackButton />
|
||||||
|
<Layout.Header.Content>
|
||||||
|
<Layout.Header.TitleText>
|
||||||
|
<Trans>Moderation inbox</Trans>
|
||||||
|
</Layout.Header.TitleText>
|
||||||
|
</Layout.Header.Content>
|
||||||
|
<Layout.Header.Slot />
|
||||||
|
</Layout.Header.Outer>
|
||||||
|
</Layout.Screen>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user