Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

temp revert to old modal (#6005)

authored by

Samuel Newman and committed by
GitHub
1cfcffd7 27ff8543

+354 -8
+13 -3
src/screens/Profile/Header/ProfileHeaderLabeler.tsx
··· 15 15 import {useHaptics} from '#/lib/haptics' 16 16 import {isAppLabeler} from '#/lib/moderation' 17 17 import {logger} from '#/logger' 18 - import {isIOS} from '#/platform/detection' 18 + import {isIOS, isWeb} from '#/platform/detection' 19 19 import {useProfileShadow} from '#/state/cache/profile-shadow' 20 20 import {Shadow} from '#/state/cache/types' 21 + import {useModalControls} from '#/state/modals' 21 22 import {useLabelerSubscriptionMutation} from '#/state/queries/labeler' 22 23 import {useLikeMutation, useUnlikeMutation} from '#/state/queries/like' 23 24 import {usePreferencesQuery} from '#/state/queries/preferences' ··· 116 117 } 117 118 }, [labeler, playHaptic, likeUri, unlikeMod, likeMod, _]) 118 119 120 + const {openModal} = useModalControls() 119 121 const editProfileControl = useDialogControl() 120 122 const onPressEditProfile = React.useCallback(() => { 121 - editProfileControl.open() 122 - }, [editProfileControl]) 123 + if (isWeb) { 124 + // temp, while we figure out the nested dialog bug 125 + openModal({ 126 + name: 'edit-profile', 127 + profile, 128 + }) 129 + } else { 130 + editProfileControl.open() 131 + } 132 + }, [editProfileControl, openModal, profile]) 123 133 124 134 const onPressSubscribe = React.useCallback( 125 135 () =>
+13 -3
src/screens/Profile/Header/ProfileHeaderStandard.tsx
··· 11 11 12 12 import {sanitizeDisplayName} from '#/lib/strings/display-names' 13 13 import {logger} from '#/logger' 14 - import {isIOS} from '#/platform/detection' 14 + import {isIOS, isWeb} from '#/platform/detection' 15 15 import {useProfileShadow} from '#/state/cache/profile-shadow' 16 16 import {Shadow} from '#/state/cache/types' 17 + import {useModalControls} from '#/state/modals' 17 18 import { 18 19 useProfileBlockMutationQueue, 19 20 useProfileFollowMutationQueue, ··· 74 75 profile.viewer?.blockedBy || 75 76 profile.viewer?.blockingByList 76 77 78 + const {openModal} = useModalControls() 77 79 const editProfileControl = useDialogControl() 78 80 const onPressEditProfile = React.useCallback(() => { 79 - editProfileControl.open() 80 - }, [editProfileControl]) 81 + if (isWeb) { 82 + // temp, while we figure out the nested dialog bug 83 + openModal({ 84 + name: 'edit-profile', 85 + profile, 86 + }) 87 + } else { 88 + editProfileControl.open() 89 + } 90 + }, [editProfileControl, openModal, profile]) 81 91 82 92 const onPressFollow = () => { 83 93 requireAuth(async () => {
+9
src/state/modals/index.tsx
··· 4 4 5 5 import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' 6 6 7 + export interface EditProfileModal { 8 + name: 'edit-profile' 9 + profile: AppBskyActorDefs.ProfileViewDetailed 10 + onUpdate?: () => void 11 + } 12 + 7 13 export interface CreateOrEditListModal { 8 14 name: 'create-or-edit-list' 9 15 purpose?: string ··· 101 107 | VerifyEmailModal 102 108 | ChangeEmailModal 103 109 | ChangePasswordModal 110 + 111 + // Temp 112 + | EditProfileModal 104 113 105 114 // Curation 106 115 | ContentLanguagesSettingsModal
+310
src/view/com/modals/EditProfile.tsx
··· 1 + import React, {useCallback, useState} from 'react' 2 + import { 3 + ActivityIndicator, 4 + KeyboardAvoidingView, 5 + ScrollView, 6 + StyleSheet, 7 + TextInput, 8 + TouchableOpacity, 9 + View, 10 + } from 'react-native' 11 + import {Image as RNImage} from 'react-native-image-crop-picker' 12 + import Animated, {FadeOut} from 'react-native-reanimated' 13 + import {LinearGradient} from 'expo-linear-gradient' 14 + import {AppBskyActorDefs} from '@atproto/api' 15 + import {msg, Trans} from '@lingui/macro' 16 + import {useLingui} from '@lingui/react' 17 + 18 + import {MAX_DESCRIPTION, MAX_DISPLAY_NAME} from '#/lib/constants' 19 + import {usePalette} from '#/lib/hooks/usePalette' 20 + import {compressIfNeeded} from '#/lib/media/manip' 21 + import {cleanError} from '#/lib/strings/errors' 22 + import {enforceLen} from '#/lib/strings/helpers' 23 + import {colors, gradients, s} from '#/lib/styles' 24 + import {useTheme} from '#/lib/ThemeContext' 25 + import {logger} from '#/logger' 26 + import {isWeb} from '#/platform/detection' 27 + import {useModalControls} from '#/state/modals' 28 + import {useProfileUpdateMutation} from '#/state/queries/profile' 29 + import {Text} from '#/view/com/util/text/Text' 30 + import * as Toast from '#/view/com/util/Toast' 31 + import {EditableUserAvatar} from '#/view/com/util/UserAvatar' 32 + import {UserBanner} from '#/view/com/util/UserBanner' 33 + import {ErrorMessage} from '../util/error/ErrorMessage' 34 + 35 + const AnimatedTouchableOpacity = 36 + Animated.createAnimatedComponent(TouchableOpacity) 37 + 38 + export const snapPoints = ['fullscreen'] 39 + 40 + export function Component({ 41 + profile, 42 + onUpdate, 43 + }: { 44 + profile: AppBskyActorDefs.ProfileViewDetailed 45 + onUpdate?: () => void 46 + }) { 47 + const pal = usePalette('default') 48 + const theme = useTheme() 49 + const {_} = useLingui() 50 + const {closeModal} = useModalControls() 51 + const updateMutation = useProfileUpdateMutation() 52 + const [imageError, setImageError] = useState<string>('') 53 + const [displayName, setDisplayName] = useState<string>( 54 + profile.displayName || '', 55 + ) 56 + const [description, setDescription] = useState<string>( 57 + profile.description || '', 58 + ) 59 + const [userBanner, setUserBanner] = useState<string | undefined | null>( 60 + profile.banner, 61 + ) 62 + const [userAvatar, setUserAvatar] = useState<string | undefined | null>( 63 + profile.avatar, 64 + ) 65 + const [newUserBanner, setNewUserBanner] = useState< 66 + RNImage | undefined | null 67 + >() 68 + const [newUserAvatar, setNewUserAvatar] = useState< 69 + RNImage | undefined | null 70 + >() 71 + const onPressCancel = () => { 72 + closeModal() 73 + } 74 + const onSelectNewAvatar = useCallback( 75 + async (img: RNImage | null) => { 76 + setImageError('') 77 + if (img === null) { 78 + setNewUserAvatar(null) 79 + setUserAvatar(null) 80 + return 81 + } 82 + try { 83 + const finalImg = await compressIfNeeded(img, 1000000) 84 + setNewUserAvatar(finalImg) 85 + setUserAvatar(finalImg.path) 86 + } catch (e: any) { 87 + setImageError(cleanError(e)) 88 + } 89 + }, 90 + [setNewUserAvatar, setUserAvatar, setImageError], 91 + ) 92 + 93 + const onSelectNewBanner = useCallback( 94 + async (img: RNImage | null) => { 95 + setImageError('') 96 + if (!img) { 97 + setNewUserBanner(null) 98 + setUserBanner(null) 99 + return 100 + } 101 + try { 102 + const finalImg = await compressIfNeeded(img, 1000000) 103 + setNewUserBanner(finalImg) 104 + setUserBanner(finalImg.path) 105 + } catch (e: any) { 106 + setImageError(cleanError(e)) 107 + } 108 + }, 109 + [setNewUserBanner, setUserBanner, setImageError], 110 + ) 111 + 112 + const onPressSave = useCallback(async () => { 113 + setImageError('') 114 + try { 115 + await updateMutation.mutateAsync({ 116 + profile, 117 + updates: { 118 + displayName, 119 + description, 120 + }, 121 + newUserAvatar, 122 + newUserBanner, 123 + }) 124 + Toast.show(_(msg`Profile updated`)) 125 + onUpdate?.() 126 + closeModal() 127 + } catch (e: any) { 128 + logger.error('Failed to update user profile', {message: String(e)}) 129 + } 130 + }, [ 131 + updateMutation, 132 + profile, 133 + onUpdate, 134 + closeModal, 135 + displayName, 136 + description, 137 + newUserAvatar, 138 + newUserBanner, 139 + setImageError, 140 + _, 141 + ]) 142 + 143 + return ( 144 + <KeyboardAvoidingView style={s.flex1} behavior="height"> 145 + <ScrollView style={[pal.view]} testID="editProfileModal"> 146 + <Text style={[styles.title, pal.text]}> 147 + <Trans>Edit my profile</Trans> 148 + </Text> 149 + <View style={styles.photos}> 150 + <UserBanner 151 + banner={userBanner} 152 + onSelectNewBanner={onSelectNewBanner} 153 + /> 154 + <View style={[styles.avi, {borderColor: pal.colors.background}]}> 155 + <EditableUserAvatar 156 + size={80} 157 + avatar={userAvatar} 158 + onSelectNewAvatar={onSelectNewAvatar} 159 + /> 160 + </View> 161 + </View> 162 + {updateMutation.isError && ( 163 + <View style={styles.errorContainer}> 164 + <ErrorMessage message={cleanError(updateMutation.error)} /> 165 + </View> 166 + )} 167 + {imageError !== '' && ( 168 + <View style={styles.errorContainer}> 169 + <ErrorMessage message={imageError} /> 170 + </View> 171 + )} 172 + <View style={styles.form}> 173 + <View> 174 + <Text style={[styles.label, pal.text]}> 175 + <Trans>Display Name</Trans> 176 + </Text> 177 + <TextInput 178 + testID="editProfileDisplayNameInput" 179 + style={[styles.textInput, pal.border, pal.text]} 180 + placeholder={_(msg`e.g. Alice Roberts`)} 181 + placeholderTextColor={colors.gray4} 182 + value={displayName} 183 + onChangeText={v => 184 + setDisplayName(enforceLen(v, MAX_DISPLAY_NAME)) 185 + } 186 + accessible={true} 187 + accessibilityLabel={_(msg`Display name`)} 188 + accessibilityHint={_(msg`Edit your display name`)} 189 + /> 190 + </View> 191 + <View style={s.pb10}> 192 + <Text style={[styles.label, pal.text]}> 193 + <Trans>Description</Trans> 194 + </Text> 195 + <TextInput 196 + testID="editProfileDescriptionInput" 197 + style={[styles.textArea, pal.border, pal.text]} 198 + placeholder={_(msg`e.g. Artist, dog-lover, and avid reader.`)} 199 + placeholderTextColor={colors.gray4} 200 + keyboardAppearance={theme.colorScheme} 201 + multiline 202 + value={description} 203 + onChangeText={v => setDescription(enforceLen(v, MAX_DESCRIPTION))} 204 + accessible={true} 205 + accessibilityLabel={_(msg`Description`)} 206 + accessibilityHint={_(msg`Edit your profile description`)} 207 + /> 208 + </View> 209 + {updateMutation.isPending ? ( 210 + <View style={[styles.btn, s.mt10, {backgroundColor: colors.gray2}]}> 211 + <ActivityIndicator /> 212 + </View> 213 + ) : ( 214 + <TouchableOpacity 215 + testID="editProfileSaveBtn" 216 + style={s.mt10} 217 + onPress={onPressSave} 218 + accessibilityRole="button" 219 + accessibilityLabel={_(msg`Save`)} 220 + accessibilityHint={_(msg`Saves any changes to your profile`)}> 221 + <LinearGradient 222 + colors={[gradients.blueLight.start, gradients.blueLight.end]} 223 + start={{x: 0, y: 0}} 224 + end={{x: 1, y: 1}} 225 + style={[styles.btn]}> 226 + <Text style={[s.white, s.bold]}> 227 + <Trans>Save Changes</Trans> 228 + </Text> 229 + </LinearGradient> 230 + </TouchableOpacity> 231 + )} 232 + {!updateMutation.isPending && ( 233 + <AnimatedTouchableOpacity 234 + exiting={!isWeb ? FadeOut : undefined} 235 + testID="editProfileCancelBtn" 236 + style={s.mt5} 237 + onPress={onPressCancel} 238 + accessibilityRole="button" 239 + accessibilityLabel={_(msg`Cancel profile editing`)} 240 + accessibilityHint="" 241 + onAccessibilityEscape={onPressCancel}> 242 + <View style={[styles.btn]}> 243 + <Text style={[s.black, s.bold, pal.text]}> 244 + <Trans>Cancel</Trans> 245 + </Text> 246 + </View> 247 + </AnimatedTouchableOpacity> 248 + )} 249 + </View> 250 + </ScrollView> 251 + </KeyboardAvoidingView> 252 + ) 253 + } 254 + 255 + const styles = StyleSheet.create({ 256 + title: { 257 + textAlign: 'center', 258 + fontWeight: '600', 259 + fontSize: 24, 260 + marginBottom: 18, 261 + }, 262 + label: { 263 + fontWeight: '600', 264 + paddingHorizontal: 4, 265 + paddingBottom: 4, 266 + marginTop: 20, 267 + }, 268 + form: { 269 + paddingHorizontal: 14, 270 + }, 271 + textInput: { 272 + borderWidth: 1, 273 + borderRadius: 6, 274 + paddingHorizontal: 14, 275 + paddingVertical: 10, 276 + fontSize: 16, 277 + }, 278 + textArea: { 279 + borderWidth: 1, 280 + borderRadius: 6, 281 + paddingHorizontal: 12, 282 + paddingTop: 10, 283 + fontSize: 16, 284 + height: 120, 285 + textAlignVertical: 'top', 286 + }, 287 + btn: { 288 + flexDirection: 'row', 289 + alignItems: 'center', 290 + justifyContent: 'center', 291 + width: '100%', 292 + borderRadius: 32, 293 + padding: 10, 294 + marginBottom: 10, 295 + }, 296 + avi: { 297 + position: 'absolute', 298 + top: 80, 299 + left: 24, 300 + width: 84, 301 + height: 84, 302 + borderWidth: 2, 303 + borderRadius: 42, 304 + }, 305 + photos: { 306 + marginBottom: 36, 307 + marginHorizontal: -14, 308 + }, 309 + errorContainer: {marginTop: 20}, 310 + })
+5 -1
src/view/com/modals/Modal.tsx
··· 13 13 import * as ChangePasswordModal from './ChangePassword' 14 14 import * as CreateOrEditListModal from './CreateOrEditList' 15 15 import * as DeleteAccountModal from './DeleteAccount' 16 + import * as EditProfileModal from './EditProfile' 16 17 import * as InAppBrowserConsentModal from './InAppBrowserConsent' 17 18 import * as InviteCodesModal from './InviteCodes' 18 19 import * as ContentLanguagesSettingsModal from './lang-settings/ContentLanguagesSettings' ··· 53 54 54 55 let snapPoints: (string | number)[] = DEFAULT_SNAPPOINTS 55 56 let element 56 - if (activeModal?.name === 'create-or-edit-list') { 57 + if (activeModal?.name === 'edit-profile') { 58 + snapPoints = EditProfileModal.snapPoints 59 + element = <EditProfileModal.Component {...activeModal} /> 60 + } else if (activeModal?.name === 'create-or-edit-list') { 57 61 snapPoints = CreateOrEditListModal.snapPoints 58 62 element = <CreateOrEditListModal.Component {...activeModal} /> 59 63 } else if (activeModal?.name === 'user-add-remove-lists') {
+4 -1
src/view/com/modals/Modal.web.tsx
··· 14 14 import * as CreateOrEditListModal from './CreateOrEditList' 15 15 import * as CropImageModal from './CropImage.web' 16 16 import * as DeleteAccountModal from './DeleteAccount' 17 + import * as EditProfileModal from './EditProfile' 17 18 import * as InviteCodesModal from './InviteCodes' 18 19 import * as ContentLanguagesSettingsModal from './lang-settings/ContentLanguagesSettings' 19 20 import * as PostLanguagesSettingsModal from './lang-settings/PostLanguagesSettings' ··· 61 62 } 62 63 63 64 let element 64 - if (modal.name === 'create-or-edit-list') { 65 + if (modal.name === 'edit-profile') { 66 + element = <EditProfileModal.Component {...modal} /> 67 + } else if (modal.name === 'create-or-edit-list') { 65 68 element = <CreateOrEditListModal.Component {...modal} /> 66 69 } else if (modal.name === 'user-add-remove-lists') { 67 70 element = <UserAddRemoveLists.Component {...modal} />