memoize things, clean code, and replace deprecated resizeMode with contentFit (#526)

zio/stable
Ansh 2023-04-24 16:45:02 -07:00 committed by GitHub
parent 1b356556c9
commit c5222db38b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 97 additions and 80 deletions

View File

@ -1,4 +1,4 @@
import React from 'react' import React, {useMemo, useState, useEffect} from 'react'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import { import {
Animated, Animated,
@ -47,8 +47,8 @@ export const FeedItem = observer(function FeedItem({
item: NotificationsFeedItemModel item: NotificationsFeedItemModel
}) { }) {
const pal = usePalette('default') const pal = usePalette('default')
const [isAuthorsExpanded, setAuthorsExpanded] = React.useState<boolean>(false) const [isAuthorsExpanded, setAuthorsExpanded] = useState<boolean>(false)
const itemHref = React.useMemo(() => { const itemHref = useMemo(() => {
if (item.isLike || item.isRepost) { if (item.isLike || item.isRepost) {
const urip = new AtUri(item.subjectUri) const urip = new AtUri(item.subjectUri)
return `/profile/${urip.host}/post/${urip.rkey}` return `/profile/${urip.host}/post/${urip.rkey}`
@ -60,7 +60,7 @@ export const FeedItem = observer(function FeedItem({
} }
return '' return ''
}, [item]) }, [item])
const itemTitle = React.useMemo(() => { const itemTitle = useMemo(() => {
if (item.isLike || item.isRepost) { if (item.isLike || item.isRepost) {
return 'Post' return 'Post'
} else if (item.isFollow) { } else if (item.isFollow) {
@ -74,6 +74,35 @@ export const FeedItem = observer(function FeedItem({
setAuthorsExpanded(!isAuthorsExpanded) setAuthorsExpanded(!isAuthorsExpanded)
} }
const authors: Author[] = useMemo(() => {
return [
{
href: `/profile/${item.author.handle}`,
handle: item.author.handle,
displayName: item.author.displayName,
avatar: item.author.avatar,
labels: item.author.labels,
},
...(item.additional?.map(
({author: {avatar, labels, handle, displayName}}) => {
return {
href: `/profile/${handle}`,
handle,
displayName,
avatar,
labels,
}
},
) || []),
]
}, [
item.additional,
item.author.avatar,
item.author.displayName,
item.author.handle,
item.author.labels,
])
if (item.additionalPost?.notFound) { if (item.additionalPost?.notFound) {
// don't render anything if the target post was deleted or unfindable // don't render anything if the target post was deleted or unfindable
return <View /> return <View />
@ -125,30 +154,9 @@ export const FeedItem = observer(function FeedItem({
icon = 'user-plus' icon = 'user-plus'
iconStyle = [s.blue3 as FontAwesomeIconStyle] iconStyle = [s.blue3 as FontAwesomeIconStyle]
} else { } else {
return <></> return null
} }
const authors: Author[] = [
{
href: `/profile/${item.author.handle}`,
handle: item.author.handle,
displayName: item.author.displayName,
avatar: item.author.avatar,
labels: item.author.labels,
},
...(item.additional?.map(
({author: {avatar, labels, handle, displayName}}) => {
return {
href: `/profile/${handle}`,
handle,
displayName,
avatar,
labels,
}
},
) || []),
]
return ( return (
<Link <Link
testID={`feedItem-by-${item.author.handle}`} testID={`feedItem-by-${item.author.handle}`}
@ -301,13 +309,14 @@ function ExpandedAuthorsList({
const heightStyle = { const heightStyle = {
height: Animated.multiply(heightInterp, targetHeight), height: Animated.multiply(heightInterp, targetHeight),
} }
React.useEffect(() => { useEffect(() => {
Animated.timing(heightInterp, { Animated.timing(heightInterp, {
toValue: visible ? 1 : 0, toValue: visible ? 1 : 0,
duration: 200, duration: 200,
useNativeDriver: false, useNativeDriver: false,
}).start() }).start()
}, [heightInterp, visible]) }, [heightInterp, visible])
return ( return (
<Animated.View <Animated.View
style={[ style={[

View File

@ -1,4 +1,4 @@
import React from 'react' import React, {useMemo} from 'react'
import {StyleSheet, View} from 'react-native' import {StyleSheet, View} from 'react-native'
import Svg, {Circle, Path} from 'react-native-svg' import Svg, {Circle, Path} from 'react-native-svg'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
@ -53,61 +53,69 @@ export function UserAvatar({
const {requestCameraAccessIfNeeded} = useCameraPermission() const {requestCameraAccessIfNeeded} = useCameraPermission()
const {requestPhotoAccessIfNeeded} = usePhotoLibraryPermission() const {requestPhotoAccessIfNeeded} = usePhotoLibraryPermission()
const dropdownItems = [ const dropdownItems = useMemo(
!isWeb && { () => [
testID: 'changeAvatarCameraBtn', !isWeb && {
label: 'Camera', testID: 'changeAvatarCameraBtn',
icon: 'camera' as IconProp, label: 'Camera',
onPress: async () => { icon: 'camera' as IconProp,
if (!(await requestCameraAccessIfNeeded())) { onPress: async () => {
return if (!(await requestCameraAccessIfNeeded())) {
} return
onSelectNewAvatar?.( }
await openCamera(store, { onSelectNewAvatar?.(
width: 1000, await openCamera(store, {
height: 1000, width: 1000,
cropperCircleOverlay: true, height: 1000,
}), cropperCircleOverlay: true,
) }),
)
},
}, },
}, {
{ testID: 'changeAvatarLibraryBtn',
testID: 'changeAvatarLibraryBtn', label: 'Library',
label: 'Library', icon: 'image' as IconProp,
icon: 'image' as IconProp, onPress: async () => {
onPress: async () => { if (!(await requestPhotoAccessIfNeeded())) {
if (!(await requestPhotoAccessIfNeeded())) { return
return }
} const items = await openPicker(store, {
const items = await openPicker(store, {
mediaType: 'photo',
multiple: false,
})
onSelectNewAvatar?.(
await openCropper(store, {
mediaType: 'photo', mediaType: 'photo',
path: items[0].path, multiple: false,
width: 1000, })
height: 1000,
cropperCircleOverlay: true,
}),
)
},
},
{
testID: 'changeAvatarRemoveBtn',
label: 'Remove',
icon: ['far', 'trash-can'] as IconProp,
onPress: async () => {
onSelectNewAvatar?.(null)
},
},
]
const warning = React.useMemo(() => { onSelectNewAvatar?.(
await openCropper(store, {
mediaType: 'photo',
path: items[0].path,
width: 1000,
height: 1000,
cropperCircleOverlay: true,
}),
)
},
},
{
testID: 'changeAvatarRemoveBtn',
label: 'Remove',
icon: ['far', 'trash-can'] as IconProp,
onPress: async () => {
onSelectNewAvatar?.(null)
},
},
],
[
onSelectNewAvatar,
requestCameraAccessIfNeeded,
requestPhotoAccessIfNeeded,
store,
],
)
const warning = useMemo(() => {
if (!hasWarning) { if (!hasWarning) {
return <></> return null
} }
return ( return (
<View style={[styles.warningIconContainer, pal.view]}> <View style={[styles.warningIconContainer, pal.view]}>
@ -156,7 +164,7 @@ export function UserAvatar({
<HighPriorityImage <HighPriorityImage
testID="userAvatarImage" testID="userAvatarImage"
style={{width: size, height: size, borderRadius: Math.floor(size / 2)}} style={{width: size, height: size, borderRadius: Math.floor(size / 2)}}
resizeMode="stretch" contentFit="cover"
source={{uri: avatar}} source={{uri: avatar}}
/> />
{warning} {warning}