refactor profile pictures's state

This commit is contained in:
João Ferreiro
2022-12-06 14:50:20 +00:00
parent 90d851c0c5
commit ed6bcfdb75
5 changed files with 62 additions and 63 deletions
+12 -14
View File
@@ -6,7 +6,6 @@ import {extractEntities} from '../../lib/strings'
import {Declaration} from './_common'
import {RootStoreModel} from './root-store'
import * as apilib from '../lib/api'
import {Image} from 'react-native-image-crop-picker'
export const ACTOR_TYPE_USER = 'app.bsky.system.actorUser'
export const ACTOR_TYPE_SCENE = 'app.bsky.system.actorScene'
@@ -44,13 +43,13 @@ export class ProfileViewModel {
postsCount: number = 0
myState = new ProfileViewMyStateModel()
// data to be implemented in the protocol
userAvatar: string | null = null
userBanner: string | null = null
// added data
descriptionEntities?: Entity[]
// temp state while imagery is not supported by the protocol
userAvatar: Image | null = null
userBanner: Image | null = null
constructor(
public rootStore: RootStoreModel,
params: GetProfile.QueryParams,
@@ -120,22 +119,21 @@ export class ProfileViewModel {
}
}
async updateProfile(fn: (existing?: Profile.Record) => Profile.Record) {
async updateProfile(
fn: (existing?: Profile.Record) => Profile.Record,
userAvatar: string | null,
userBanner: string | null,
) {
await apilib.updateProfile(this.rootStore, this.did, fn)
// add userBanner & userAvatar in the protocol when suported
this.userAvatar = userAvatar
this.userBanner = userBanner
await this.refresh()
}
// state transitions
// =
updateUserAvatar(img: Image) {
this.userAvatar = img
}
updateUserBanner(img: Image) {
this.userBanner = img
}
private _xLoading(isRefreshing = false) {
this.isLoading = true
this.isRefreshing = isRefreshing
+12 -4
View File
@@ -33,6 +33,12 @@ export function Component({
const [description, setDescription] = useState<string>(
profileView.description || '',
)
const [userBanner, setUserBanner] = useState<string | null>(
profileView.userBanner,
)
const [userAvatar, setUserAvatar] = useState<string | null>(
profileView.userAvatar,
)
const onPressCancel = () => {
store.shell.closeModal()
}
@@ -53,6 +59,8 @@ export function Component({
description,
}
},
userAvatar,
userBanner,
)
Toast.show('Profile updated')
onUpdate?.()
@@ -72,17 +80,17 @@ export function Component({
<View style={styles.photos}>
<UserBanner
isMe
isEditable
userBanner={userBanner}
setUserBanner={setUserBanner}
handle={profileView.handle}
profileView={profileView}
/>
<View style={styles.avi}>
<UserAvatar
isMe
isEditable
size={80}
userAvatar={userAvatar}
handle={profileView.handle}
profileView={profileView}
setUserAvatar={setUserAvatar}
displayName={profileView.displayName}
/>
</View>
+6 -2
View File
@@ -152,14 +152,18 @@ export const ProfileHeader = observer(function ProfileHeader({
}
return (
<View style={styles.outer}>
<UserBanner isMe={isMe} handle={view.handle} profileView={view} />
<UserBanner
isMe={isMe}
handle={view.handle}
userBanner={view.userBanner}
/>
<View style={styles.avi}>
<UserAvatar
size={80}
isMe={isMe}
handle={view.handle}
displayName={view.displayName}
profileView={view}
userAvatar={view.userAvatar}
/>
</View>
<View style={styles.content}>
+18 -21
View File
@@ -1,4 +1,4 @@
import React, {useCallback, useState} 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'
@@ -10,28 +10,25 @@ import {
import {getGradient} from '../../lib/asset-gen'
import {colors} from '../../lib/styles'
import {IMAGES_ENABLED} from '../../../build-flags'
import {ProfileViewModel} from '../../../state/models/profile-view'
export function UserAvatar({
size,
handle,
profileView,
userAvatar,
displayName,
isMe = false,
isEditable = false,
setUserAvatar,
}: {
size: number
handle: string
isMe?: boolean
isEditable?: boolean
displayName: string | undefined
profileView: ProfileViewModel
userAvatar: string | null
setUserAvatar?: React.Dispatch<React.SetStateAction<string | null>>
}) {
const initials = getInitials(displayName || handle)
const gradient = getGradient(handle)
const [tempUserAvatar, setTempUserAvatar] = useState<string | null>(null)
const handleEditAvatar = useCallback(() => {
Alert.alert('Select upload method', '', [
{
@@ -44,8 +41,9 @@ export function UserAvatar({
height: 80,
cropperCircleOverlay: true,
}).then(item => {
profileView.updateUserAvatar(item)
setTempUserAvatar(item.path)
if (setUserAvatar != null) {
setUserAvatar(item.path)
}
})
},
},
@@ -62,14 +60,15 @@ export function UserAvatar({
height: 80,
cropperCircleOverlay: true,
}).then(croppedItem => {
profileView.updateUserAvatar(croppedItem)
setTempUserAvatar(croppedItem.path)
if (setUserAvatar != null) {
setUserAvatar(croppedItem.path)
}
})
})
},
},
])
}, [profileView])
}, [setUserAvatar])
const renderSvg = (size: number, initials: string) => (
<Svg width={size} height={size} viewBox="0 0 100 100">
@@ -92,14 +91,12 @@ export function UserAvatar({
</Svg>
)
return isEditable && IMAGES_ENABLED ? (
// setUserAvatar is only passed as prop on the EditProfile component
return setUserAvatar != null && IMAGES_ENABLED ? (
<TouchableOpacity onPress={handleEditAvatar}>
{/* Added a react state temporary photo while the protocol does not support imagery */}
{profileView.userAvatar != null || tempUserAvatar != null ? (
<Image
style={styles.avatarImage}
source={{uri: tempUserAvatar ?? profileView.userAvatar?.path}}
/>
{userAvatar != null ? (
<Image style={styles.avatarImage} source={{uri: userAvatar}} />
) : (
renderSvg(size, initials)
)}
@@ -111,11 +108,11 @@ export function UserAvatar({
/>
</View>
</TouchableOpacity>
) : isMe && profileView.userAvatar != null ? (
) : isMe && userAvatar != null ? (
<Image
style={styles.avatarImage}
resizeMode="stretch"
source={{uri: profileView.userAvatar.path}}
source={{uri: userAvatar}}
/>
) : (
renderSvg(size, initials)
+14 -22
View File
@@ -1,4 +1,4 @@
import React, {useCallback, useState} 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'
@@ -10,23 +10,20 @@ import {
openPicker,
} from 'react-native-image-crop-picker'
import {IMAGES_ENABLED} from '../../../build-flags'
import {ProfileViewModel} from '../../../state/models/profile-view'
export function UserBanner({
handle,
profileView,
userBanner,
isMe = false,
isEditable = false,
setUserBanner,
}: {
handle: string
isMe?: boolean
profileView: ProfileViewModel
isEditable?: boolean
userBanner: string | null
setUserBanner?: React.Dispatch<React.SetStateAction<string | null>>
}) {
const gradient = getGradient(handle)
const [tempUserBanner, setTempUserBanner] = useState<string | null>(null)
const handleEditBanner = useCallback(() => {
Alert.alert('Select upload method', '', [
{
@@ -38,8 +35,7 @@ export function UserBanner({
width: 1500,
height: 500,
}).then(item => {
profileView.updateUserBanner(item)
setTempUserBanner(item.path)
setUserBanner(item.path)
})
},
},
@@ -55,14 +51,13 @@ export function UserBanner({
width: 1500,
height: 500,
}).then(croppedItem => {
profileView.updateUserBanner(croppedItem)
setTempUserBanner(croppedItem.path)
setUserBanner(croppedItem.path)
})
})
},
},
])
}, [profileView])
}, [setUserBanner])
const renderSvg = () => (
<Svg width="100%" height="120" viewBox="50 0 200 100">
@@ -81,15 +76,12 @@ export function UserBanner({
</Svg>
)
return isEditable && IMAGES_ENABLED ? (
// setUserBanner is only passed as prop on the EditProfile component
return setUserBanner != null && IMAGES_ENABLED ? (
<TouchableOpacity onPress={handleEditBanner}>
{/* Added a react state temporary photo while the protocol does not support imagery */}
{profileView.userBanner != null || tempUserBanner != null ? (
<Image
style={styles.bannerImage}
resizeMode="stretch"
source={{uri: tempUserBanner ?? profileView.userBanner?.path}}
/>
{userBanner != null ? (
<Image style={styles.bannerImage} source={{uri: userBanner}} />
) : (
renderSvg()
)}
@@ -101,11 +93,11 @@ export function UserBanner({
/>
</View>
</TouchableOpacity>
) : isMe && profileView.userBanner != null ? (
) : isMe && userBanner != null ? (
<Image
style={styles.bannerImage}
resizeMode="stretch"
source={{uri: profileView.userBanner.path}}
source={{uri: userBanner}}
/>
) : (
renderSvg()