Add the ability to create new mutelists
This commit is contained in:
@@ -8,17 +8,25 @@ import {
|
|||||||
ViewStyle,
|
ViewStyle,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {observer} from 'mobx-react-lite'
|
import {observer} from 'mobx-react-lite'
|
||||||
|
import {
|
||||||
|
FontAwesomeIcon,
|
||||||
|
FontAwesomeIconStyle,
|
||||||
|
} from '@fortawesome/react-native-fontawesome'
|
||||||
import {FlatList} from '../util/Views'
|
import {FlatList} from '../util/Views'
|
||||||
import {ListCard} from './ListCard'
|
import {ListCard} from './ListCard'
|
||||||
import {ProfileCardFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
|
import {ProfileCardFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
|
||||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||||
import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn'
|
import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn'
|
||||||
|
import {Button} from '../util/forms/Button'
|
||||||
|
import {Text} from '../util/text/Text'
|
||||||
import {ListsListModel} from 'state/models/lists/lists-list'
|
import {ListsListModel} from 'state/models/lists/lists-list'
|
||||||
import {useAnalytics} from 'lib/analytics'
|
import {useAnalytics} from 'lib/analytics'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {s} from 'lib/styles'
|
import {s} from 'lib/styles'
|
||||||
|
import {isDesktopWeb} from 'platform/detection'
|
||||||
|
|
||||||
const LOADING_ITEM = {_reactKey: '__loading__'}
|
const LOADING_ITEM = {_reactKey: '__loading__'}
|
||||||
|
const CREATENEW_ITEM = {_reactKey: '__loading__'}
|
||||||
const EMPTY_ITEM = {_reactKey: '__empty__'}
|
const EMPTY_ITEM = {_reactKey: '__empty__'}
|
||||||
const ERROR_ITEM = {_reactKey: '__error__'}
|
const ERROR_ITEM = {_reactKey: '__error__'}
|
||||||
const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'}
|
const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'}
|
||||||
@@ -27,17 +35,17 @@ export const ListsList = observer(
|
|||||||
({
|
({
|
||||||
listsList,
|
listsList,
|
||||||
style,
|
style,
|
||||||
showPostFollowBtn,
|
|
||||||
scrollElRef,
|
scrollElRef,
|
||||||
onPressTryAgain,
|
onPressTryAgain,
|
||||||
|
onPressCreateNew,
|
||||||
renderEmptyState,
|
renderEmptyState,
|
||||||
testID,
|
testID,
|
||||||
headerOffset = 0,
|
headerOffset = 0,
|
||||||
}: {
|
}: {
|
||||||
listsList: ListsListModel
|
listsList: ListsListModel
|
||||||
style?: StyleProp<ViewStyle>
|
style?: StyleProp<ViewStyle>
|
||||||
showPostFollowBtn?: boolean
|
|
||||||
scrollElRef?: MutableRefObject<FlatList<any> | null>
|
scrollElRef?: MutableRefObject<FlatList<any> | null>
|
||||||
|
onPressCreateNew: () => void
|
||||||
onPressTryAgain?: () => void
|
onPressTryAgain?: () => void
|
||||||
renderEmptyState?: () => JSX.Element
|
renderEmptyState?: () => JSX.Element
|
||||||
testID?: string
|
testID?: string
|
||||||
@@ -56,6 +64,9 @@ export const ListsList = observer(
|
|||||||
if (listsList.isEmpty) {
|
if (listsList.isEmpty) {
|
||||||
items = items.concat([EMPTY_ITEM])
|
items = items.concat([EMPTY_ITEM])
|
||||||
} else {
|
} else {
|
||||||
|
if (isDesktopWeb) {
|
||||||
|
items = items.concat([CREATENEW_ITEM])
|
||||||
|
}
|
||||||
items = items.concat(listsList.lists)
|
items = items.concat(listsList.lists)
|
||||||
}
|
}
|
||||||
if (listsList.loadMoreError) {
|
if (listsList.loadMoreError) {
|
||||||
@@ -111,6 +122,8 @@ export const ListsList = observer(
|
|||||||
return renderEmptyState()
|
return renderEmptyState()
|
||||||
}
|
}
|
||||||
return <View />
|
return <View />
|
||||||
|
} else if (item === CREATENEW_ITEM) {
|
||||||
|
return <CreateNewItem onPress={onPressCreateNew} />
|
||||||
} else if (item === ERROR_ITEM) {
|
} else if (item === ERROR_ITEM) {
|
||||||
return (
|
return (
|
||||||
<ErrorMessage
|
<ErrorMessage
|
||||||
@@ -130,7 +143,7 @@ export const ListsList = observer(
|
|||||||
}
|
}
|
||||||
return <ListCard list={item} />
|
return <ListCard list={item} />
|
||||||
},
|
},
|
||||||
[listsList, onPressTryAgain, onPressRetryLoadMore, showPostFollowBtn],
|
[listsList, onPressTryAgain, onPressRetryLoadMore, onPressCreateNew],
|
||||||
)
|
)
|
||||||
|
|
||||||
const Footer = React.useCallback(
|
const Footer = React.useCallback(
|
||||||
@@ -179,6 +192,35 @@ export const ListsList = observer(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
function CreateNewItem({onPress}: {onPress: () => void}) {
|
||||||
|
const palInverted = usePalette('inverted')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={[styles.createNewContainer]}>
|
||||||
|
<Button type="inverted" onPress={onPress} style={styles.createNewButton}>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon="plus"
|
||||||
|
style={palInverted.text as FontAwesomeIconStyle}
|
||||||
|
/>
|
||||||
|
<Text type="button" style={palInverted.text}>
|
||||||
|
New Mute-list
|
||||||
|
</Text>
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
createNewContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingVertical: 16,
|
||||||
|
paddingHorizontal: 18,
|
||||||
|
},
|
||||||
|
createNewButton: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 8,
|
||||||
|
},
|
||||||
feedFooter: {paddingTop: 20},
|
feedFooter: {paddingTop: 20},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import {usePalette} from 'lib/hooks/usePalette'
|
|||||||
import {useTheme} from 'lib/ThemeContext'
|
import {useTheme} from 'lib/ThemeContext'
|
||||||
import {useAnalytics} from 'lib/analytics'
|
import {useAnalytics} from 'lib/analytics'
|
||||||
import {cleanError, isNetworkError} from 'lib/strings/errors'
|
import {cleanError, isNetworkError} from 'lib/strings/errors'
|
||||||
|
import {isDesktopWeb} from 'platform/detection'
|
||||||
|
|
||||||
const MAX_NAME = 64 // todo
|
const MAX_NAME = 64 // todo
|
||||||
const MAX_DESCRIPTION = 300 // todo
|
const MAX_DESCRIPTION = 300 // todo
|
||||||
@@ -104,7 +105,9 @@ export function Component({onCreate}: {onCreate?: (uri: string) => void}) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<KeyboardAvoidingView behavior="height">
|
<KeyboardAvoidingView behavior="height">
|
||||||
<ScrollView style={[pal.view]} testID="createMuteListModal">
|
<ScrollView
|
||||||
|
style={[pal.view, styles.container]}
|
||||||
|
testID="createMuteListModal">
|
||||||
<Text style={[styles.title, pal.text]}>New Mute List</Text>
|
<Text style={[styles.title, pal.text]}>New Mute List</Text>
|
||||||
{error !== '' && (
|
{error !== '' && (
|
||||||
<View style={styles.errorContainer}>
|
<View style={styles.errorContainer}>
|
||||||
@@ -190,6 +193,9 @@ export function Component({onCreate}: {onCreate?: (uri: string) => void}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
paddingHorizontal: isDesktopWeb ? 0 : 16,
|
||||||
|
},
|
||||||
title: {
|
title: {
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export const ModalsContainer = observer(function ModalsContainer() {
|
|||||||
snapPoints = ReportAccountModal.snapPoints
|
snapPoints = ReportAccountModal.snapPoints
|
||||||
element = <ReportAccountModal.Component {...activeModal} />
|
element = <ReportAccountModal.Component {...activeModal} />
|
||||||
} else if (activeModal?.name === 'create-mute-list') {
|
} else if (activeModal?.name === 'create-mute-list') {
|
||||||
snapPoints = ReportAccountModal.snapPoints
|
snapPoints = CreateMuteListModal.snapPoints
|
||||||
element = <CreateMuteListModal.Component {...activeModal} />
|
element = <CreateMuteListModal.Component {...activeModal} />
|
||||||
} else if (activeModal?.name === 'delete-account') {
|
} else if (activeModal?.name === 'delete-account') {
|
||||||
snapPoints = DeleteAccountModal.snapPoints
|
snapPoints = DeleteAccountModal.snapPoints
|
||||||
|
|||||||
@@ -20,11 +20,13 @@ export const ViewHeader = observer(function ({
|
|||||||
canGoBack,
|
canGoBack,
|
||||||
hideOnScroll,
|
hideOnScroll,
|
||||||
showOnDesktop,
|
showOnDesktop,
|
||||||
|
renderButton,
|
||||||
}: {
|
}: {
|
||||||
title: string
|
title: string
|
||||||
canGoBack?: boolean
|
canGoBack?: boolean
|
||||||
hideOnScroll?: boolean
|
hideOnScroll?: boolean
|
||||||
showOnDesktop?: boolean
|
showOnDesktop?: boolean
|
||||||
|
renderButton?: () => JSX.Element
|
||||||
}) {
|
}) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
@@ -79,7 +81,11 @@ export const ViewHeader = observer(function ({
|
|||||||
{title}
|
{title}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={canGoBack ? styles.backBtn : styles.backBtnWide} />
|
{renderButton ? (
|
||||||
|
renderButton()
|
||||||
|
) : (
|
||||||
|
<View style={canGoBack ? styles.backBtn : styles.backBtnWide} />
|
||||||
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {StyleSheet} from 'react-native'
|
import {StyleSheet} from 'react-native'
|
||||||
|
import {useFocusEffect, useNavigation} from '@react-navigation/native'
|
||||||
import {
|
import {
|
||||||
useFocusEffect,
|
FontAwesomeIcon,
|
||||||
useNavigation,
|
FontAwesomeIconStyle,
|
||||||
StackActions,
|
} from '@fortawesome/react-native-fontawesome'
|
||||||
} from '@react-navigation/native'
|
|
||||||
import {AtUri} from '@atproto/api'
|
import {AtUri} from '@atproto/api'
|
||||||
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
|
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
|
||||||
import {withAuthRequired} from 'view/com/auth/withAuthRequired'
|
import {withAuthRequired} from 'view/com/auth/withAuthRequired'
|
||||||
@@ -12,6 +12,7 @@ import {EmptyStateWithButton} from 'view/com/util/EmptyStateWithButton'
|
|||||||
import {useStores} from 'state/index'
|
import {useStores} from 'state/index'
|
||||||
import {ListsListModel} from 'state/models/lists/lists-list'
|
import {ListsListModel} from 'state/models/lists/lists-list'
|
||||||
import {ListsList} from 'view/com/lists/ListsList'
|
import {ListsList} from 'view/com/lists/ListsList'
|
||||||
|
import {Button} from 'view/com/util/forms/Button'
|
||||||
import {NavigationProp} from 'lib/routes/types'
|
import {NavigationProp} from 'lib/routes/types'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {CenteredView} from 'view/com/util/Views'
|
import {CenteredView} from 'view/com/util/Views'
|
||||||
@@ -65,6 +66,22 @@ export const ModerationMuteListsScreen = withAuthRequired(({route}: Props) => {
|
|||||||
)
|
)
|
||||||
}, [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 (
|
return (
|
||||||
<CenteredView
|
<CenteredView
|
||||||
style={[
|
style={[
|
||||||
@@ -74,8 +91,16 @@ export const ModerationMuteListsScreen = withAuthRequired(({route}: Props) => {
|
|||||||
pal.border,
|
pal.border,
|
||||||
]}
|
]}
|
||||||
testID="moderationMutelistsScreen">
|
testID="moderationMutelistsScreen">
|
||||||
<ViewHeader title="Mute-list Subscriptions" showOnDesktop />
|
<ViewHeader
|
||||||
<ListsList listsList={mutelists} renderEmptyState={renderEmptyState} />
|
title="Mute-lists"
|
||||||
|
showOnDesktop
|
||||||
|
renderButton={renderHeaderButton}
|
||||||
|
/>
|
||||||
|
<ListsList
|
||||||
|
listsList={mutelists}
|
||||||
|
renderEmptyState={renderEmptyState}
|
||||||
|
onPressCreateNew={onPressNewMuteList}
|
||||||
|
/>
|
||||||
</CenteredView>
|
</CenteredView>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -89,36 +114,7 @@ const styles = StyleSheet.create({
|
|||||||
borderLeftWidth: 1,
|
borderLeftWidth: 1,
|
||||||
borderRightWidth: 1,
|
borderRightWidth: 1,
|
||||||
},
|
},
|
||||||
title: {
|
createBtn: {
|
||||||
textAlign: 'center',
|
width: 40,
|
||||||
marginTop: 12,
|
|
||||||
marginBottom: 12,
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
textAlign: 'center',
|
|
||||||
paddingHorizontal: 30,
|
|
||||||
marginBottom: 14,
|
|
||||||
},
|
|
||||||
descriptionDesktop: {
|
|
||||||
marginTop: 14,
|
|
||||||
},
|
|
||||||
|
|
||||||
flex1: {
|
|
||||||
flex: 1,
|
|
||||||
},
|
|
||||||
empty: {
|
|
||||||
paddingHorizontal: 20,
|
|
||||||
paddingVertical: 20,
|
|
||||||
borderRadius: 16,
|
|
||||||
marginHorizontal: 24,
|
|
||||||
marginTop: 10,
|
|
||||||
},
|
|
||||||
emptyText: {
|
|
||||||
textAlign: 'center',
|
|
||||||
},
|
|
||||||
|
|
||||||
footer: {
|
|
||||||
height: 200,
|
|
||||||
paddingTop: 20,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user