Rework: create a new moderation screen and move everything related under it

This commit is contained in:
Paul Frazee
2023-05-05 13:32:02 -05:00
parent caf289d1d5
commit 32a0d75a23
13 changed files with 195 additions and 87 deletions
+4 -3
View File
@@ -100,11 +100,12 @@ func serve(cctx *cli.Context) error {
// generic routes // generic routes
e.GET("/search", server.WebGeneric) e.GET("/search", server.WebGeneric)
e.GET("/notifications", server.WebGeneric) e.GET("/notifications", server.WebGeneric)
e.GET("/lists", server.WebGeneric) e.GET("/moderation", server.WebGeneric)
e.GET("/moderation/mute-lists", server.WebGeneric)
e.GET("/moderation/muted-accounts", server.WebGeneric)
e.GET("/moderation/blocked-accounts", server.WebGeneric)
e.GET("/settings", server.WebGeneric) e.GET("/settings", server.WebGeneric)
e.GET("/settings/app-passwords", server.WebGeneric) e.GET("/settings/app-passwords", server.WebGeneric)
e.GET("/settings/muted-accounts", server.WebGeneric)
e.GET("/settings/blocked-accounts", server.WebGeneric)
e.GET("/sys/debug", server.WebGeneric) e.GET("/sys/debug", server.WebGeneric)
e.GET("/sys/log", server.WebGeneric) e.GET("/sys/log", server.WebGeneric)
e.GET("/support", server.WebGeneric) e.GET("/support", server.WebGeneric)
+17 -6
View File
@@ -33,7 +33,8 @@ import {useStores} from './state'
import {HomeScreen} from './view/screens/Home' import {HomeScreen} from './view/screens/Home'
import {SearchScreen} from './view/screens/Search' import {SearchScreen} from './view/screens/Search'
import {NotificationsScreen} from './view/screens/Notifications' import {NotificationsScreen} from './view/screens/Notifications'
import {ListsScreen} from './view/screens/Lists' import {ModerationScreen} from './view/screens/Moderation'
import {ModerationMuteListsScreen} from './view/screens/ModerationMuteLists'
import {NotFoundScreen} from './view/screens/NotFound' import {NotFoundScreen} from './view/screens/NotFound'
import {SettingsScreen} from './view/screens/Settings' import {SettingsScreen} from './view/screens/Settings'
import {ProfileScreen} from './view/screens/Profile' import {ProfileScreen} from './view/screens/Profile'
@@ -51,8 +52,8 @@ import {TermsOfServiceScreen} from './view/screens/TermsOfService'
import {CommunityGuidelinesScreen} from './view/screens/CommunityGuidelines' import {CommunityGuidelinesScreen} from './view/screens/CommunityGuidelines'
import {CopyrightPolicyScreen} from './view/screens/CopyrightPolicy' import {CopyrightPolicyScreen} from './view/screens/CopyrightPolicy'
import {AppPasswords} from 'view/screens/AppPasswords' import {AppPasswords} from 'view/screens/AppPasswords'
import {MutedAccounts} from 'view/screens/MutedAccounts' import {ModerationMutedAccounts} from 'view/screens/ModerationMutedAccounts'
import {BlockedAccounts} from 'view/screens/BlockedAccounts' import {ModerationBlockedAccounts} from 'view/screens/ModerationBlockedAccounts'
import {getRoutingInstrumentation} from 'lib/sentry' import {getRoutingInstrumentation} from 'lib/sentry'
const navigationRef = createNavigationContainerRef<AllNavigatorParams>() const navigationRef = createNavigationContainerRef<AllNavigatorParams>()
@@ -72,7 +73,19 @@ function commonScreens(Stack: typeof HomeTab) {
return ( return (
<> <>
<Stack.Screen name="NotFound" component={NotFoundScreen} /> <Stack.Screen name="NotFound" component={NotFoundScreen} />
<Stack.Screen name="Lists" component={ListsScreen} /> <Stack.Screen name="Moderation" component={ModerationScreen} />
<Stack.Screen
name="ModerationMuteLists"
component={ModerationMuteListsScreen}
/>
<Stack.Screen
name="ModerationMutedAccounts"
component={ModerationMutedAccounts}
/>
<Stack.Screen
name="ModerationBlockedAccounts"
component={ModerationBlockedAccounts}
/>
<Stack.Screen name="Settings" component={SettingsScreen} /> <Stack.Screen name="Settings" component={SettingsScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} /> <Stack.Screen name="Profile" component={ProfileScreen} />
<Stack.Screen <Stack.Screen
@@ -95,8 +108,6 @@ function commonScreens(Stack: typeof HomeTab) {
/> />
<Stack.Screen name="CopyrightPolicy" component={CopyrightPolicyScreen} /> <Stack.Screen name="CopyrightPolicy" component={CopyrightPolicyScreen} />
<Stack.Screen name="AppPasswords" component={AppPasswords} /> <Stack.Screen name="AppPasswords" component={AppPasswords} />
<Stack.Screen name="MutedAccounts" component={MutedAccounts} />
<Stack.Screen name="BlockedAccounts" component={BlockedAccounts} />
</> </>
) )
} }
+4 -3
View File
@@ -5,7 +5,10 @@ export type {NativeStackScreenProps} from '@react-navigation/native-stack'
export type CommonNavigatorParams = { export type CommonNavigatorParams = {
NotFound: undefined NotFound: undefined
Lists: undefined Moderation: undefined
ModerationMuteLists: undefined
ModerationMutedAccounts: undefined
ModerationBlockedAccounts: undefined
Settings: undefined Settings: undefined
Profile: {name: string; hideBackButton?: boolean} Profile: {name: string; hideBackButton?: boolean}
ProfileFollowers: {name: string} ProfileFollowers: {name: string}
@@ -22,8 +25,6 @@ export type CommonNavigatorParams = {
CommunityGuidelines: undefined CommunityGuidelines: undefined
CopyrightPolicy: undefined CopyrightPolicy: undefined
AppPasswords: undefined AppPasswords: undefined
MutedAccounts: undefined
BlockedAccounts: undefined
} }
export type BottomTabNavigatorParams = CommonNavigatorParams & { export type BottomTabNavigatorParams = CommonNavigatorParams & {
+4 -3
View File
@@ -4,8 +4,11 @@ export const router = new Router({
Home: '/', Home: '/',
Search: '/search', Search: '/search',
Notifications: '/notifications', Notifications: '/notifications',
Lists: '/lists',
Settings: '/settings', Settings: '/settings',
Moderation: '/moderation',
ModerationMuteLists: '/moderation/mute-lists',
ModerationMutedAccounts: '/moderation/muted-accounts',
ModerationBlockedAccounts: '/moderation/blocked-accounts',
Profile: '/profile/:name', Profile: '/profile/:name',
ProfileFollowers: '/profile/:name/followers', ProfileFollowers: '/profile/:name/followers',
ProfileFollows: '/profile/:name/follows', ProfileFollows: '/profile/:name/follows',
@@ -16,8 +19,6 @@ export const router = new Router({
Debug: '/sys/debug', Debug: '/sys/debug',
Log: '/sys/log', Log: '/sys/log',
AppPasswords: '/settings/app-passwords', AppPasswords: '/settings/app-passwords',
MutedAccounts: '/settings/muted-accounts',
BlockedAccounts: '/settings/blocked-accounts',
Support: '/support', Support: '/support',
PrivacyPolicy: '/support/privacy', PrivacyPolicy: '/support/privacy',
TermsOfService: '/support/tos', TermsOfService: '/support/tos',
@@ -28,7 +28,8 @@ export function SubscribedMutelistsEmptyState() {
</View> </View>
<Text type="xl-medium" style={[s.textCenter, pal.text]}> <Text type="xl-medium" style={[s.textCenter, pal.text]}>
You can subscribe to mutelists to automatically mute all of the users You can subscribe to mutelists to automatically mute all of the users
they include. Mutelists are public but your subscription is not. they include. Mutelists are public but your subscription to a mutelist
is private.
</Text> </Text>
<View style={styles.btns}> <View style={styles.btns}>
<Button type="inverted" style={styles.btn} onPress={onPressCreateList}> <Button type="inverted" style={styles.btn} onPress={onPressCreateList}>
+8
View File
@@ -37,6 +37,8 @@ import {faEye} from '@fortawesome/free-solid-svg-icons/faEye'
import {faEyeSlash as farEyeSlash} from '@fortawesome/free-regular-svg-icons/faEyeSlash' import {faEyeSlash as farEyeSlash} from '@fortawesome/free-regular-svg-icons/faEyeSlash'
import {faGear} from '@fortawesome/free-solid-svg-icons/faGear' import {faGear} from '@fortawesome/free-solid-svg-icons/faGear'
import {faGlobe} from '@fortawesome/free-solid-svg-icons/faGlobe' import {faGlobe} from '@fortawesome/free-solid-svg-icons/faGlobe'
import {faHand} from '@fortawesome/free-solid-svg-icons/faHand'
import {faHand as farHand} from '@fortawesome/free-regular-svg-icons/faHand'
import {faHeart} from '@fortawesome/free-regular-svg-icons/faHeart' import {faHeart} from '@fortawesome/free-regular-svg-icons/faHeart'
import {faHeart as fasHeart} from '@fortawesome/free-solid-svg-icons/faHeart' import {faHeart as fasHeart} from '@fortawesome/free-solid-svg-icons/faHeart'
import {faHouse} from '@fortawesome/free-solid-svg-icons/faHouse' import {faHouse} from '@fortawesome/free-solid-svg-icons/faHouse'
@@ -67,8 +69,10 @@ import {faRss} from '@fortawesome/free-solid-svg-icons/faRss'
import {faUser} from '@fortawesome/free-regular-svg-icons/faUser' import {faUser} from '@fortawesome/free-regular-svg-icons/faUser'
import {faUsers} from '@fortawesome/free-solid-svg-icons/faUsers' import {faUsers} from '@fortawesome/free-solid-svg-icons/faUsers'
import {faUserCheck} from '@fortawesome/free-solid-svg-icons/faUserCheck' import {faUserCheck} from '@fortawesome/free-solid-svg-icons/faUserCheck'
import {faUserSlash} from '@fortawesome/free-solid-svg-icons/faUserSlash'
import {faUserPlus} from '@fortawesome/free-solid-svg-icons/faUserPlus' import {faUserPlus} from '@fortawesome/free-solid-svg-icons/faUserPlus'
import {faUserXmark} from '@fortawesome/free-solid-svg-icons/faUserXmark' import {faUserXmark} from '@fortawesome/free-solid-svg-icons/faUserXmark'
import {faUsersSlash} from '@fortawesome/free-solid-svg-icons/faUsersSlash'
import {faTicket} from '@fortawesome/free-solid-svg-icons/faTicket' import {faTicket} from '@fortawesome/free-solid-svg-icons/faTicket'
import {faTrashCan} from '@fortawesome/free-regular-svg-icons/faTrashCan' import {faTrashCan} from '@fortawesome/free-regular-svg-icons/faTrashCan'
import {faX} from '@fortawesome/free-solid-svg-icons/faX' import {faX} from '@fortawesome/free-solid-svg-icons/faX'
@@ -115,6 +119,8 @@ export function setup() {
farEyeSlash, farEyeSlash,
faGear, faGear,
faGlobe, faGlobe,
faHand,
farHand,
faHeart, faHeart,
fasHeart, fasHeart,
faHouse, faHouse,
@@ -145,8 +151,10 @@ export function setup() {
faUser, faUser,
faUsers, faUsers,
faUserCheck, faUserCheck,
faUserSlash,
faUserPlus, faUserPlus,
faUserXmark, faUserXmark,
faUsersSlash,
faTicket, faTicket,
faTrashCan, faTrashCan,
faX, faX,
+128
View File
@@ -0,0 +1,128 @@
import React from 'react'
import {StyleSheet, TouchableOpacity, View} from 'react-native'
import {useFocusEffect} from '@react-navigation/native'
import {
FontAwesomeIcon,
FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
import {observer} from 'mobx-react-lite'
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
import {withAuthRequired} from 'view/com/auth/withAuthRequired'
import {useStores} from 'state/index'
import {s} from 'lib/styles'
import {CenteredView, ScrollView} from '../com/util/Views'
import {ViewHeader} from '../com/util/ViewHeader'
import {Link} from '../com/util/Link'
import {Text} from '../com/util/text/Text'
import {usePalette} from 'lib/hooks/usePalette'
import {useAnalytics} from 'lib/analytics'
import {isDesktopWeb} from 'platform/detection'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Moderation'>
export const ModerationScreen = withAuthRequired(
observer(function Moderation({}: Props) {
const pal = usePalette('default')
const store = useStores()
const {screen, track} = useAnalytics()
useFocusEffect(
React.useCallback(() => {
screen('Moderation')
store.shell.setMinimalShellMode(false)
}, [screen, store]),
)
const onPressContentFiltering = React.useCallback(() => {
track('Moderation:ContentfilteringButtonClicked')
store.shell.openModal({name: 'content-filtering-settings'})
}, [track, store])
return (
<View
style={[s.hContentRegion, !isDesktopWeb && pal.viewLight]}
testID="moderationScreen">
<ViewHeader title="Moderation" showOnDesktop />
<View style={styles.spacer} />
<TouchableOpacity
testID="contentFilteringBtn"
style={[styles.linkCard, pal.view]}
onPress={onPressContentFiltering}
accessibilityHint="Content filtering"
accessibilityLabel="Opens configurable content filtering settings">
<View style={[styles.iconContainer, pal.btn]}>
<FontAwesomeIcon
icon="eye"
style={pal.text as FontAwesomeIconStyle}
/>
</View>
<Text type="lg" style={pal.text}>
Content filtering
</Text>
</TouchableOpacity>
<Link
testID="mutelistsBtn"
style={[styles.linkCard, pal.view]}
href="/moderation/mute-lists">
<View style={[styles.iconContainer, pal.btn]}>
<FontAwesomeIcon
icon="users-slash"
style={pal.text as FontAwesomeIconStyle}
/>
</View>
<Text type="lg" style={pal.text}>
Mutelist subscriptions
</Text>
</Link>
<Link
testID="mutedAccountsBtn"
style={[styles.linkCard, pal.view]}
href="/moderation/muted-accounts">
<View style={[styles.iconContainer, pal.btn]}>
<FontAwesomeIcon
icon="user-slash"
style={pal.text as FontAwesomeIconStyle}
/>
</View>
<Text type="lg" style={pal.text}>
Muted accounts
</Text>
</Link>
<Link
testID="blockedAccountsBtn"
style={[styles.linkCard, pal.view]}
href="/moderation/blocked-accounts">
<View style={[styles.iconContainer, pal.btn]}>
<FontAwesomeIcon
icon="ban"
style={pal.text as FontAwesomeIconStyle}
/>
</View>
<Text type="lg" style={pal.text}>
Blocked accounts
</Text>
</Link>
</View>
)
}),
)
const styles = StyleSheet.create({
spacer: {
height: 6,
},
linkCard: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 12,
paddingHorizontal: 18,
marginBottom: 1,
},
iconContainer: {
alignItems: 'center',
justifyContent: 'center',
width: 40,
height: 40,
borderRadius: 30,
marginRight: 12,
},
})
@@ -22,8 +22,11 @@ import {ViewHeader} from '../com/util/ViewHeader'
import {CenteredView} from 'view/com/util/Views' import {CenteredView} from 'view/com/util/Views'
import {ProfileCard} from 'view/com/profile/ProfileCard' import {ProfileCard} from 'view/com/profile/ProfileCard'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'BlockedAccounts'> type Props = NativeStackScreenProps<
export const BlockedAccounts = withAuthRequired( CommonNavigatorParams,
'ModerationBlockedAccounts'
>
export const ModerationBlockedAccounts = withAuthRequired(
observer(({}: Props) => { observer(({}: Props) => {
const pal = usePalette('default') const pal = usePalette('default')
const store = useStores() const store = useStores()
@@ -11,8 +11,11 @@ import {ListsListModel} from 'state/models/lists/lists-list'
import {ListsList} from 'view/com/lists/ListsList' import {ListsList} from 'view/com/lists/ListsList'
import {usePalette} from 'lib/hooks/usePalette' import {usePalette} from 'lib/hooks/usePalette'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Lists'> type Props = NativeStackScreenProps<
export const ListsScreen = withAuthRequired(({route}: Props) => { CommonNavigatorParams,
'ModerationMuteLists'
>
export const ModerationMuteListsScreen = withAuthRequired(({route}: Props) => {
const pal = usePalette('default') const pal = usePalette('default')
const store = useStores() const store = useStores()
const [selectedPage, setSelectedPage] = React.useState(0) const [selectedPage, setSelectedPage] = React.useState(0)
@@ -22,8 +22,11 @@ import {ViewHeader} from '../com/util/ViewHeader'
import {CenteredView} from 'view/com/util/Views' import {CenteredView} from 'view/com/util/Views'
import {ProfileCard} from 'view/com/profile/ProfileCard' import {ProfileCard} from 'view/com/profile/ProfileCard'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'MutedAccounts'> type Props = NativeStackScreenProps<
export const MutedAccounts = withAuthRequired( CommonNavigatorParams,
'ModerationMutedAccounts'
>
export const ModerationMutedAccounts = withAuthRequired(
observer(({}: Props) => { observer(({}: Props) => {
const pal = usePalette('default') const pal = usePalette('default')
const store = useStores() const store = useStores()
+2 -53
View File
@@ -126,11 +126,6 @@ export const SettingsScreen = withAuthRequired(
store.shell.openModal({name: 'invite-codes'}) store.shell.openModal({name: 'invite-codes'})
}, [track, store]) }, [track, store])
const onPressContentFiltering = React.useCallback(() => {
track('Settings:ContentfilteringButtonClicked')
store.shell.openModal({name: 'content-filtering-settings'})
}, [track, store])
const onPressContentLanguages = React.useCallback(() => { const onPressContentLanguages = React.useCallback(() => {
track('Settings:ContentlanguagesButtonClicked') track('Settings:ContentlanguagesButtonClicked')
store.shell.openModal({name: 'content-languages-settings'}) store.shell.openModal({name: 'content-languages-settings'})
@@ -234,7 +229,9 @@ export const SettingsScreen = withAuthRequired(
Add account Add account
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
<View style={styles.spacer20} /> <View style={styles.spacer20} />
<Text type="xl-bold" style={[pal.text, styles.heading]}> <Text type="xl-bold" style={[pal.text, styles.heading]}>
Invite a friend Invite a friend
</Text> </Text>
@@ -269,54 +266,6 @@ export const SettingsScreen = withAuthRequired(
<View style={styles.spacer20} /> <View style={styles.spacer20} />
<Text type="xl-bold" style={[pal.text, styles.heading]}>
Moderation
</Text>
<TouchableOpacity
testID="contentFilteringBtn"
style={[styles.linkCard, pal.view, isSwitching && styles.dimmed]}
onPress={isSwitching ? undefined : onPressContentFiltering}
accessibilityHint="Content moderation"
accessibilityLabel="Opens configurable content moderation settings">
<View style={[styles.iconContainer, pal.btn]}>
<FontAwesomeIcon
icon="eye"
style={pal.text as FontAwesomeIconStyle}
/>
</View>
<Text type="lg" style={pal.text}>
Content moderation
</Text>
</TouchableOpacity>
<Link
testID="mutedAccountsBtn"
style={[styles.linkCard, pal.view, isSwitching && styles.dimmed]}
href="/settings/muted-accounts">
<View style={[styles.iconContainer, pal.btn]}>
<FontAwesomeIcon
icon={['far', 'eye-slash']}
style={pal.text as FontAwesomeIconStyle}
/>
</View>
<Text type="lg" style={pal.text}>
Muted accounts
</Text>
</Link>
<Link
testID="blockedAccountsBtn"
style={[styles.linkCard, pal.view, isSwitching && styles.dimmed]}
href="/settings/blocked-accounts">
<View style={[styles.iconContainer, pal.btn]}>
<FontAwesomeIcon
icon="ban"
style={pal.text as FontAwesomeIconStyle}
/>
</View>
<Text type="lg" style={pal.text}>
Blocked accounts
</Text>
</Link>
<View style={styles.spacer20} />
<Text type="xl-bold" style={[pal.text, styles.heading]}> <Text type="xl-bold" style={[pal.text, styles.heading]}>
Advanced Advanced
</Text> </Text>
+7 -8
View File
@@ -94,9 +94,9 @@ export const DrawerContent = observer(() => {
onPressTab('MyProfile') onPressTab('MyProfile')
}, [onPressTab]) }, [onPressTab])
const onPressLists = React.useCallback(() => { const onPressModeration = React.useCallback(() => {
track('Menu:ItemClicked', {url: 'Lists'}) track('Menu:ItemClicked', {url: 'Moderation'})
navigation.navigate('Lists') navigation.navigate('Moderation')
store.shell.closeDrawer() store.shell.closeDrawer()
}, [navigation, track, store.shell]) }, [navigation, track, store.shell])
@@ -227,18 +227,17 @@ export const DrawerContent = observer(() => {
onPress={onPressNotifications} onPress={onPressNotifications}
/> />
<MenuItem <MenuItem
href="/lists"
icon={ icon={
<FontAwesomeIcon <FontAwesomeIcon
icon="list-ul" icon={['far', 'hand']}
style={pal.text as FontAwesomeIconStyle} style={pal.text as FontAwesomeIconStyle}
size={20} size={20}
/> />
} }
label="Lists" label="Moderation"
accessibilityLabel="Lists" accessibilityLabel="Moderation"
accessibilityHint="" accessibilityHint=""
onPress={onPressLists} onPress={onPressModeration}
/> />
<MenuItem <MenuItem
icon={ icon={
+4 -4
View File
@@ -202,22 +202,22 @@ export const DesktopLeftNav = observer(function DesktopLeftNav() {
label="Notifications" label="Notifications"
/> />
<NavItem <NavItem
href="/lists" href="/moderation"
icon={ icon={
<FontAwesomeIcon <FontAwesomeIcon
icon="list-ul" icon={['far', 'hand']}
style={pal.text as FontAwesomeIconStyle} style={pal.text as FontAwesomeIconStyle}
size={20} size={20}
/> />
} }
iconFilled={ iconFilled={
<FontAwesomeIcon <FontAwesomeIcon
icon="list-ul" icon="hand"
style={pal.text as FontAwesomeIconStyle} style={pal.text as FontAwesomeIconStyle}
size={20} size={20}
/> />
} }
label="Lists" label="Moderation"
/> />
{store.session.hasSession && ( {store.session.hasSession && (
<NavItem <NavItem