From 0038a77f37c421c6062db7227e758ccff28aca3c Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Fri, 5 May 2023 14:36:18 -0500 Subject: [PATCH] Add CreateMuteList modal --- src/state/models/ui/shell.ts | 8 +- src/view/com/modals/CreateMuteList.tsx | 236 +++++++++++++++++++++++ src/view/com/modals/Modal.tsx | 4 + src/view/com/modals/Modal.web.tsx | 3 + src/view/screens/ModerationMuteLists.tsx | 13 +- 5 files changed, 261 insertions(+), 3 deletions(-) create mode 100644 src/view/com/modals/CreateMuteList.tsx diff --git a/src/state/models/ui/shell.ts b/src/state/models/ui/shell.ts index 4a55c23ad2..97605d4d14 100644 --- a/src/state/models/ui/shell.ts +++ b/src/state/models/ui/shell.ts @@ -37,6 +37,11 @@ export interface ReportAccountModal { did: string } +export interface CreateMuteListModal { + name: 'create-mute-list' + onCreate?: (uri: string) => void +} + export interface CropImageModal { name: 'crop-image' uri: string @@ -95,9 +100,10 @@ export type Modal = | ContentFilteringSettingsModal | ContentLanguagesSettingsModal - // Reporting + // Moderation | ReportAccountModal | ReportPostModal + | CreateMuteListModal // Posts | AltTextImageModal diff --git a/src/view/com/modals/CreateMuteList.tsx b/src/view/com/modals/CreateMuteList.tsx new file mode 100644 index 0000000000..97e770b918 --- /dev/null +++ b/src/view/com/modals/CreateMuteList.tsx @@ -0,0 +1,236 @@ +import React, {useState, useCallback} from 'react' +import * as Toast from '../util/Toast' +import { + ActivityIndicator, + KeyboardAvoidingView, + ScrollView, + StyleSheet, + TextInput, + TouchableOpacity, + View, +} from 'react-native' +import LinearGradient from 'react-native-linear-gradient' +import {Image as RNImage} from 'react-native-image-crop-picker' +import {Text} from '../util/text/Text' +import {ErrorMessage} from '../util/error/ErrorMessage' +import {useStores} from 'state/index' +import {s, colors, gradients} from 'lib/styles' +import {enforceLen} from 'lib/strings/helpers' +import {compressIfNeeded} from 'lib/media/manip' +import {UserAvatar} from '../util/UserAvatar' +import {usePalette} from 'lib/hooks/usePalette' +import {useTheme} from 'lib/ThemeContext' +import {useAnalytics} from 'lib/analytics' +import {cleanError, isNetworkError} from 'lib/strings/errors' + +const MAX_NAME = 64 // todo +const MAX_DESCRIPTION = 300 // todo + +export const snapPoints = ['fullscreen'] + +export function Component({onCreate}: {onCreate?: (uri: string) => void}) { + const store = useStores() + const [error, setError] = useState('') + const pal = usePalette('default') + const theme = useTheme() + const {track} = useAnalytics() + + const [isProcessing, setProcessing] = useState(false) + const [name, setName] = useState('') + const [description, setDescription] = useState('') + const [avatar, setAvatar] = useState(undefined) + const [newAvatar, setNewAvatar] = useState() + + const onPressCancel = useCallback(() => { + store.shell.closeModal() + }, [store]) + + const onSelectNewAvatar = useCallback( + async (img: RNImage | null) => { + if (!img) { + setNewAvatar(null) + setAvatar(null) + return + } + track('CreateMuteList:AvatarSelected') + try { + const finalImg = await compressIfNeeded(img, 1000000) + setNewAvatar(finalImg) + setAvatar(finalImg.path) + } catch (e: any) { + setError(cleanError(e)) + } + }, + [track, setNewAvatar, setAvatar, setError], + ) + + const onPressSave = useCallback(async () => { + track('CreateMuteList:Save') + setProcessing(true) + if (error) { + setError('') + } + try { + // TODO + Toast.show('Mute-list created') + onCreate?.('todo') + store.shell.closeModal() + } catch (e: any) { + if (isNetworkError(e)) { + setError( + 'Failed to create the mute-list. Check your internet connection and try again.', + ) + } else { + setError(cleanError(e)) + } + } + setProcessing(false) + }, [ + track, + setProcessing, + setError, + error, + onCreate, + store, + name, + description, + newAvatar, + ]) + + return ( + + + New Mute List + {error !== '' && ( + + + + )} + List Avatar + + + + + + List Name + setName(enforceLen(v, MAX_NAME))} + accessible={true} + accessibilityLabel="Name" + accessibilityHint="Set the list's name" + /> + + + Description + setDescription(enforceLen(v, MAX_DESCRIPTION))} + accessible={true} + accessibilityLabel="Description" + accessibilityHint="Edit your list's description" + /> + + {isProcessing ? ( + + + + ) : ( + + + Save + + + )} + + + Cancel + + + + + + ) +} + +const styles = StyleSheet.create({ + title: { + textAlign: 'center', + fontWeight: 'bold', + fontSize: 24, + marginBottom: 18, + }, + label: { + fontWeight: 'bold', + paddingHorizontal: 4, + paddingBottom: 4, + marginTop: 20, + }, + form: { + paddingHorizontal: 6, + }, + textInput: { + borderWidth: 1, + borderRadius: 6, + paddingHorizontal: 14, + paddingVertical: 10, + fontSize: 16, + }, + textArea: { + borderWidth: 1, + borderRadius: 6, + paddingHorizontal: 12, + paddingTop: 10, + fontSize: 16, + height: 100, + textAlignVertical: 'top', + }, + btn: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + width: '100%', + borderRadius: 32, + padding: 10, + marginBottom: 10, + }, + avi: { + width: 84, + height: 84, + borderWidth: 2, + borderRadius: 42, + marginTop: 4, + }, + errorContainer: {marginTop: 20}, +}) diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx index 18b7ae4c4d..4f7d098085 100644 --- a/src/view/com/modals/Modal.tsx +++ b/src/view/com/modals/Modal.tsx @@ -12,6 +12,7 @@ import * as EditProfileModal from './EditProfile' import * as ServerInputModal from './ServerInput' import * as ReportPostModal from './ReportPost' import * as RepostModal from './Repost' +import * as CreateMuteListModal from './CreateMuteList' import * as AltImageModal from './AltImage' import * as ReportAccountModal from './ReportAccount' import * as DeleteAccountModal from './DeleteAccount' @@ -66,6 +67,9 @@ export const ModalsContainer = observer(function ModalsContainer() { } else if (activeModal?.name === 'report-account') { snapPoints = ReportAccountModal.snapPoints element = + } else if (activeModal?.name === 'create-mute-list') { + snapPoints = ReportAccountModal.snapPoints + element = } else if (activeModal?.name === 'delete-account') { snapPoints = DeleteAccountModal.snapPoints element = diff --git a/src/view/com/modals/Modal.web.tsx b/src/view/com/modals/Modal.web.tsx index 9dcc8fa7e6..dd186bfdd0 100644 --- a/src/view/com/modals/Modal.web.tsx +++ b/src/view/com/modals/Modal.web.tsx @@ -11,6 +11,7 @@ import * as EditProfileModal from './EditProfile' import * as ServerInputModal from './ServerInput' import * as ReportPostModal from './ReportPost' import * as ReportAccountModal from './ReportAccount' +import * as CreateMuteListModal from './CreateMuteList' import * as DeleteAccountModal from './DeleteAccount' import * as RepostModal from './Repost' import * as CropImageModal from './crop-image/CropImage.web' @@ -68,6 +69,8 @@ function Modal({modal}: {modal: ModalIface}) { element = } else if (modal.name === 'report-account') { element = + } else if (modal.name === 'create-mute-list') { + element = } else if (modal.name === 'crop-image') { element = } else if (modal.name === 'delete-account') { diff --git a/src/view/screens/ModerationMuteLists.tsx b/src/view/screens/ModerationMuteLists.tsx index b4360e6535..3107883b1f 100644 --- a/src/view/screens/ModerationMuteLists.tsx +++ b/src/view/screens/ModerationMuteLists.tsx @@ -33,16 +33,25 @@ export const ModerationMuteListsScreen = withAuthRequired(({route}: Props) => { }, [store]), ) + const onPressNewMuteList = React.useCallback(() => { + store.shell.openModal({ + name: 'create-mute-list', + onCreate: (uri: string) => { + // TODO + }, + }) + }, [store]) + const renderEmptyState = React.useCallback(() => { return ( undefined} + onPress={onPressNewMuteList} /> ) - }, []) + }, [onPressNewMuteList]) return (