diff options
author | Paul Frazee <pfrazee@gmail.com> | 2023-05-11 16:08:21 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-05-11 16:08:21 -0500 |
commit | ebcd6333863a2073278fad482981d9898c0f20ca (patch) | |
tree | 9417a5c282fc6ce22af2251f437f02b0700c7714 /src/view/screens/ProfileList.tsx | |
parent | 34d8fa59916d87922c83a6cf93e3e288d43dadcc (diff) | |
download | voidsky-ebcd6333863a2073278fad482981d9898c0f20ca.tar.zst |
[APP-635] Mutelists (#601)
* Add lists and profilelist screens * Implement lists screen and lists-list in profiles * Add empty states to the lists screen * Switch (mostly) from blocklists to mutelists * Rework: create a new moderation screen and move everything related under it * Fix moderation screen on desktop web * Tune the empty state code * Change content moderation modal to content filtering * Add CreateMuteList modal * Implement mutelist creation * Add lists listings * Add the ability to create new mutelists * Add 'add to list' tool * Satisfy the hashtag hyphen haters * Add update/delete/subscribe/unsubscribe to lists * Show which list caused a mute * Add list un/subscribe * Add the mute override when viewing a profile's posts * Update to latest backend * Add simulation tests and tune some behaviors * Fix lint * Bump deps * Fix list refresh after creation * Mute list subscriptions -> Mute lists
Diffstat (limited to 'src/view/screens/ProfileList.tsx')
-rw-r--r-- | src/view/screens/ProfileList.tsx | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/src/view/screens/ProfileList.tsx b/src/view/screens/ProfileList.tsx new file mode 100644 index 000000000..a78faaf62 --- /dev/null +++ b/src/view/screens/ProfileList.tsx @@ -0,0 +1,175 @@ +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 {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]) + + 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="Deletes the mutelist" + onPress={onPressDeleteList} + /> + )} + {list?.isOwner && ( + <Button + type="default" + label="Edit List" + testID="editListBtn" + accessibilityLabel="Edit list" + accessibilityHint="Opens a modal to edit the mutelist" + onPress={onPressEditList} + /> + )} + {list.list?.viewer?.muted ? ( + <Button + type="inverted" + label="Unsubscribe" + testID="unsubscribeListBtn" + accessibilityLabel="Unsubscribe from this list" + accessibilityHint="Stops muting the users included in this list" + 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, + }, +}) |