Merge branch 'main' into custom-algos
This commit is contained in:
commit
7aa1d9010e
99 changed files with 4234 additions and 716 deletions
|
@ -140,8 +140,8 @@ function AppPasswordsHeader() {
|
|||
pal.text,
|
||||
isDesktopWeb && styles.descriptionDesktop,
|
||||
]}>
|
||||
These passwords can be used to log onto Bluesky in other apps without
|
||||
giving them full access to your account or your password.
|
||||
Use app passwords to login to other Bluesky clients without giving full
|
||||
access to your account or password.
|
||||
</Text>
|
||||
</>
|
||||
)
|
||||
|
@ -289,5 +289,6 @@ const styles = StyleSheet.create({
|
|||
|
||||
trashIcon: {
|
||||
color: 'red',
|
||||
minWidth: 16,
|
||||
},
|
||||
})
|
||||
|
|
|
@ -62,7 +62,7 @@ export const HomeScreen = withAuthRequired(
|
|||
setSelectedPage(index)
|
||||
store.shell.setIsDrawerSwipeDisabled(index > 0)
|
||||
},
|
||||
[store],
|
||||
[store, setSelectedPage],
|
||||
)
|
||||
|
||||
const onPressSelected = React.useCallback(() => {
|
||||
|
|
137
src/view/screens/Moderation.tsx
Normal file
137
src/view/screens/Moderation.tsx
Normal file
|
@ -0,0 +1,137 @@
|
|||
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} 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 (
|
||||
<CenteredView
|
||||
style={[
|
||||
s.hContentRegion,
|
||||
pal.border,
|
||||
isDesktopWeb ? styles.desktopContainer : pal.viewLight,
|
||||
]}
|
||||
testID="moderationScreen">
|
||||
<ViewHeader title="Moderation" showOnDesktop />
|
||||
<View style={styles.spacer} />
|
||||
<TouchableOpacity
|
||||
testID="contentFilteringBtn"
|
||||
style={[styles.linkCard, pal.view]}
|
||||
onPress={onPressContentFiltering}
|
||||
accessibilityRole="tab"
|
||||
accessibilityHint="Content filtering"
|
||||
accessibilityLabel="">
|
||||
<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}>
|
||||
Mute lists
|
||||
</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>
|
||||
</CenteredView>
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
desktopContainer: {
|
||||
borderLeftWidth: 1,
|
||||
borderRightWidth: 1,
|
||||
},
|
||||
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 {ProfileCard} from 'view/com/profile/ProfileCard'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'BlockedAccounts'>
|
||||
export const BlockedAccounts = withAuthRequired(
|
||||
type Props = NativeStackScreenProps<
|
||||
CommonNavigatorParams,
|
||||
'ModerationBlockedAccounts'
|
||||
>
|
||||
export const ModerationBlockedAccounts = withAuthRequired(
|
||||
observer(({}: Props) => {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
122
src/view/screens/ModerationMuteLists.tsx
Normal file
122
src/view/screens/ModerationMuteLists.tsx
Normal file
|
@ -0,0 +1,122 @@
|
|||
import React from 'react'
|
||||
import {StyleSheet} from 'react-native'
|
||||
import {useFocusEffect, useNavigation} from '@react-navigation/native'
|
||||
import {
|
||||
FontAwesomeIcon,
|
||||
FontAwesomeIconStyle,
|
||||
} from '@fortawesome/react-native-fontawesome'
|
||||
import {AtUri} from '@atproto/api'
|
||||
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
|
||||
import {withAuthRequired} from 'view/com/auth/withAuthRequired'
|
||||
import {EmptyStateWithButton} from 'view/com/util/EmptyStateWithButton'
|
||||
import {useStores} from 'state/index'
|
||||
import {ListsListModel} from 'state/models/lists/lists-list'
|
||||
import {ListsList} from 'view/com/lists/ListsList'
|
||||
import {Button} from 'view/com/util/forms/Button'
|
||||
import {NavigationProp} from 'lib/routes/types'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {CenteredView} from 'view/com/util/Views'
|
||||
import {ViewHeader} from 'view/com/util/ViewHeader'
|
||||
import {isDesktopWeb} from 'platform/detection'
|
||||
|
||||
type Props = NativeStackScreenProps<
|
||||
CommonNavigatorParams,
|
||||
'ModerationMuteLists'
|
||||
>
|
||||
export const ModerationMuteListsScreen = withAuthRequired(({}: Props) => {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
|
||||
const mutelists: ListsListModel = React.useMemo(
|
||||
() => new ListsListModel(store, 'my-modlists'),
|
||||
[store],
|
||||
)
|
||||
|
||||
useFocusEffect(
|
||||
React.useCallback(() => {
|
||||
store.shell.setMinimalShellMode(false)
|
||||
mutelists.refresh()
|
||||
}, [store, mutelists]),
|
||||
)
|
||||
|
||||
const onPressNewMuteList = React.useCallback(() => {
|
||||
store.shell.openModal({
|
||||
name: 'create-or-edit-mute-list',
|
||||
onSave: (uri: string) => {
|
||||
try {
|
||||
const urip = new AtUri(uri)
|
||||
navigation.navigate('ProfileList', {
|
||||
name: urip.hostname,
|
||||
rkey: urip.rkey,
|
||||
})
|
||||
} catch {}
|
||||
},
|
||||
})
|
||||
}, [store, navigation])
|
||||
|
||||
const renderEmptyState = React.useCallback(() => {
|
||||
return (
|
||||
<EmptyStateWithButton
|
||||
testID="emptyMuteLists"
|
||||
icon="users-slash"
|
||||
message="You can subscribe to mute lists to automatically mute all of the users they include. Mute lists are public but your subscription to a mute list is private."
|
||||
buttonLabel="New Mute List"
|
||||
onPress={onPressNewMuteList}
|
||||
/>
|
||||
)
|
||||
}, [onPressNewMuteList])
|
||||
|
||||
const renderHeaderButton = React.useCallback(
|
||||
() => (
|
||||
<Button
|
||||
type="primary-light"
|
||||
onPress={onPressNewMuteList}
|
||||
style={styles.createBtn}>
|
||||
<FontAwesomeIcon
|
||||
icon="plus"
|
||||
style={pal.link as FontAwesomeIconStyle}
|
||||
size={18}
|
||||
/>
|
||||
</Button>
|
||||
),
|
||||
[onPressNewMuteList, pal],
|
||||
)
|
||||
|
||||
return (
|
||||
<CenteredView
|
||||
style={[
|
||||
styles.container,
|
||||
isDesktopWeb && styles.containerDesktop,
|
||||
pal.view,
|
||||
pal.border,
|
||||
]}
|
||||
testID="moderationMutelistsScreen">
|
||||
<ViewHeader
|
||||
title="Mute Lists"
|
||||
showOnDesktop
|
||||
renderButton={renderHeaderButton}
|
||||
/>
|
||||
<ListsList
|
||||
listsList={mutelists}
|
||||
showAddBtns={isDesktopWeb}
|
||||
renderEmptyState={renderEmptyState}
|
||||
onPressCreateNew={onPressNewMuteList}
|
||||
/>
|
||||
</CenteredView>
|
||||
)
|
||||
})
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
paddingBottom: isDesktopWeb ? 0 : 100,
|
||||
},
|
||||
containerDesktop: {
|
||||
borderLeftWidth: 1,
|
||||
borderRightWidth: 1,
|
||||
},
|
||||
createBtn: {
|
||||
width: 40,
|
||||
},
|
||||
})
|
|
@ -22,8 +22,11 @@ import {ViewHeader} from '../com/util/ViewHeader'
|
|||
import {CenteredView} from 'view/com/util/Views'
|
||||
import {ProfileCard} from 'view/com/profile/ProfileCard'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'MutedAccounts'>
|
||||
export const MutedAccounts = withAuthRequired(
|
||||
type Props = NativeStackScreenProps<
|
||||
CommonNavigatorParams,
|
||||
'ModerationMutedAccounts'
|
||||
>
|
||||
export const ModerationMutedAccounts = withAuthRequired(
|
||||
observer(({}: Props) => {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
|
@ -7,12 +7,16 @@ import {withAuthRequired} from 'view/com/auth/withAuthRequired'
|
|||
import {ViewSelector} from '../com/util/ViewSelector'
|
||||
import {CenteredView} from '../com/util/Views'
|
||||
import {ScreenHider} from 'view/com/util/moderation/ScreenHider'
|
||||
import {ProfileUiModel} from 'state/models/ui/profile'
|
||||
import {ProfileUiModel, Sections} from 'state/models/ui/profile'
|
||||
import {useStores} from 'state/index'
|
||||
import {PostsFeedSliceModel} from 'state/models/feeds/posts'
|
||||
import {ProfileHeader} from '../com/profile/ProfileHeader'
|
||||
import {FeedSlice} from '../com/posts/FeedSlice'
|
||||
import {PostFeedLoadingPlaceholder} from '../com/util/LoadingPlaceholder'
|
||||
import {ListCard} from 'view/com/lists/ListCard'
|
||||
import {
|
||||
PostFeedLoadingPlaceholder,
|
||||
ProfileCardFeedLoadingPlaceholder,
|
||||
} from '../com/util/LoadingPlaceholder'
|
||||
import {ErrorScreen} from '../com/util/error/ErrorScreen'
|
||||
import {ErrorMessage} from '../com/util/error/ErrorMessage'
|
||||
import {EmptyState} from '../com/util/EmptyState'
|
||||
|
@ -23,6 +27,8 @@ import {useAnalytics} from 'lib/analytics'
|
|||
import {ComposeIcon2} from 'lib/icons'
|
||||
import AlgoItem from 'view/com/algos/AlgoItem'
|
||||
import {AlgoItemModel} from 'state/models/feeds/algo/algo-item'
|
||||
import {useSetTitle} from 'lib/hooks/useSetTitle'
|
||||
import {combinedDisplayName} from 'lib/strings/display-names'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Profile'>
|
||||
export const ProfileScreen = withAuthRequired(
|
||||
|
@ -39,6 +45,7 @@ export const ProfileScreen = withAuthRequired(
|
|||
() => new ProfileUiModel(store, {user: route.params.name}),
|
||||
[route.params.name, store],
|
||||
)
|
||||
useSetTitle(combinedDisplayName(uiState.profile))
|
||||
|
||||
useFocusEffect(
|
||||
React.useCallback(() => {
|
||||
|
@ -113,53 +120,87 @@ export const ProfileScreen = withAuthRequired(
|
|||
}, [uiState.showLoadingMoreFooter])
|
||||
const renderItem = React.useCallback(
|
||||
(item: any) => {
|
||||
if (item === ProfileUiModel.END_ITEM) {
|
||||
return <Text style={styles.endItem}>- end of feed -</Text>
|
||||
} else if (item === ProfileUiModel.LOADING_ITEM) {
|
||||
return <PostFeedLoadingPlaceholder />
|
||||
} else if (item._reactKey === '__error__') {
|
||||
if (uiState.feed.isBlocking) {
|
||||
if (uiState.selectedView === Sections.Lists) {
|
||||
if (item === ProfileUiModel.LOADING_ITEM) {
|
||||
return <ProfileCardFeedLoadingPlaceholder />
|
||||
} else if (item._reactKey === '__error__') {
|
||||
return (
|
||||
<View style={s.p5}>
|
||||
<ErrorMessage
|
||||
message={item.error}
|
||||
onPressTryAgain={onPressTryAgain}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
} else if (item === ProfileUiModel.EMPTY_ITEM) {
|
||||
return (
|
||||
<EmptyState
|
||||
icon="ban"
|
||||
message="Posts hidden"
|
||||
testID="listsEmpty"
|
||||
icon="list-ul"
|
||||
message="No lists yet!"
|
||||
style={styles.emptyState}
|
||||
/>
|
||||
)
|
||||
} else {
|
||||
return <ListCard testID={`list-${item.name}`} list={item} />
|
||||
}
|
||||
if (uiState.feed.isBlockedBy) {
|
||||
} else {
|
||||
if (item === ProfileUiModel.END_ITEM) {
|
||||
return <Text style={styles.endItem}>- end of feed -</Text>
|
||||
} else if (item === ProfileUiModel.LOADING_ITEM) {
|
||||
return <PostFeedLoadingPlaceholder />
|
||||
} else if (item._reactKey === '__error__') {
|
||||
if (uiState.feed.isBlocking) {
|
||||
return (
|
||||
<EmptyState
|
||||
icon="ban"
|
||||
message="Posts hidden"
|
||||
style={styles.emptyState}
|
||||
/>
|
||||
)
|
||||
}
|
||||
if (uiState.feed.isBlockedBy) {
|
||||
return (
|
||||
<EmptyState
|
||||
icon="ban"
|
||||
message="Posts hidden"
|
||||
style={styles.emptyState}
|
||||
/>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<View style={s.p5}>
|
||||
<ErrorMessage
|
||||
message={item.error}
|
||||
onPressTryAgain={onPressTryAgain}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
} else if (item === ProfileUiModel.EMPTY_ITEM) {
|
||||
return (
|
||||
<EmptyState
|
||||
icon="ban"
|
||||
message="Posts hidden"
|
||||
icon={['far', 'message']}
|
||||
message="No posts yet!"
|
||||
style={styles.emptyState}
|
||||
/>
|
||||
)
|
||||
} else if (item instanceof PostsFeedSliceModel) {
|
||||
return (
|
||||
<FeedSlice slice={item} ignoreMuteFor={uiState.profile.did} />
|
||||
)
|
||||
} else if (item instanceof AlgoItemModel) {
|
||||
return <AlgoItem item={item} />
|
||||
}
|
||||
return (
|
||||
<View style={s.p5}>
|
||||
<ErrorMessage
|
||||
message={item.error}
|
||||
onPressTryAgain={onPressTryAgain}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
} else if (item === ProfileUiModel.EMPTY_ITEM) {
|
||||
return (
|
||||
<EmptyState
|
||||
icon={['far', 'message']}
|
||||
message="No posts yet!"
|
||||
style={styles.emptyState}
|
||||
/>
|
||||
)
|
||||
} else if (item instanceof PostsFeedSliceModel) {
|
||||
return <FeedSlice slice={item} ignoreMuteFor={uiState.profile.did} />
|
||||
} else if (item instanceof AlgoItemModel) {
|
||||
return <AlgoItem item={item} />
|
||||
}
|
||||
return <View />
|
||||
},
|
||||
[onPressTryAgain, uiState],
|
||||
[
|
||||
onPressTryAgain,
|
||||
uiState.selectedView,
|
||||
uiState.profile.did,
|
||||
uiState.feed.isBlocking,
|
||||
uiState.feed.isBlockedBy,
|
||||
],
|
||||
)
|
||||
|
||||
return (
|
||||
|
|
177
src/view/screens/ProfileList.tsx
Normal file
177
src/view/screens/ProfileList.tsx
Normal file
|
@ -0,0 +1,177 @@
|
|||
import React from 'react'
|
||||
import {StyleSheet, View} from 'react-native'
|
||||
import {useFocusEffect} from '@react-navigation/native'
|
||||
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import {withAuthRequired} from 'view/com/auth/withAuthRequired'
|
||||
import {ViewHeader} from 'view/com/util/ViewHeader'
|
||||
import {CenteredView} from 'view/com/util/Views'
|
||||
import {ListItems} from 'view/com/lists/ListItems'
|
||||
import {EmptyState} from 'view/com/util/EmptyState'
|
||||
import {Button} from 'view/com/util/forms/Button'
|
||||
import * as Toast from 'view/com/util/Toast'
|
||||
import {ListModel} from 'state/models/content/list'
|
||||
import {useStores} from 'state/index'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useSetTitle} from 'lib/hooks/useSetTitle'
|
||||
import {NavigationProp} from 'lib/routes/types'
|
||||
import {isDesktopWeb} from 'platform/detection'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'ProfileList'>
|
||||
export const ProfileListScreen = withAuthRequired(
|
||||
observer(({route}: Props) => {
|
||||
const store = useStores()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const pal = usePalette('default')
|
||||
const {name, rkey} = route.params
|
||||
|
||||
const list: ListModel = React.useMemo(() => {
|
||||
const model = new ListModel(
|
||||
store,
|
||||
`at://${name}/app.bsky.graph.list/${rkey}`,
|
||||
)
|
||||
return model
|
||||
}, [store, name, rkey])
|
||||
useSetTitle(list.list?.name)
|
||||
|
||||
useFocusEffect(
|
||||
React.useCallback(() => {
|
||||
store.shell.setMinimalShellMode(false)
|
||||
list.loadMore(true)
|
||||
}, [store, list]),
|
||||
)
|
||||
|
||||
const onToggleSubscribed = React.useCallback(async () => {
|
||||
try {
|
||||
if (list.list?.viewer?.muted) {
|
||||
await list.unsubscribe()
|
||||
} else {
|
||||
await list.subscribe()
|
||||
}
|
||||
} catch (err) {
|
||||
Toast.show(
|
||||
'There was an an issue updating your subscription, please check your internet connection and try again.',
|
||||
)
|
||||
store.log.error('Failed up update subscription', {err})
|
||||
}
|
||||
}, [store, list])
|
||||
|
||||
const onPressEditList = React.useCallback(() => {
|
||||
store.shell.openModal({
|
||||
name: 'create-or-edit-mute-list',
|
||||
list,
|
||||
onSave() {
|
||||
list.refresh()
|
||||
},
|
||||
})
|
||||
}, [store, list])
|
||||
|
||||
const onPressDeleteList = React.useCallback(() => {
|
||||
store.shell.openModal({
|
||||
name: 'confirm',
|
||||
title: 'Delete List',
|
||||
message: 'Are you sure?',
|
||||
async onPressConfirm() {
|
||||
await list.delete()
|
||||
if (navigation.canGoBack()) {
|
||||
navigation.goBack()
|
||||
} else {
|
||||
navigation.navigate('Home')
|
||||
}
|
||||
},
|
||||
})
|
||||
}, [store, list, navigation])
|
||||
|
||||
const renderEmptyState = React.useCallback(() => {
|
||||
return <EmptyState icon="users-slash" message="This list is empty!" />
|
||||
}, [])
|
||||
|
||||
const renderHeaderBtn = React.useCallback(() => {
|
||||
return (
|
||||
<View style={styles.headerBtns}>
|
||||
{list?.isOwner && (
|
||||
<Button
|
||||
type="default"
|
||||
label="Delete List"
|
||||
testID="deleteListBtn"
|
||||
accessibilityLabel="Delete list"
|
||||
accessibilityHint=""
|
||||
onPress={onPressDeleteList}
|
||||
/>
|
||||
)}
|
||||
{list?.isOwner && (
|
||||
<Button
|
||||
type="default"
|
||||
label="Edit List"
|
||||
testID="editListBtn"
|
||||
accessibilityLabel="Edit list"
|
||||
accessibilityHint=""
|
||||
onPress={onPressEditList}
|
||||
/>
|
||||
)}
|
||||
{list.list?.viewer?.muted ? (
|
||||
<Button
|
||||
type="inverted"
|
||||
label="Unsubscribe"
|
||||
testID="unsubscribeListBtn"
|
||||
accessibilityLabel="Unsubscribe from list"
|
||||
accessibilityHint=""
|
||||
onPress={onToggleSubscribed}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
type="primary"
|
||||
label="Subscribe & Mute"
|
||||
testID="subscribeListBtn"
|
||||
accessibilityLabel="Subscribe to this list"
|
||||
accessibilityHint="Mutes the users included in this list"
|
||||
onPress={onToggleSubscribed}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
)
|
||||
}, [
|
||||
list?.isOwner,
|
||||
list.list?.viewer?.muted,
|
||||
onPressDeleteList,
|
||||
onPressEditList,
|
||||
onToggleSubscribed,
|
||||
])
|
||||
|
||||
return (
|
||||
<CenteredView
|
||||
style={[
|
||||
styles.container,
|
||||
isDesktopWeb && styles.containerDesktop,
|
||||
pal.view,
|
||||
pal.border,
|
||||
]}
|
||||
testID="moderationMutelistsScreen">
|
||||
<ViewHeader title="" renderButton={renderHeaderBtn} />
|
||||
<ListItems
|
||||
list={list}
|
||||
renderEmptyState={renderEmptyState}
|
||||
onToggleSubscribed={onToggleSubscribed}
|
||||
onPressEditList={onPressEditList}
|
||||
onPressDeleteList={onPressDeleteList}
|
||||
/>
|
||||
</CenteredView>
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
headerBtns: {
|
||||
flexDirection: 'row',
|
||||
gap: 8,
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
paddingBottom: isDesktopWeb ? 0 : 100,
|
||||
},
|
||||
containerDesktop: {
|
||||
borderLeftWidth: 1,
|
||||
borderRightWidth: 1,
|
||||
},
|
||||
})
|
|
@ -121,7 +121,7 @@ export const SearchScreen = withAuthRequired(
|
|||
<TouchableWithoutFeedback onPress={onPress} accessible={false}>
|
||||
<View style={[pal.view, styles.container]}>
|
||||
<HeaderWithInput
|
||||
isInputFocused={true}
|
||||
isInputFocused={isInputFocused}
|
||||
query={query}
|
||||
setIsInputFocused={setIsInputFocused}
|
||||
onChangeQuery={onChangeQuery}
|
||||
|
|
|
@ -127,11 +127,6 @@ export const SettingsScreen = withAuthRequired(
|
|||
store.shell.openModal({name: 'invite-codes'})
|
||||
}, [track, store])
|
||||
|
||||
const onPressContentFiltering = React.useCallback(() => {
|
||||
track('Settings:ContentfilteringButtonClicked')
|
||||
store.shell.openModal({name: 'content-filtering-settings'})
|
||||
}, [track, store])
|
||||
|
||||
const onPressContentLanguages = React.useCallback(() => {
|
||||
track('Settings:ContentlanguagesButtonClicked')
|
||||
store.shell.openModal({name: 'content-languages-settings'})
|
||||
|
@ -252,7 +247,9 @@ export const SettingsScreen = withAuthRequired(
|
|||
Add account
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<View style={styles.spacer20} />
|
||||
|
||||
<Text type="xl-bold" style={[pal.text, styles.heading]}>
|
||||
Invite a friend
|
||||
</Text>
|
||||
|
@ -287,9 +284,6 @@ export const SettingsScreen = withAuthRequired(
|
|||
|
||||
<View style={styles.spacer20} />
|
||||
|
||||
<Text type="xl-bold" style={[pal.text, styles.heading]}>
|
||||
Moderation
|
||||
</Text>
|
||||
<Link
|
||||
testID="bookmarkedAlgosBtn"
|
||||
style={[styles.linkCard, pal.view, isSwitching && styles.dimmed]}
|
||||
|
@ -306,51 +300,7 @@ export const SettingsScreen = withAuthRequired(
|
|||
Custom Algorithms
|
||||
</Text>
|
||||
</Link>
|
||||
<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]}>
|
||||
Advanced
|
||||
</Text>
|
||||
|
@ -398,8 +348,8 @@ export const SettingsScreen = withAuthRequired(
|
|||
style={pal.text as FontAwesomeIconStyle}
|
||||
/>
|
||||
</View>
|
||||
<Text type="lg" style={pal.text}>
|
||||
Change my handle
|
||||
<Text type="lg" style={pal.text} numberOfLines={1}>
|
||||
Change handle
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.spacer20} />
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue