about summary refs log tree commit diff
path: root/src/view/com/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/util')
-rw-r--r--src/view/com/util/UserAvatar.tsx108
-rw-r--r--src/view/com/util/UserBanner.tsx107
2 files changed, 208 insertions, 7 deletions
diff --git a/src/view/com/util/UserAvatar.tsx b/src/view/com/util/UserAvatar.tsx
index 9016cc4cc..2ed161253 100644
--- a/src/view/com/util/UserAvatar.tsx
+++ b/src/view/com/util/UserAvatar.tsx
@@ -1,19 +1,74 @@
-import React from 'react'
+import React, {useCallback} from 'react'
+import {StyleSheet, View, TouchableOpacity, Alert, Image} from 'react-native'
 import Svg, {Circle, Text, Defs, LinearGradient, Stop} from 'react-native-svg'
+import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import {
+  openCamera,
+  openCropper,
+  openPicker,
+} from 'react-native-image-crop-picker'
 import {getGradient} from '../../lib/asset-gen'
+import {colors} from '../../lib/styles'
+import {IMAGES_ENABLED} from '../../../build-flags'
 
 export function UserAvatar({
   size,
-  displayName,
   handle,
+  userAvatar,
+  displayName,
+  setUserAvatar,
 }: {
   size: number
-  displayName: string | undefined
   handle: string
+  displayName: string | undefined
+  userAvatar: string | null
+  setUserAvatar?: React.Dispatch<React.SetStateAction<string | null>>
 }) {
   const initials = getInitials(displayName || handle)
   const gradient = getGradient(handle)
-  return (
+
+  const handleEditAvatar = useCallback(() => {
+    Alert.alert('Select upload method', '', [
+      {
+        text: 'Take a new photo',
+        onPress: () => {
+          openCamera({
+            mediaType: 'photo',
+            cropping: true,
+            width: 80,
+            height: 80,
+            cropperCircleOverlay: true,
+          }).then(item => {
+            if (setUserAvatar != null) {
+              setUserAvatar(item.path)
+            }
+          })
+        },
+      },
+      {
+        text: 'Select from gallery',
+        onPress: () => {
+          openPicker({
+            mediaType: 'photo',
+          }).then(async item => {
+            await openCropper({
+              mediaType: 'photo',
+              path: item.path,
+              width: 80,
+              height: 80,
+              cropperCircleOverlay: true,
+            }).then(croppedItem => {
+              if (setUserAvatar != null) {
+                setUserAvatar(croppedItem.path)
+              }
+            })
+          })
+        },
+      },
+    ])
+  }, [setUserAvatar])
+
+  const renderSvg = (size: number, initials: string) => (
     <Svg width={size} height={size} viewBox="0 0 100 100">
       <Defs>
         <LinearGradient id="grad" x1="0" y1="0" x2="1" y2="1">
@@ -33,6 +88,32 @@ export function UserAvatar({
       </Text>
     </Svg>
   )
+
+  // setUserAvatar is only passed as prop on the EditProfile component
+  return setUserAvatar != null && IMAGES_ENABLED ? (
+    <TouchableOpacity onPress={handleEditAvatar}>
+      {userAvatar != null ? (
+        <Image style={styles.avatarImage} source={{uri: userAvatar}} />
+      ) : (
+        renderSvg(size, initials)
+      )}
+      <View style={styles.editButtonContainer}>
+        <FontAwesomeIcon
+          icon="camera"
+          size={12}
+          style={{color: colors.white}}
+        />
+      </View>
+    </TouchableOpacity>
+  ) : userAvatar != null ? (
+    <Image
+      style={styles.avatarImage}
+      resizeMode="stretch"
+      source={{uri: userAvatar}}
+    />
+  ) : (
+    renderSvg(size, initials)
+  )
 }
 
 function getInitials(str: string): string {
@@ -50,3 +131,22 @@ function getInitials(str: string): string {
   }
   return 'X'
 }
+
+const styles = StyleSheet.create({
+  editButtonContainer: {
+    position: 'absolute',
+    width: 24,
+    height: 24,
+    bottom: 0,
+    right: 0,
+    borderRadius: 12,
+    alignItems: 'center',
+    justifyContent: 'center',
+    backgroundColor: colors.gray5,
+  },
+  avatarImage: {
+    width: 80,
+    height: 80,
+    borderRadius: 40,
+  },
+})
diff --git a/src/view/com/util/UserBanner.tsx b/src/view/com/util/UserBanner.tsx
index 16e18c84d..c0421fe12 100644
--- a/src/view/com/util/UserBanner.tsx
+++ b/src/view/com/util/UserBanner.tsx
@@ -1,10 +1,67 @@
-import React from 'react'
+import React, {useCallback} from 'react'
+import {StyleSheet, View, TouchableOpacity, Alert, Image} from 'react-native'
 import Svg, {Rect, Defs, LinearGradient, Stop} from 'react-native-svg'
+import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {getGradient} from '../../lib/asset-gen'
+import {colors} from '../../lib/styles'
+import {
+  openCamera,
+  openCropper,
+  openPicker,
+} from 'react-native-image-crop-picker'
+import {IMAGES_ENABLED} from '../../../build-flags'
 
-export function UserBanner({handle}: {handle: string}) {
+export function UserBanner({
+  handle,
+  userBanner,
+  setUserBanner,
+}: {
+  handle: string
+  userBanner: string | null
+  setUserBanner?: React.Dispatch<React.SetStateAction<string | null>>
+}) {
   const gradient = getGradient(handle)
-  return (
+
+  const handleEditBanner = useCallback(() => {
+    Alert.alert('Select upload method', '', [
+      {
+        text: 'Take a new photo',
+        onPress: () => {
+          openCamera({
+            mediaType: 'photo',
+            cropping: true,
+            width: 1500,
+            height: 500,
+          }).then(item => {
+            if (setUserBanner != null) {
+              setUserBanner(item.path)
+            }
+          })
+        },
+      },
+      {
+        text: 'Select from gallery',
+        onPress: () => {
+          openPicker({
+            mediaType: 'photo',
+          }).then(async item => {
+            await openCropper({
+              mediaType: 'photo',
+              path: item.path,
+              width: 1500,
+              height: 500,
+            }).then(croppedItem => {
+              if (setUserBanner != null) {
+                setUserBanner(croppedItem.path)
+              }
+            })
+          })
+        },
+      },
+    ])
+  }, [setUserBanner])
+
+  const renderSvg = () => (
     <Svg width="100%" height="120" viewBox="50 0 200 100">
       <Defs>
         <LinearGradient id="grad" x1="0" y1="0" x2="1" y2="1">
@@ -20,4 +77,48 @@ export function UserBanner({handle}: {handle: string}) {
       <Rect x="0" y="0" width="400" height="100" fill="url(#grad2)" />
     </Svg>
   )
+
+  // setUserBanner is only passed as prop on the EditProfile component
+  return setUserBanner != null && IMAGES_ENABLED ? (
+    <TouchableOpacity onPress={handleEditBanner}>
+      {userBanner != null ? (
+        <Image style={styles.bannerImage} source={{uri: userBanner}} />
+      ) : (
+        renderSvg()
+      )}
+      <View style={styles.editButtonContainer}>
+        <FontAwesomeIcon
+          icon="camera"
+          size={12}
+          style={{color: colors.white}}
+        />
+      </View>
+    </TouchableOpacity>
+  ) : userBanner != null ? (
+    <Image
+      style={styles.bannerImage}
+      resizeMode="stretch"
+      source={{uri: userBanner}}
+    />
+  ) : (
+    renderSvg()
+  )
 }
+
+const styles = StyleSheet.create({
+  editButtonContainer: {
+    position: 'absolute',
+    width: 24,
+    height: 24,
+    bottom: 8,
+    right: 8,
+    borderRadius: 12,
+    alignItems: 'center',
+    justifyContent: 'center',
+    backgroundColor: colors.gray5,
+  },
+  bannerImage: {
+    width: '100%',
+    height: 120,
+  },
+})