about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/state/models/profile-view.ts22
-rw-r--r--src/view/com/modals/EditProfile.tsx15
-rw-r--r--src/view/com/profile/ProfileHeader.tsx2
-rw-r--r--src/view/com/util/UserBanner.tsx46
4 files changed, 50 insertions, 35 deletions
diff --git a/src/state/models/profile-view.ts b/src/state/models/profile-view.ts
index 787e69e3f..9b32c8744 100644
--- a/src/state/models/profile-view.ts
+++ b/src/state/models/profile-view.ts
@@ -39,15 +39,13 @@ export class ProfileViewModel {
   displayName?: string
   description?: string
   avatar?: string
+  banner?: string
   followersCount: number = 0
   followsCount: number = 0
   membersCount: number = 0
   postsCount: number = 0
   myState = new ProfileViewMyStateModel()
 
-  // TODO TEMP data to be implemented in the protocol
-  userBanner: string | null = null
-
   // added data
   descriptionEntities?: Entity[]
 
@@ -123,11 +121,8 @@ export class ProfileViewModel {
   async updateProfile(
     updates: Profile.Record,
     newUserAvatar: PickedImage | undefined,
-    userBanner: string | null, // TODO TEMP
+    newUserBanner: PickedImage | undefined,
   ) {
-    // TODO TEMP add userBanner to the protocol when suported
-    this.userBanner = userBanner
-
     if (newUserAvatar) {
       const res = await this.rootStore.api.com.atproto.blob.upload(
         newUserAvatar.path, // this will be special-cased by the fetch monkeypatch in /src/state/lib/api.ts
@@ -140,6 +135,18 @@ export class ProfileViewModel {
         mimeType: newUserAvatar.mime,
       }
     }
+    if (newUserBanner) {
+      const res = await this.rootStore.api.com.atproto.blob.upload(
+        newUserBanner.path, // this will be special-cased by the fetch monkeypatch in /src/state/lib/api.ts
+        {
+          encoding: newUserBanner.mime,
+        },
+      )
+      updates.banner = {
+        cid: res.data.cid,
+        mimeType: newUserBanner.mime,
+      }
+    }
     await this.rootStore.api.app.bsky.actor.updateProfile(updates)
     await this.rootStore.me.load()
     await this.refresh()
@@ -187,6 +194,7 @@ export class ProfileViewModel {
     this.displayName = res.data.displayName
     this.description = res.data.description
     this.avatar = res.data.avatar
+    this.banner = res.data.banner
     this.followersCount = res.data.followersCount
     this.followsCount = res.data.followsCount
     this.membersCount = res.data.membersCount
diff --git a/src/view/com/modals/EditProfile.tsx b/src/view/com/modals/EditProfile.tsx
index cf89f1822..dcb0ba834 100644
--- a/src/view/com/modals/EditProfile.tsx
+++ b/src/view/com/modals/EditProfile.tsx
@@ -41,12 +41,13 @@ export function Component({
   const [description, setDescription] = useState<string>(
     profileView.description || '',
   )
-  const [userBanner, setUserBanner] = useState<string | null>(
-    profileView.userBanner,
+  const [userBanner, setUserBanner] = useState<string | undefined>(
+    profileView.banner,
   )
   const [userAvatar, setUserAvatar] = useState<string | undefined>(
     profileView.avatar,
   )
+  const [newUserBanner, setNewUserBanner] = useState<PickedImage | undefined>()
   const [newUserAvatar, setNewUserAvatar] = useState<PickedImage | undefined>()
   const onPressCancel = () => {
     store.shell.closeModal()
@@ -55,6 +56,10 @@ export function Component({
     setNewUserAvatar(img)
     setUserAvatar(img.path)
   }
+  const onSelectNewBanner = (img: PickedImage) => {
+    setNewUserBanner(img)
+    setUserBanner(img.path)
+  }
   const onPressSave = async () => {
     setProcessing(true)
     if (error) {
@@ -67,7 +72,7 @@ export function Component({
           description,
         },
         newUserAvatar,
-        userBanner, // TEMP
+        newUserBanner,
       )
       Toast.show('Profile updated')
       onUpdate?.()
@@ -90,8 +95,8 @@ export function Component({
         <Text style={styles.title}>Edit my profile</Text>
         <View style={styles.photos}>
           <UserBanner
-            userBanner={userBanner}
-            setUserBanner={setUserBanner}
+            banner={userBanner}
+            onSelectNewBanner={onSelectNewBanner}
             handle={profileView.handle}
           />
           <View style={styles.avi}>
diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx
index fb31c1847..eb0a7477c 100644
--- a/src/view/com/profile/ProfileHeader.tsx
+++ b/src/view/com/profile/ProfileHeader.tsx
@@ -152,7 +152,7 @@ export const ProfileHeader = observer(function ProfileHeader({
   }
   return (
     <View style={styles.outer}>
-      <UserBanner handle={view.handle} userBanner={view.userBanner} />
+      <UserBanner handle={view.handle} banner={view.banner} />
       <View style={styles.avi}>
         <UserAvatar
           size={80}
diff --git a/src/view/com/util/UserBanner.tsx b/src/view/com/util/UserBanner.tsx
index b0c6bb165..697e900b1 100644
--- a/src/view/com/util/UserBanner.tsx
+++ b/src/view/com/util/UserBanner.tsx
@@ -2,6 +2,7 @@ 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 {Image as PickedImage} from 'react-native-image-crop-picker'
 import {getGradient} from '../../lib/asset-gen'
 import {colors} from '../../lib/styles'
 import {
@@ -9,16 +10,15 @@ import {
   openCropper,
   openPicker,
 } from 'react-native-image-crop-picker'
-import {IMAGES_ENABLED} from '../../../build-flags'
 
 export function UserBanner({
   handle,
-  userBanner,
-  setUserBanner,
+  banner,
+  onSelectNewBanner,
 }: {
   handle: string
-  userBanner?: string | null
-  setUserBanner?: React.Dispatch<React.SetStateAction<string | null>>
+  banner?: string | null
+  onSelectNewBanner?: (img: PickedImage) => void
 }) {
   const gradient = getGradient(handle)
 
@@ -30,13 +30,14 @@ export function UserBanner({
           openCamera({
             mediaType: 'photo',
             cropping: true,
+            compressImageMaxWidth: 1500,
             width: 1500,
+            compressImageMaxHeight: 500,
             height: 500,
-          }).then(item => {
-            if (setUserBanner != null) {
-              setUserBanner(item.path)
-            }
-          })
+            forceJpg: true, // ios only
+            compressImageQuality: 0.4,
+            includeExif: true,
+          }).then(onSelectNewBanner)
         },
       },
       {
@@ -48,18 +49,19 @@ export function UserBanner({
             await openCropper({
               mediaType: 'photo',
               path: item.path,
+              compressImageMaxWidth: 1500,
               width: 1500,
+              compressImageMaxHeight: 500,
               height: 500,
-            }).then(croppedItem => {
-              if (setUserBanner != null) {
-                setUserBanner(croppedItem.path)
-              }
-            })
+              forceJpg: true, // ios only
+              compressImageQuality: 0.4,
+              includeExif: true,
+            }).then(onSelectNewBanner)
           })
         },
       },
     ])
-  }, [setUserBanner])
+  }, [onSelectNewBanner])
 
   const renderSvg = () => (
     <Svg width="100%" height="120" viewBox="50 0 200 100">
@@ -79,10 +81,10 @@ export function UserBanner({
   )
 
   // setUserBanner is only passed as prop on the EditProfile component
-  return setUserBanner != null && IMAGES_ENABLED ? (
+  return onSelectNewBanner ? (
     <TouchableOpacity onPress={handleEditBanner}>
-      {userBanner ? (
-        <Image style={styles.bannerImage} source={{uri: userBanner}} />
+      {banner ? (
+        <Image style={styles.bannerImage} source={{uri: banner}} />
       ) : (
         renderSvg()
       )}
@@ -94,11 +96,11 @@ export function UserBanner({
         />
       </View>
     </TouchableOpacity>
-  ) : userBanner ? (
+  ) : banner ? (
     <Image
       style={styles.bannerImage}
-      resizeMode="stretch"
-      source={{uri: userBanner}}
+      resizeMode="cover"
+      source={{uri: banner}}
     />
   ) : (
     renderSvg()