diff options
Diffstat (limited to 'src/view/com/modals/EditProfile.tsx')
-rw-r--r-- | src/view/com/modals/EditProfile.tsx | 310 |
1 files changed, 0 insertions, 310 deletions
diff --git a/src/view/com/modals/EditProfile.tsx b/src/view/com/modals/EditProfile.tsx deleted file mode 100644 index 1e94f483e..000000000 --- a/src/view/com/modals/EditProfile.tsx +++ /dev/null @@ -1,310 +0,0 @@ -import React, {useCallback, useState} from 'react' -import { - ActivityIndicator, - KeyboardAvoidingView, - ScrollView, - StyleSheet, - TextInput, - TouchableOpacity, - View, -} from 'react-native' -import {Image as RNImage} from 'react-native-image-crop-picker' -import Animated, {FadeOut} from 'react-native-reanimated' -import {LinearGradient} from 'expo-linear-gradient' -import {AppBskyActorDefs} from '@atproto/api' -import {msg, Trans} from '@lingui/macro' -import {useLingui} from '@lingui/react' - -import {MAX_DESCRIPTION, MAX_DISPLAY_NAME} from '#/lib/constants' -import {usePalette} from '#/lib/hooks/usePalette' -import {compressIfNeeded} from '#/lib/media/manip' -import {cleanError} from '#/lib/strings/errors' -import {enforceLen} from '#/lib/strings/helpers' -import {colors, gradients, s} from '#/lib/styles' -import {useTheme} from '#/lib/ThemeContext' -import {logger} from '#/logger' -import {isWeb} from '#/platform/detection' -import {useModalControls} from '#/state/modals' -import {useProfileUpdateMutation} from '#/state/queries/profile' -import {Text} from '#/view/com/util/text/Text' -import * as Toast from '#/view/com/util/Toast' -import {EditableUserAvatar} from '#/view/com/util/UserAvatar' -import {UserBanner} from '#/view/com/util/UserBanner' -import {ErrorMessage} from '../util/error/ErrorMessage' - -const AnimatedTouchableOpacity = - Animated.createAnimatedComponent(TouchableOpacity) - -export const snapPoints = ['fullscreen'] - -export function Component({ - profile, - onUpdate, -}: { - profile: AppBskyActorDefs.ProfileViewDetailed - onUpdate?: () => void -}) { - const pal = usePalette('default') - const theme = useTheme() - const {_} = useLingui() - const {closeModal} = useModalControls() - const updateMutation = useProfileUpdateMutation() - const [imageError, setImageError] = useState<string>('') - const [displayName, setDisplayName] = useState<string>( - profile.displayName || '', - ) - const [description, setDescription] = useState<string>( - profile.description || '', - ) - const [userBanner, setUserBanner] = useState<string | undefined | null>( - profile.banner, - ) - const [userAvatar, setUserAvatar] = useState<string | undefined | null>( - profile.avatar, - ) - const [newUserBanner, setNewUserBanner] = useState< - RNImage | undefined | null - >() - const [newUserAvatar, setNewUserAvatar] = useState< - RNImage | undefined | null - >() - const onPressCancel = () => { - closeModal() - } - const onSelectNewAvatar = useCallback( - async (img: RNImage | null) => { - setImageError('') - if (img === null) { - setNewUserAvatar(null) - setUserAvatar(null) - return - } - try { - const finalImg = await compressIfNeeded(img, 1000000) - setNewUserAvatar(finalImg) - setUserAvatar(finalImg.path) - } catch (e: any) { - setImageError(cleanError(e)) - } - }, - [setNewUserAvatar, setUserAvatar, setImageError], - ) - - const onSelectNewBanner = useCallback( - async (img: RNImage | null) => { - setImageError('') - if (!img) { - setNewUserBanner(null) - setUserBanner(null) - return - } - try { - const finalImg = await compressIfNeeded(img, 1000000) - setNewUserBanner(finalImg) - setUserBanner(finalImg.path) - } catch (e: any) { - setImageError(cleanError(e)) - } - }, - [setNewUserBanner, setUserBanner, setImageError], - ) - - const onPressSave = useCallback(async () => { - setImageError('') - try { - await updateMutation.mutateAsync({ - profile, - updates: { - displayName, - description, - }, - newUserAvatar, - newUserBanner, - }) - Toast.show(_(msg`Profile updated`)) - onUpdate?.() - closeModal() - } catch (e: any) { - logger.error('Failed to update user profile', {message: String(e)}) - } - }, [ - updateMutation, - profile, - onUpdate, - closeModal, - displayName, - description, - newUserAvatar, - newUserBanner, - setImageError, - _, - ]) - - return ( - <KeyboardAvoidingView style={s.flex1} behavior="height"> - <ScrollView style={[pal.view]} testID="editProfileModal"> - <Text style={[styles.title, pal.text]}> - <Trans>Edit my profile</Trans> - </Text> - <View style={styles.photos}> - <UserBanner - banner={userBanner} - onSelectNewBanner={onSelectNewBanner} - /> - <View style={[styles.avi, {borderColor: pal.colors.background}]}> - <EditableUserAvatar - size={80} - avatar={userAvatar} - onSelectNewAvatar={onSelectNewAvatar} - /> - </View> - </View> - {updateMutation.isError && ( - <View style={styles.errorContainer}> - <ErrorMessage message={cleanError(updateMutation.error)} /> - </View> - )} - {imageError !== '' && ( - <View style={styles.errorContainer}> - <ErrorMessage message={imageError} /> - </View> - )} - <View style={styles.form}> - <View> - <Text style={[styles.label, pal.text]}> - <Trans>Display Name</Trans> - </Text> - <TextInput - testID="editProfileDisplayNameInput" - style={[styles.textInput, pal.border, pal.text]} - placeholder={_(msg`e.g. Alice Roberts`)} - placeholderTextColor={colors.gray4} - value={displayName} - onChangeText={v => - setDisplayName(enforceLen(v, MAX_DISPLAY_NAME)) - } - accessible={true} - accessibilityLabel={_(msg`Display name`)} - accessibilityHint={_(msg`Edit your display name`)} - /> - </View> - <View style={s.pb10}> - <Text style={[styles.label, pal.text]}> - <Trans>Description</Trans> - </Text> - <TextInput - testID="editProfileDescriptionInput" - style={[styles.textArea, pal.border, pal.text]} - placeholder={_(msg`e.g. Artist, dog-lover, and avid reader.`)} - placeholderTextColor={colors.gray4} - keyboardAppearance={theme.colorScheme} - multiline - value={description} - onChangeText={v => setDescription(enforceLen(v, MAX_DESCRIPTION))} - accessible={true} - accessibilityLabel={_(msg`Description`)} - accessibilityHint={_(msg`Edit your profile description`)} - /> - </View> - {updateMutation.isPending ? ( - <View style={[styles.btn, s.mt10, {backgroundColor: colors.gray2}]}> - <ActivityIndicator /> - </View> - ) : ( - <TouchableOpacity - testID="editProfileSaveBtn" - style={s.mt10} - onPress={onPressSave} - accessibilityRole="button" - accessibilityLabel={_(msg`Save`)} - accessibilityHint={_(msg`Saves any changes to your profile`)}> - <LinearGradient - colors={[gradients.blueLight.start, gradients.blueLight.end]} - start={{x: 0, y: 0}} - end={{x: 1, y: 1}} - style={[styles.btn]}> - <Text style={[s.white, s.bold]}> - <Trans>Save Changes</Trans> - </Text> - </LinearGradient> - </TouchableOpacity> - )} - {!updateMutation.isPending && ( - <AnimatedTouchableOpacity - exiting={!isWeb ? FadeOut : undefined} - testID="editProfileCancelBtn" - style={s.mt5} - onPress={onPressCancel} - accessibilityRole="button" - accessibilityLabel={_(msg`Cancel profile editing`)} - accessibilityHint="" - onAccessibilityEscape={onPressCancel}> - <View style={[styles.btn]}> - <Text style={[s.black, s.bold, pal.text]}> - <Trans>Cancel</Trans> - </Text> - </View> - </AnimatedTouchableOpacity> - )} - </View> - </ScrollView> - </KeyboardAvoidingView> - ) -} - -const styles = StyleSheet.create({ - title: { - textAlign: 'center', - fontWeight: '600', - fontSize: 24, - marginBottom: 18, - }, - label: { - fontWeight: '600', - paddingHorizontal: 4, - paddingBottom: 4, - marginTop: 20, - }, - form: { - paddingHorizontal: 14, - }, - textInput: { - borderWidth: 1, - borderRadius: 6, - paddingHorizontal: 14, - paddingVertical: 10, - fontSize: 16, - }, - textArea: { - borderWidth: 1, - borderRadius: 6, - paddingHorizontal: 12, - paddingTop: 10, - fontSize: 16, - height: 120, - textAlignVertical: 'top', - }, - btn: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - width: '100%', - borderRadius: 32, - padding: 10, - marginBottom: 10, - }, - avi: { - position: 'absolute', - top: 80, - left: 24, - width: 84, - height: 84, - borderWidth: 2, - borderRadius: 42, - }, - photos: { - marginBottom: 36, - marginHorizontal: -14, - }, - errorContainer: {marginTop: 20}, -}) |