Replaces the alert with dropdown for profile image and banner (#123)

* replaces the alert with dropdown for profile image and banner

* lint
This commit is contained in:
Aryan Goharzad
2023-01-31 13:07:06 -05:00
committed by GitHub
parent 9302940a23
commit b2e7a90565
3 changed files with 118 additions and 82 deletions
+56 -41
View File
@@ -1,5 +1,5 @@
import React, {useCallback} from 'react'
import {Alert, Image, StyleSheet, TouchableOpacity, View} from 'react-native'
import React from 'react'
import {Image, StyleSheet, View} from 'react-native'
import Svg, {Circle, Text, Defs, LinearGradient, Stop} from 'react-native-svg'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {
@@ -9,6 +9,7 @@ import {
Image as PickedImage,
} from 'react-native-image-crop-picker'
import {colors, gradients} from '../../lib/styles'
import {DropdownButton} from './forms/DropdownButton'
export function UserAvatar({
size,
@@ -25,43 +26,6 @@ export function UserAvatar({
}) {
const initials = getInitials(displayName || handle)
const handleEditAvatar = useCallback(() => {
Alert.alert('Select upload method', '', [
{
text: 'Take a new photo',
onPress: () => {
openCamera({
mediaType: 'photo',
cropping: true,
width: 2000,
height: 2000,
cropperCircleOverlay: true,
forceJpg: true, // ios only
compressImageQuality: 1,
}).then(onSelectNewAvatar)
},
},
{
text: 'Select from gallery',
onPress: () => {
openPicker({
mediaType: 'photo',
}).then(async item => {
await openCropper({
mediaType: 'photo',
path: item.path,
width: 2000,
height: 2000,
cropperCircleOverlay: true,
forceJpg: true, // ios only
compressImageQuality: 1,
}).then(onSelectNewAvatar)
})
},
},
])
}, [onSelectNewAvatar])
const renderSvg = (svgSize: number, svgInitials: string) => (
<Svg width={svgSize} height={svgSize} viewBox="0 0 100 100">
<Defs>
@@ -83,9 +47,60 @@ export function UserAvatar({
</Svg>
)
const dropdownItems = [
{
label: 'Camera',
icon: 'camera',
// TODO: dark mode icon
onPress: () => {
openCamera({
mediaType: 'photo',
cropping: true,
width: 2000,
height: 2000,
cropperCircleOverlay: true,
forceJpg: true, // ios only
compressImageQuality: 1,
}).then(onSelectNewAvatar)
},
},
{
label: 'Library',
icon: 'image',
onPress: () => {
openPicker({
mediaType: 'photo',
}).then(async item => {
await openCropper({
mediaType: 'photo',
path: item.path,
width: 2000,
height: 2000,
cropperCircleOverlay: true,
forceJpg: true, // ios only
compressImageQuality: 1,
}).then(onSelectNewAvatar)
})
},
},
// TODO: Remove avatar https://github.com/bluesky-social/social-app/issues/122
// {
// label: 'Remove',
// icon: ['far', 'trash-can'],
// onPress: () => {
// // Remove avatar API call
// },
// },
]
// onSelectNewAvatar is only passed as prop on the EditProfile component
return onSelectNewAvatar ? (
<TouchableOpacity onPress={handleEditAvatar}>
<DropdownButton
type="bare"
items={dropdownItems}
openToRight
rightOffset={-10}
bottomOffset={-10}
menuWidth={170}>
{avatar ? (
<Image
style={{
@@ -105,7 +120,7 @@ export function UserAvatar({
style={{color: colors.white}}
/>
</View>
</TouchableOpacity>
</DropdownButton>
) : avatar ? (
<Image
style={{width: size, height: size, borderRadius: Math.floor(size / 2)}}
+50 -34
View File
@@ -1,5 +1,5 @@
import React, {useCallback} from 'react'
import {StyleSheet, View, TouchableOpacity, Alert, Image} from 'react-native'
import React from 'react'
import {StyleSheet, View, Image} from 'react-native'
import Svg, {Rect, Defs, LinearGradient, Stop} from 'react-native-svg'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {Image as PickedImage} from 'react-native-image-crop-picker'
@@ -9,6 +9,7 @@ import {
openCropper,
openPicker,
} from 'react-native-image-crop-picker'
import {DropdownButton} from './forms/DropdownButton'
export function UserBanner({
banner,
@@ -17,14 +18,35 @@ export function UserBanner({
banner?: string | null
onSelectNewBanner?: (img: PickedImage) => void
}) {
const handleEditBanner = useCallback(() => {
Alert.alert('Select upload method', '', [
{
text: 'Take a new photo',
onPress: () => {
openCamera({
const dropdownItems = [
{
label: 'Camera',
icon: 'camera',
// TODO: Add darkmode support https://github.com/bluesky-social/social-app/issues/78
onPress: () => {
openCamera({
mediaType: 'photo',
cropping: true,
compressImageMaxWidth: 6000,
width: 6000,
compressImageMaxHeight: 2000,
height: 2000,
forceJpg: true, // ios only
compressImageQuality: 1,
includeExif: true,
}).then(onSelectNewBanner)
},
},
{
label: 'Library',
icon: 'image',
onPress: () => {
openPicker({
mediaType: 'photo',
}).then(async item => {
await openCropper({
mediaType: 'photo',
cropping: true,
path: item.path,
compressImageMaxWidth: 6000,
width: 6000,
compressImageMaxHeight: 2000,
@@ -33,30 +55,18 @@ export function UserBanner({
compressImageQuality: 1,
includeExif: true,
}).then(onSelectNewBanner)
},
})
},
{
text: 'Select from gallery',
onPress: () => {
openPicker({
mediaType: 'photo',
}).then(async item => {
await openCropper({
mediaType: 'photo',
path: item.path,
compressImageMaxWidth: 6000,
width: 6000,
compressImageMaxHeight: 2000,
height: 2000,
forceJpg: true, // ios only
compressImageQuality: 1,
includeExif: true,
}).then(onSelectNewBanner)
})
},
},
])
}, [onSelectNewBanner])
},
// TODO: Remove banner https://github.com/bluesky-social/social-app/issues/122
// {
// label: 'Remove',
// icon: ['far', 'trash-can'],
// onPress: () => {
// // Remove banner api call
// },
// },
]
const renderSvg = () => (
<Svg width="100%" height="150" viewBox="50 0 200 100">
@@ -77,7 +87,13 @@ export function UserBanner({
// setUserBanner is only passed as prop on the EditProfile component
return onSelectNewBanner ? (
<TouchableOpacity onPress={handleEditBanner}>
<DropdownButton
type="bare"
items={dropdownItems}
openToRight
rightOffset={-200}
bottomOffset={-10}
menuWidth={170}>
{banner ? (
<Image style={styles.bannerImage} source={{uri: banner}} />
) : (
@@ -90,7 +106,7 @@ export function UserBanner({
style={{color: colors.white}}
/>
</View>
</TouchableOpacity>
</DropdownButton>
) : banner ? (
<Image
style={styles.bannerImage}
+12 -7
View File
@@ -36,6 +36,9 @@ export function DropdownButton({
label,
menuWidth,
children,
openToRight = false,
rightOffset = 0,
bottomOffset = 0,
}: {
type: DropdownButtonType
style?: StyleProp<ViewStyle>
@@ -43,6 +46,9 @@ export function DropdownButton({
label?: string
menuWidth?: number
children?: React.ReactNode
openToRight?: boolean
rightOffset?: number
bottomOffset?: number
}) {
const ref = useRef<TouchableOpacity>(null)
@@ -59,12 +65,11 @@ export function DropdownButton({
if (!menuWidth) {
menuWidth = 200
}
createDropdownMenu(
pageX + width - menuWidth,
pageY + height,
menuWidth,
items,
)
const newX = openToRight
? pageX + width + rightOffset
: pageX + width - menuWidth
const newY = pageY + height + bottomOffset
createDropdownMenu(newX, newY, menuWidth, items)
},
)
}
@@ -184,7 +189,7 @@ function createDropdownMenu(
{items.map((item, index) => (
<TouchableOpacity
key={index}
style={[styles.menuItem]}
style={styles.menuItem}
onPress={() => onPressItem(index)}>
{item.icon && (
<FontAwesomeIcon style={styles.icon} icon={item.icon} />