Add avatar images and fix some type signatures
This commit is contained in:
parent
273e6d2973
commit
539bf5d350
56 changed files with 543 additions and 370 deletions
|
@ -205,6 +205,7 @@ export const ComposePost = observer(function ComposePost({
|
|||
<UserAvatar
|
||||
handle={replyTo.author.handle}
|
||||
displayName={replyTo.author.displayName}
|
||||
avatar={replyTo.author.avatar}
|
||||
size={50}
|
||||
/>
|
||||
<View style={styles.replyToPost}>
|
||||
|
@ -223,6 +224,7 @@ export const ComposePost = observer(function ComposePost({
|
|||
<UserAvatar
|
||||
handle={store.me.handle || ''}
|
||||
displayName={store.me.displayName}
|
||||
avatar={store.me.avatar}
|
||||
size={50}
|
||||
/>
|
||||
<TextInput
|
||||
|
|
|
@ -29,6 +29,7 @@ export function ComposePrompt({
|
|||
size={50}
|
||||
handle={store.me.handle || ''}
|
||||
displayName={store.me.displayName}
|
||||
avatar={store.me.avatar}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
) : undefined}
|
||||
|
|
|
@ -149,6 +149,7 @@ const User = ({
|
|||
size={40}
|
||||
displayName={item.displayName}
|
||||
handle={item.handle}
|
||||
avatar={item.avatar}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.actorContent}>
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import React, {useState} from 'react'
|
||||
import {ComAtprotoBlobUpload} from '../../../third-party/api/index'
|
||||
import * as Toast from '../util/Toast'
|
||||
import {StyleSheet, Text, TouchableOpacity, View} from 'react-native'
|
||||
import LinearGradient from 'react-native-linear-gradient'
|
||||
import {BottomSheetScrollView, BottomSheetTextInput} from '@gorhom/bottom-sheet'
|
||||
import {Image as PickedImage} from 'react-native-image-crop-picker'
|
||||
import {ErrorMessage} from '../util/ErrorMessage'
|
||||
import {useStores} from '../../../state'
|
||||
import {ProfileViewModel} from '../../../state/models/profile-view'
|
||||
|
@ -12,7 +14,6 @@ import {
|
|||
MAX_DISPLAY_NAME,
|
||||
MAX_DESCRIPTION,
|
||||
} from '../../../lib/strings'
|
||||
import * as Profile from '../../../third-party/api/src/client/types/app/bsky/actor/profile'
|
||||
import {UserBanner} from '../util/UserBanner'
|
||||
import {UserAvatar} from '../util/UserAvatar'
|
||||
|
||||
|
@ -36,40 +37,44 @@ export function Component({
|
|||
const [userBanner, setUserBanner] = useState<string | null>(
|
||||
profileView.userBanner,
|
||||
)
|
||||
const [userAvatar, setUserAvatar] = useState<string | null>(
|
||||
profileView.userAvatar,
|
||||
const [userAvatar, setUserAvatar] = useState<string | undefined>(
|
||||
profileView.avatar,
|
||||
)
|
||||
const [newUserAvatar, setNewUserAvatar] = useState<PickedImage | undefined>()
|
||||
const onPressCancel = () => {
|
||||
store.shell.closeModal()
|
||||
}
|
||||
const onSelectNewAvatar = (img: PickedImage) => {
|
||||
console.log(img)
|
||||
setNewUserAvatar(img)
|
||||
setUserAvatar(img.path)
|
||||
}
|
||||
const onPressSave = async () => {
|
||||
if (error) {
|
||||
setError('')
|
||||
}
|
||||
try {
|
||||
await profileView.updateProfile(
|
||||
(existing?: Profile.Record): Profile.Record => {
|
||||
if (existing) {
|
||||
existing.displayName = displayName
|
||||
existing.description = description
|
||||
return existing
|
||||
}
|
||||
return {
|
||||
displayName,
|
||||
description,
|
||||
}
|
||||
{
|
||||
displayName,
|
||||
description,
|
||||
},
|
||||
userAvatar, // TEMP
|
||||
newUserAvatar,
|
||||
userBanner, // TEMP
|
||||
)
|
||||
Toast.show('Profile updated')
|
||||
onUpdate?.()
|
||||
store.shell.closeModal()
|
||||
} catch (e: any) {
|
||||
console.error(e)
|
||||
setError(
|
||||
'Failed to save your profile. Check your internet connection and try again.',
|
||||
)
|
||||
if (e instanceof ComAtprotoBlobUpload.InvalidBlobError) {
|
||||
setError(e.message)
|
||||
} else {
|
||||
// TODO replace when error detection is correct
|
||||
setError(e.message)
|
||||
// setError(
|
||||
// 'Failed to save your profile. Check your internet connection and try again.',
|
||||
// )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,15 +91,15 @@ export function Component({
|
|||
<View style={styles.avi}>
|
||||
<UserAvatar
|
||||
size={80}
|
||||
userAvatar={userAvatar}
|
||||
avatar={userAvatar}
|
||||
handle={profileView.handle}
|
||||
setUserAvatar={setUserAvatar}
|
||||
onSelectNewAvatar={onSelectNewAvatar}
|
||||
displayName={profileView.displayName}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
{error !== '' && (
|
||||
<View style={s.mb10}>
|
||||
<View style={{marginTop: 20}}>
|
||||
<ErrorMessage message={error} />
|
||||
</View>
|
||||
)}
|
||||
|
|
|
@ -130,6 +130,7 @@ export const Component = observer(function Component({
|
|||
did={item.did}
|
||||
handle={item.handle}
|
||||
displayName={item.displayName}
|
||||
avatar={item.avatar}
|
||||
renderButton={() =>
|
||||
!createdInvite ? (
|
||||
<>
|
||||
|
@ -162,6 +163,7 @@ export const Component = observer(function Component({
|
|||
did={item.subject.did}
|
||||
handle={item.subject.handle}
|
||||
displayName={item.subject.displayName}
|
||||
avatar={item.subject.avatar}
|
||||
renderButton={() => (
|
||||
<>
|
||||
<FontAwesomeIcon icon="x" style={[s.mr5]} size={14} />
|
||||
|
|
|
@ -139,6 +139,7 @@ export const FeedItem = observer(function FeedItem({
|
|||
size={30}
|
||||
displayName={author.displayName}
|
||||
handle={author.handle}
|
||||
avatar={author.avatar}
|
||||
/>
|
||||
</Link>
|
||||
))}
|
||||
|
|
|
@ -26,6 +26,7 @@ export function InviteAccepter({item}: {item: NotificationsViewItemModel}) {
|
|||
did={item.author.did}
|
||||
handle={item.author.handle}
|
||||
displayName={item.author.displayName}
|
||||
avatar={item.author.avatar}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
|
|
@ -93,6 +93,7 @@ const RepostedByItem = ({item}: {item: RepostedByViewItemModel}) => {
|
|||
size={40}
|
||||
displayName={item.displayName}
|
||||
handle={item.handle}
|
||||
avatar={item.avatar}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.layoutContent}>
|
||||
|
|
|
@ -61,6 +61,7 @@ export const PostThreadItem = observer(function PostThreadItem({
|
|||
author: {
|
||||
handle: item.author.handle,
|
||||
displayName: item.author.displayName,
|
||||
avatar: item.author.avatar,
|
||||
},
|
||||
},
|
||||
onPost: onPostReply,
|
||||
|
@ -113,6 +114,7 @@ export const PostThreadItem = observer(function PostThreadItem({
|
|||
size={50}
|
||||
displayName={item.author.displayName}
|
||||
handle={item.author.handle}
|
||||
avatar={item.author.avatar}
|
||||
/>
|
||||
</Link>
|
||||
</View>
|
||||
|
@ -236,6 +238,7 @@ export const PostThreadItem = observer(function PostThreadItem({
|
|||
<UserAvatar
|
||||
handle={item.replyingTo.author.handle}
|
||||
displayName={item.replyingTo.author.displayName}
|
||||
avatar={item.replyingTo.author.avatar}
|
||||
size={30}
|
||||
/>
|
||||
</View>
|
||||
|
@ -251,6 +254,7 @@ export const PostThreadItem = observer(function PostThreadItem({
|
|||
size={50}
|
||||
displayName={item.author.displayName}
|
||||
handle={item.author.handle}
|
||||
avatar={item.author.avatar}
|
||||
/>
|
||||
</Link>
|
||||
</View>
|
||||
|
|
|
@ -93,6 +93,7 @@ const LikedByItem = ({item}: {item: VotesViewItemModel}) => {
|
|||
size={40}
|
||||
displayName={item.actor.displayName}
|
||||
handle={item.actor.handle}
|
||||
avatar={item.actor.avatar}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.layoutContent}>
|
||||
|
|
|
@ -97,6 +97,7 @@ export const Post = observer(function Post({
|
|||
author: {
|
||||
handle: item.author.handle,
|
||||
displayName: item.author.displayName,
|
||||
avatar: item.author.avatar,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -137,6 +138,7 @@ export const Post = observer(function Post({
|
|||
size={50}
|
||||
displayName={item.author.displayName}
|
||||
handle={item.author.handle}
|
||||
avatar={item.author.avatar}
|
||||
/>
|
||||
</Link>
|
||||
</View>
|
||||
|
|
|
@ -54,6 +54,7 @@ export const FeedItem = observer(function FeedItem({
|
|||
author: {
|
||||
handle: item.author.handle,
|
||||
displayName: item.author.displayName,
|
||||
avatar: item.author.avatar,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -139,6 +140,7 @@ export const FeedItem = observer(function FeedItem({
|
|||
displayName={
|
||||
item.additionalParentPost?.thread?.author.displayName
|
||||
}
|
||||
avatar={item.additionalParentPost?.thread?.author.avatar}
|
||||
size={32}
|
||||
/>
|
||||
</View>
|
||||
|
@ -159,6 +161,7 @@ export const FeedItem = observer(function FeedItem({
|
|||
size={item._isThreadChild ? 30 : 50}
|
||||
displayName={item.author.displayName}
|
||||
handle={item.author.handle}
|
||||
avatar={item.author.avatar}
|
||||
/>
|
||||
</Link>
|
||||
</View>
|
||||
|
|
|
@ -8,14 +8,14 @@ export function ProfileCard({
|
|||
did,
|
||||
handle,
|
||||
displayName,
|
||||
description,
|
||||
avatar,
|
||||
renderButton,
|
||||
onPressButton,
|
||||
}: {
|
||||
did: string
|
||||
handle: string
|
||||
displayName?: string
|
||||
description?: string
|
||||
avatar?: string
|
||||
renderButton?: () => JSX.Element
|
||||
onPressButton?: () => void
|
||||
}) {
|
||||
|
@ -23,7 +23,12 @@ export function ProfileCard({
|
|||
<Link style={styles.outer} href={`/profile/${handle}`} title={handle}>
|
||||
<View style={styles.layout}>
|
||||
<View style={styles.layoutAvi}>
|
||||
<UserAvatar size={40} displayName={displayName} handle={handle} />
|
||||
<UserAvatar
|
||||
size={40}
|
||||
displayName={displayName}
|
||||
handle={handle}
|
||||
avatar={avatar}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.layoutContent}>
|
||||
<Text style={[s.f16, s.bold]} numberOfLines={1}>
|
||||
|
|
|
@ -91,6 +91,7 @@ const User = ({item}: {item: FollowerItem}) => {
|
|||
size={40}
|
||||
displayName={item.displayName}
|
||||
handle={item.handle}
|
||||
avatar={item.avatar}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.layoutContent}>
|
||||
|
|
|
@ -91,6 +91,7 @@ const User = ({item}: {item: FollowItem}) => {
|
|||
size={40}
|
||||
displayName={item.displayName}
|
||||
handle={item.handle}
|
||||
avatar={item.avatar}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.layoutContent}>
|
||||
|
|
|
@ -158,7 +158,7 @@ export const ProfileHeader = observer(function ProfileHeader({
|
|||
size={80}
|
||||
handle={view.handle}
|
||||
displayName={view.displayName}
|
||||
userAvatar={view.userAvatar}
|
||||
avatar={view.avatar}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.content}>
|
||||
|
|
|
@ -65,6 +65,7 @@ export const ProfileMembers = observer(function ProfileMembers({
|
|||
did={item.did}
|
||||
handle={item.handle}
|
||||
displayName={item.displayName}
|
||||
avatar={item.avatar}
|
||||
/>
|
||||
)
|
||||
return (
|
||||
|
|
|
@ -6,23 +6,23 @@ import {
|
|||
openCamera,
|
||||
openCropper,
|
||||
openPicker,
|
||||
Image as PickedImage,
|
||||
} 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,
|
||||
handle,
|
||||
userAvatar,
|
||||
avatar,
|
||||
displayName,
|
||||
setUserAvatar,
|
||||
onSelectNewAvatar,
|
||||
}: {
|
||||
size: number
|
||||
handle: string
|
||||
displayName: string | undefined
|
||||
userAvatar?: string | null
|
||||
setUserAvatar?: React.Dispatch<React.SetStateAction<string | null>>
|
||||
avatar?: string | null
|
||||
onSelectNewAvatar?: (img: PickedImage) => void
|
||||
}) {
|
||||
const initials = getInitials(displayName || handle)
|
||||
const gradient = getGradient(handle)
|
||||
|
@ -35,14 +35,12 @@ export function UserAvatar({
|
|||
openCamera({
|
||||
mediaType: 'photo',
|
||||
cropping: true,
|
||||
width: 80,
|
||||
height: 80,
|
||||
width: 400,
|
||||
height: 400,
|
||||
cropperCircleOverlay: true,
|
||||
}).then(item => {
|
||||
if (setUserAvatar != null) {
|
||||
setUserAvatar(item.path)
|
||||
}
|
||||
})
|
||||
forceJpg: true, // ios only
|
||||
compressImageQuality: 0.7,
|
||||
}).then(onSelectNewAvatar)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -54,19 +52,17 @@ export function UserAvatar({
|
|||
await openCropper({
|
||||
mediaType: 'photo',
|
||||
path: item.path,
|
||||
width: 80,
|
||||
height: 80,
|
||||
width: 400,
|
||||
height: 400,
|
||||
cropperCircleOverlay: true,
|
||||
}).then(croppedItem => {
|
||||
if (setUserAvatar != null) {
|
||||
setUserAvatar(croppedItem.path)
|
||||
}
|
||||
})
|
||||
forceJpg: true, // ios only
|
||||
compressImageQuality: 0.7,
|
||||
}).then(onSelectNewAvatar)
|
||||
})
|
||||
},
|
||||
},
|
||||
])
|
||||
}, [setUserAvatar])
|
||||
}, [onSelectNewAvatar])
|
||||
|
||||
const renderSvg = (size: number, initials: string) => (
|
||||
<Svg width={size} height={size} viewBox="0 0 100 100">
|
||||
|
@ -89,11 +85,14 @@ export function UserAvatar({
|
|||
</Svg>
|
||||
)
|
||||
|
||||
// setUserAvatar is only passed as prop on the EditProfile component
|
||||
return setUserAvatar != null && IMAGES_ENABLED ? (
|
||||
// onSelectNewAvatar is only passed as prop on the EditProfile component
|
||||
return onSelectNewAvatar ? (
|
||||
<TouchableOpacity onPress={handleEditAvatar}>
|
||||
{userAvatar ? (
|
||||
<Image style={styles.avatarImage} source={{uri: userAvatar}} />
|
||||
{avatar ? (
|
||||
<Image
|
||||
style={{width: size, height: size, borderRadius: (size / 2) | 0}}
|
||||
source={{uri: avatar}}
|
||||
/>
|
||||
) : (
|
||||
renderSvg(size, initials)
|
||||
)}
|
||||
|
@ -105,11 +104,11 @@ export function UserAvatar({
|
|||
/>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
) : userAvatar ? (
|
||||
) : avatar ? (
|
||||
<Image
|
||||
style={styles.avatarImage}
|
||||
style={{width: size, height: size, borderRadius: (size / 2) | 0}}
|
||||
resizeMode="stretch"
|
||||
source={{uri: userAvatar}}
|
||||
source={{uri: avatar}}
|
||||
/>
|
||||
) : (
|
||||
renderSvg(size, initials)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue