memoize things, clean code, and replace deprecated resizeMode with contentFit (#526)
parent
1b356556c9
commit
c5222db38b
|
@ -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={[
|
||||||
|
|
|
@ -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}
|
||||||
|
|
Loading…
Reference in New Issue