Shadow refactoring and improvements (#1959)

* Make shadow a type-only concept

* Prevent unnecessary init state recalc

* Use derived state instead of effects

* Batch emitter updates

* Use object first seen time instead of dataUpdatedAt

* Stop threading dataUpdatedAt through

* Use same value consistently
zio/stable
dan 2023-11-21 22:42:30 +00:00 committed by GitHub
parent f18b9b32b0
commit 4c4ba553bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 115 additions and 203 deletions

View File

@ -0,0 +1 @@
export {unstable_batchedUpdates as batchedUpdates} from 'react-native'

View File

@ -0,0 +1,2 @@
// @ts-ignore
export {unstable_batchedUpdates as batchedUpdates} from 'react-dom'

View File

@ -1,7 +1,8 @@
import {useEffect, useState, useMemo, useCallback, useRef} from 'react'
import {useEffect, useState, useMemo, useCallback} from 'react'
import EventEmitter from 'eventemitter3'
import {AppBskyFeedDefs} from '@atproto/api'
import {Shadow} from './types'
import {batchedUpdates} from '#/lib/batchedUpdates'
import {Shadow, castAsShadow} from './types'
export type {Shadow} from './types'
const emitter = new EventEmitter()
@ -21,15 +22,36 @@ interface CacheEntry {
value: PostShadow
}
const firstSeenMap = new WeakMap<AppBskyFeedDefs.PostView, number>()
function getFirstSeenTS(post: AppBskyFeedDefs.PostView): number {
let timeStamp = firstSeenMap.get(post)
if (timeStamp !== undefined) {
return timeStamp
}
timeStamp = Date.now()
firstSeenMap.set(post, timeStamp)
return timeStamp
}
export function usePostShadow(
post: AppBskyFeedDefs.PostView,
ifAfterTS: number,
): Shadow<AppBskyFeedDefs.PostView> | typeof POST_TOMBSTONE {
const [state, setState] = useState<CacheEntry>({
ts: Date.now(),
const postSeenTS = getFirstSeenTS(post)
const [state, setState] = useState<CacheEntry>(() => ({
ts: postSeenTS,
value: fromPost(post),
}))
const [prevPost, setPrevPost] = useState(post)
if (post !== prevPost) {
// if we got a new prop, assume it's fresher
// than whatever shadow state we accumulated
setPrevPost(post)
setState({
ts: postSeenTS,
value: fromPost(post),
})
const firstRun = useRef(true)
}
const onUpdate = useCallback(
(value: Partial<PostShadow>) => {
@ -46,30 +68,17 @@ export function usePostShadow(
}
}, [post.uri, onUpdate])
// react to post updates
useEffect(() => {
// dont fire on first run to avoid needless re-renders
if (!firstRun.current) {
setState({ts: Date.now(), value: fromPost(post)})
}
firstRun.current = false
}, [post])
return useMemo(() => {
return state.ts > ifAfterTS
return state.ts > postSeenTS
? mergeShadow(post, state.value)
: {...post, isShadowed: true}
}, [post, state, ifAfterTS])
: castAsShadow(post)
}, [post, state, postSeenTS])
}
export function updatePostShadow(uri: string, value: Partial<PostShadow>) {
batchedUpdates(() => {
emitter.emit(uri, value)
}
export function isPostShadowed(
v: AppBskyFeedDefs.PostView | Shadow<AppBskyFeedDefs.PostView>,
): v is Shadow<AppBskyFeedDefs.PostView> {
return 'isShadowed' in v && !!v.isShadowed
})
}
function fromPost(post: AppBskyFeedDefs.PostView): PostShadow {
@ -89,7 +98,7 @@ function mergeShadow(
if (shadow.isDeleted) {
return POST_TOMBSTONE
}
return {
return castAsShadow({
...post,
likeCount: shadow.likeCount,
repostCount: shadow.repostCount,
@ -98,6 +107,5 @@ function mergeShadow(
like: shadow.likeUri,
repost: shadow.repostUri,
},
isShadowed: true,
}
})
}

View File

@ -1,7 +1,8 @@
import {useEffect, useState, useMemo, useCallback, useRef} from 'react'
import {useEffect, useState, useMemo, useCallback} from 'react'
import EventEmitter from 'eventemitter3'
import {AppBskyActorDefs} from '@atproto/api'
import {Shadow} from './types'
import {batchedUpdates} from '#/lib/batchedUpdates'
import {Shadow, castAsShadow} from './types'
export type {Shadow} from './types'
const emitter = new EventEmitter()
@ -22,15 +23,34 @@ type ProfileView =
| AppBskyActorDefs.ProfileViewBasic
| AppBskyActorDefs.ProfileViewDetailed
export function useProfileShadow(
profile: ProfileView,
ifAfterTS: number,
): Shadow<ProfileView> {
const [state, setState] = useState<CacheEntry>({
ts: Date.now(),
const firstSeenMap = new WeakMap<ProfileView, number>()
function getFirstSeenTS(profile: ProfileView): number {
let timeStamp = firstSeenMap.get(profile)
if (timeStamp !== undefined) {
return timeStamp
}
timeStamp = Date.now()
firstSeenMap.set(profile, timeStamp)
return timeStamp
}
export function useProfileShadow(profile: ProfileView): Shadow<ProfileView> {
const profileSeenTS = getFirstSeenTS(profile)
const [state, setState] = useState<CacheEntry>(() => ({
ts: profileSeenTS,
value: fromProfile(profile),
}))
const [prevProfile, setPrevProfile] = useState(profile)
if (profile !== prevProfile) {
// if we got a new prop, assume it's fresher
// than whatever shadow state we accumulated
setPrevProfile(profile)
setState({
ts: profileSeenTS,
value: fromProfile(profile),
})
const firstRun = useRef(true)
}
const onUpdate = useCallback(
(value: Partial<ProfileShadow>) => {
@ -47,33 +67,20 @@ export function useProfileShadow(
}
}, [profile.did, onUpdate])
// react to profile updates
useEffect(() => {
// dont fire on first run to avoid needless re-renders
if (!firstRun.current) {
setState({ts: Date.now(), value: fromProfile(profile)})
}
firstRun.current = false
}, [profile])
return useMemo(() => {
return state.ts > ifAfterTS
return state.ts > profileSeenTS
? mergeShadow(profile, state.value)
: {...profile, isShadowed: true}
}, [profile, state, ifAfterTS])
: castAsShadow(profile)
}, [profile, state, profileSeenTS])
}
export function updateProfileShadow(
uri: string,
value: Partial<ProfileShadow>,
) {
batchedUpdates(() => {
emitter.emit(uri, value)
}
export function isProfileShadowed<T extends ProfileView>(
v: T | Shadow<T>,
): v is Shadow<T> {
return 'isShadowed' in v && !!v.isShadowed
})
}
function fromProfile(profile: ProfileView): ProfileShadow {
@ -88,7 +95,7 @@ function mergeShadow(
profile: ProfileView,
shadow: ProfileShadow,
): Shadow<ProfileView> {
return {
return castAsShadow({
...profile,
viewer: {
...(profile.viewer || {}),
@ -96,6 +103,5 @@ function mergeShadow(
muted: shadow.muted,
blocking: shadow.blockingUri,
},
isShadowed: true,
}
})
}

View File

@ -1 +1,7 @@
export type Shadow<T> = T & {isShadowed: true}
// This isn't a real property, but it prevents T being compatible with Shadow<T>.
declare const shadowTag: unique symbol
export type Shadow<T> = T & {[shadowTag]: true}
export function castAsShadow<T>(value: T): Shadow<T> {
return value as any as Shadow<T>
}

View File

@ -24,7 +24,7 @@ export function RecommendedFollows({next}: Props) {
const pal = usePalette('default')
const {_} = useLingui()
const {isTabletOrMobile} = useWebMediaQueries()
const {data: suggestedFollows, dataUpdatedAt} = useSuggestedFollowsQuery()
const {data: suggestedFollows} = useSuggestedFollowsQuery()
const getSuggestedFollowsByActor = useGetSuggestedFollowersByActor()
const [additionalSuggestions, setAdditionalSuggestions] = React.useState<{
[did: string]: AppBskyActorDefs.ProfileView[]
@ -162,7 +162,6 @@ export function RecommendedFollows({next}: Props) {
renderItem={({item}) => (
<RecommendedFollowsItem
profile={item}
dataUpdatedAt={dataUpdatedAt}
onFollowStateChange={onFollowStateChange}
moderation={moderateProfile(item, moderationOpts)}
/>
@ -197,7 +196,6 @@ export function RecommendedFollows({next}: Props) {
renderItem={({item}) => (
<RecommendedFollowsItem
profile={item}
dataUpdatedAt={dataUpdatedAt}
onFollowStateChange={onFollowStateChange}
moderation={moderateProfile(item, moderationOpts)}
/>

View File

@ -18,7 +18,6 @@ import {logger} from '#/logger'
type Props = {
profile: AppBskyActorDefs.ProfileViewBasic
dataUpdatedAt: number
moderation: ProfileModeration
onFollowStateChange: (props: {
did: string
@ -28,13 +27,12 @@ type Props = {
export function RecommendedFollowsItem({
profile,
dataUpdatedAt,
moderation,
onFollowStateChange,
}: React.PropsWithChildren<Props>) {
const pal = usePalette('default')
const {isMobile} = useWebMediaQueries()
const shadowedProfile = useProfileShadow(profile, dataUpdatedAt)
const shadowedProfile = useProfileShadow(profile)
return (
<Animated.View

View File

@ -64,7 +64,6 @@ export function ListMembers({
const {
data,
dataUpdatedAt,
isFetching,
isFetched,
isError,
@ -185,7 +184,6 @@ export function ListMembers({
(item as AppBskyGraphDefs.ListItemView).subject.handle
}`}
profile={(item as AppBskyGraphDefs.ListItemView).subject}
dataUpdatedAt={dataUpdatedAt}
renderButton={renderMemberButton}
style={{paddingHorizontal: isMobile ? 8 : 14, paddingVertical: 4}}
/>
@ -198,7 +196,6 @@ export function ListMembers({
onPressTryAgain,
onPressRetryLoadMore,
isMobile,
dataUpdatedAt,
],
)

View File

@ -22,7 +22,6 @@ export function Component({did}: {did: string}) {
const moderationOpts = useModerationOpts()
const {
data: profile,
dataUpdatedAt,
error: profileError,
refetch: refetchProfile,
isFetching: isFetchingProfile,
@ -51,13 +50,7 @@ export function Component({did}: {did: string}) {
)
}
if (profile && moderationOpts) {
return (
<ComponentLoaded
profile={profile}
dataUpdatedAt={dataUpdatedAt}
moderationOpts={moderationOpts}
/>
)
return <ComponentLoaded profile={profile} moderationOpts={moderationOpts} />
}
// should never happen
return (
@ -71,15 +64,13 @@ export function Component({did}: {did: string}) {
function ComponentLoaded({
profile: profileUnshadowed,
dataUpdatedAt,
moderationOpts,
}: {
profile: AppBskyActorDefs.ProfileViewDetailed
dataUpdatedAt: number
moderationOpts: ModerationOpts
}) {
const pal = usePalette('default')
const profile = useProfileShadow(profileUnshadowed, dataUpdatedAt)
const profile = useProfileShadow(profileUnshadowed)
const {screen} = useAnalytics()
const moderation = React.useMemo(
() => moderateProfile(profile, moderationOpts),

View File

@ -38,7 +38,6 @@ export function Feed({
const {markAllRead} = useUnreadNotificationsApi()
const {
data,
dataUpdatedAt,
isLoading,
isFetching,
isFetched,
@ -132,15 +131,9 @@ export function Feed({
} else if (item === LOADING_ITEM) {
return <NotificationFeedLoadingPlaceholder />
}
return (
<FeedItem
item={item}
dataUpdatedAt={dataUpdatedAt}
moderationOpts={moderationOpts!}
/>
)
return <FeedItem item={item} moderationOpts={moderationOpts!} />
},
[onPressRetryLoadMore, dataUpdatedAt, moderationOpts],
[onPressRetryLoadMore, moderationOpts],
)
const showHeaderSpinner = !isPTRing && isFetching && !isLoading

View File

@ -58,11 +58,9 @@ interface Author {
let FeedItem = ({
item,
dataUpdatedAt,
moderationOpts,
}: {
item: FeedNotification
dataUpdatedAt: number
moderationOpts: ModerationOpts
}): React.ReactNode => {
const pal = usePalette('default')
@ -135,7 +133,6 @@ let FeedItem = ({
accessible={false}>
<Post
post={item.subject}
dataUpdatedAt={dataUpdatedAt}
style={
item.notification.isRead
? undefined

View File

@ -20,7 +20,6 @@ export function PostLikedBy({uri}: {uri: string}) {
} = useResolveUriQuery(uri)
const {
data,
dataUpdatedAt,
isFetching,
isFetched,
isFetchingNextPage,
@ -55,18 +54,11 @@ export function PostLikedBy({uri}: {uri: string}) {
}
}, [isFetching, hasNextPage, isError, fetchNextPage])
const renderItem = useCallback(
({item}: {item: GetLikes.Like}) => {
const renderItem = useCallback(({item}: {item: GetLikes.Like}) => {
return (
<ProfileCardWithFollowBtn
key={item.actor.did}
profile={item.actor}
dataUpdatedAt={dataUpdatedAt}
/>
)
},
[dataUpdatedAt],
<ProfileCardWithFollowBtn key={item.actor.did} profile={item.actor} />
)
}, [])
if (isFetchingResolvedUri || !isFetched) {
return (

View File

@ -20,7 +20,6 @@ export function PostRepostedBy({uri}: {uri: string}) {
} = useResolveUriQuery(uri)
const {
data,
dataUpdatedAt,
isFetching,
isFetched,
isFetchingNextPage,
@ -57,15 +56,9 @@ export function PostRepostedBy({uri}: {uri: string}) {
const renderItem = useCallback(
({item}: {item: ActorDefs.ProfileViewBasic}) => {
return (
<ProfileCardWithFollowBtn
key={item.did}
profile={item}
dataUpdatedAt={dataUpdatedAt}
/>
)
return <ProfileCardWithFollowBtn key={item.did} profile={item} />
},
[dataUpdatedAt],
[],
)
if (isFetchingResolvedUri || !isFetched) {

View File

@ -73,7 +73,6 @@ export function PostThread({
refetch,
isRefetching,
data: thread,
dataUpdatedAt,
} = usePostThreadQuery(uri)
const {data: preferences} = usePreferencesQuery()
const rootPost = thread?.type === 'post' ? thread.post : undefined
@ -111,7 +110,6 @@ export function PostThread({
<PostThreadLoaded
thread={thread}
isRefetching={isRefetching}
dataUpdatedAt={dataUpdatedAt}
threadViewPrefs={preferences.threadViewPrefs}
onRefresh={refetch}
onPressReply={onPressReply}
@ -122,14 +120,12 @@ export function PostThread({
function PostThreadLoaded({
thread,
isRefetching,
dataUpdatedAt,
threadViewPrefs,
onRefresh,
onPressReply,
}: {
thread: ThreadNode
isRefetching: boolean
dataUpdatedAt: number
threadViewPrefs: UsePreferencesQueryResponse['threadViewPrefs']
onRefresh: () => void
onPressReply: () => void
@ -295,7 +291,6 @@ function PostThreadLoaded({
<PostThreadItem
post={item.post}
record={item.record}
dataUpdatedAt={dataUpdatedAt}
treeView={threadViewPrefs.lab_treeViewEnabled || false}
depth={item.ctx.depth}
isHighlightedPost={item.ctx.isHighlightedPost}
@ -322,7 +317,6 @@ function PostThreadLoaded({
posts,
onRefresh,
threadViewPrefs.lab_treeViewEnabled,
dataUpdatedAt,
_,
],
)

View File

@ -45,7 +45,6 @@ import {Shadow, usePostShadow, POST_TOMBSTONE} from '#/state/cache/post-shadow'
export function PostThreadItem({
post,
record,
dataUpdatedAt,
treeView,
depth,
isHighlightedPost,
@ -57,7 +56,6 @@ export function PostThreadItem({
}: {
post: AppBskyFeedDefs.PostView
record: AppBskyFeedPost.Record
dataUpdatedAt: number
treeView: boolean
depth: number
isHighlightedPost?: boolean
@ -68,7 +66,7 @@ export function PostThreadItem({
onPostReply: () => void
}) {
const moderationOpts = useModerationOpts()
const postShadowed = usePostShadow(post, dataUpdatedAt)
const postShadowed = usePostShadow(post)
const richText = useMemo(
() =>
new RichTextAPI({

View File

@ -30,12 +30,10 @@ import {Shadow, usePostShadow, POST_TOMBSTONE} from '#/state/cache/post-shadow'
export function Post({
post,
dataUpdatedAt,
showReplyLine,
style,
}: {
post: AppBskyFeedDefs.PostView
dataUpdatedAt: number
showReplyLine?: boolean
style?: StyleProp<ViewStyle>
}) {
@ -48,7 +46,7 @@ export function Post({
: undefined,
[post],
)
const postShadowed = usePostShadow(post, dataUpdatedAt)
const postShadowed = usePostShadow(post)
const richText = useMemo(
() =>
record

View File

@ -76,7 +76,6 @@ let Feed = ({
const opts = React.useMemo(() => ({enabled}), [enabled])
const {
data,
dataUpdatedAt,
isFetching,
isFetched,
isError,
@ -200,7 +199,6 @@ let Feed = ({
return (
<FeedSlice
slice={item}
dataUpdatedAt={dataUpdatedAt}
// we check for this before creating the feedItems array
moderationOpts={moderationOpts!}
/>
@ -208,7 +206,6 @@ let Feed = ({
},
[
feed,
dataUpdatedAt,
error,
onPressTryAgain,
onPressRetryLoadMore,

View File

@ -40,7 +40,6 @@ export function FeedItem({
record,
reason,
moderation,
dataUpdatedAt,
isThreadChild,
isThreadLastChild,
isThreadParent,
@ -49,12 +48,11 @@ export function FeedItem({
record: AppBskyFeedPost.Record
reason: AppBskyFeedDefs.ReasonRepost | ReasonFeedSource | undefined
moderation: PostModeration
dataUpdatedAt: number
isThreadChild?: boolean
isThreadLastChild?: boolean
isThreadParent?: boolean
}) {
const postShadowed = usePostShadow(post, dataUpdatedAt)
const postShadowed = usePostShadow(post)
const richText = useMemo(
() =>
new RichTextAPI({

View File

@ -11,12 +11,10 @@ import {makeProfileLink} from 'lib/routes/links'
let FeedSlice = ({
slice,
dataUpdatedAt,
ignoreFilterFor,
moderationOpts,
}: {
slice: FeedPostSlice
dataUpdatedAt: number
ignoreFilterFor?: string
moderationOpts: ModerationOpts
}): React.ReactNode => {
@ -44,7 +42,6 @@ let FeedSlice = ({
record={slice.items[0].record}
reason={slice.items[0].reason}
moderation={moderations[0]}
dataUpdatedAt={dataUpdatedAt}
isThreadParent={isThreadParentAt(slice.items, 0)}
isThreadChild={isThreadChildAt(slice.items, 0)}
/>
@ -54,7 +51,6 @@ let FeedSlice = ({
record={slice.items[1].record}
reason={slice.items[1].reason}
moderation={moderations[1]}
dataUpdatedAt={dataUpdatedAt}
isThreadParent={isThreadParentAt(slice.items, 1)}
isThreadChild={isThreadChildAt(slice.items, 1)}
/>
@ -65,7 +61,6 @@ let FeedSlice = ({
record={slice.items[last].record}
reason={slice.items[last].reason}
moderation={moderations[last]}
dataUpdatedAt={dataUpdatedAt}
isThreadParent={isThreadParentAt(slice.items, last)}
isThreadChild={isThreadChildAt(slice.items, last)}
isThreadLastChild
@ -83,7 +78,6 @@ let FeedSlice = ({
record={slice.items[i].record}
reason={slice.items[i].reason}
moderation={moderations[i]}
dataUpdatedAt={dataUpdatedAt}
isThreadParent={isThreadParentAt(slice.items, i)}
isThreadChild={isThreadChildAt(slice.items, i)}
isThreadLastChild={

View File

@ -27,7 +27,6 @@ import {useSession} from '#/state/session'
export function ProfileCard({
testID,
profile: profileUnshadowed,
dataUpdatedAt,
noBg,
noBorder,
followers,
@ -36,7 +35,6 @@ export function ProfileCard({
}: {
testID?: string
profile: AppBskyActorDefs.ProfileViewBasic
dataUpdatedAt: number
noBg?: boolean
noBorder?: boolean
followers?: AppBskyActorDefs.ProfileView[] | undefined
@ -46,7 +44,7 @@ export function ProfileCard({
style?: StyleProp<ViewStyle>
}) {
const pal = usePalette('default')
const profile = useProfileShadow(profileUnshadowed, dataUpdatedAt)
const profile = useProfileShadow(profileUnshadowed)
const moderationOpts = useModerationOpts()
if (!moderationOpts) {
return null
@ -202,13 +200,11 @@ export function ProfileCardWithFollowBtn({
noBg,
noBorder,
followers,
dataUpdatedAt,
}: {
profile: AppBskyActorDefs.ProfileViewBasic
noBg?: boolean
noBorder?: boolean
followers?: AppBskyActorDefs.ProfileView[] | undefined
dataUpdatedAt: number
}) {
const {currentAccount} = useSession()
const isMe = profile.did === currentAccount?.did
@ -224,7 +220,6 @@ export function ProfileCardWithFollowBtn({
? undefined
: profileShadow => <FollowButton profile={profileShadow} />
}
dataUpdatedAt={dataUpdatedAt}
/>
)
}

View File

@ -20,7 +20,6 @@ export function ProfileFollowers({name}: {name: string}) {
} = useResolveDidQuery(name)
const {
data,
dataUpdatedAt,
isFetching,
isFetched,
isFetchingNextPage,
@ -58,13 +57,9 @@ export function ProfileFollowers({name}: {name: string}) {
const renderItem = React.useCallback(
({item}: {item: ActorDefs.ProfileViewBasic}) => (
<ProfileCardWithFollowBtn
key={item.did}
profile={item}
dataUpdatedAt={dataUpdatedAt}
/>
<ProfileCardWithFollowBtn key={item.did} profile={item} />
),
[dataUpdatedAt],
[],
)
if (isFetchingDid || !isFetched) {

View File

@ -20,7 +20,6 @@ export function ProfileFollows({name}: {name: string}) {
} = useResolveDidQuery(name)
const {
data,
dataUpdatedAt,
isFetching,
isFetched,
isFetchingNextPage,
@ -58,13 +57,9 @@ export function ProfileFollows({name}: {name: string}) {
const renderItem = React.useCallback(
({item}: {item: ActorDefs.ProfileViewBasic}) => (
<ProfileCardWithFollowBtn
key={item.did}
profile={item}
dataUpdatedAt={dataUpdatedAt}
/>
<ProfileCardWithFollowBtn key={item.did} profile={item} />
),
[dataUpdatedAt],
[],
)
if (isFetchingDid || !isFetched) {

View File

@ -65,7 +65,7 @@ export function ProfileHeaderSuggestedFollows({
}
}, [active, animatedHeight, track])
const {isLoading, data, dataUpdatedAt} = useSuggestedFollowsByActorQuery({
const {isLoading, data} = useSuggestedFollowsByActorQuery({
did: actorDid,
})
@ -127,11 +127,7 @@ export function ProfileHeaderSuggestedFollows({
</>
) : data ? (
data.suggestions.map(profile => (
<SuggestedFollow
key={profile.did}
profile={profile}
dataUpdatedAt={dataUpdatedAt}
/>
<SuggestedFollow key={profile.did} profile={profile} />
))
) : (
<View />
@ -196,15 +192,13 @@ function SuggestedFollowSkeleton() {
function SuggestedFollow({
profile: profileUnshadowed,
dataUpdatedAt,
}: {
profile: AppBskyActorDefs.ProfileView
dataUpdatedAt: number
}) {
const {track} = useAnalytics()
const pal = usePalette('default')
const moderationOpts = useModerationOpts()
const profile = useProfileShadow(profileUnshadowed, dataUpdatedAt)
const profile = useProfileShadow(profileUnshadowed)
const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(profile)
const onPressFollow = React.useCallback(async () => {

View File

@ -40,7 +40,6 @@ export const ModerationBlockedAccounts = withAuthRequired(
const [isPTRing, setIsPTRing] = React.useState(false)
const {
data,
dataUpdatedAt,
isFetching,
isError,
error,
@ -95,7 +94,6 @@ export const ModerationBlockedAccounts = withAuthRequired(
testID={`blockedAccount-${index}`}
key={item.did}
profile={item}
dataUpdatedAt={dataUpdatedAt}
/>
)
return (

View File

@ -40,7 +40,6 @@ export const ModerationMutedAccounts = withAuthRequired(
const [isPTRing, setIsPTRing] = React.useState(false)
const {
data,
dataUpdatedAt,
isFetching,
isError,
error,
@ -95,7 +94,6 @@ export const ModerationMutedAccounts = withAuthRequired(
testID={`mutedAccount-${index}`}
key={item.did}
profile={item}
dataUpdatedAt={dataUpdatedAt}
/>
)
return (

View File

@ -57,7 +57,6 @@ export const ProfileScreen = withAuthRequired(
} = useResolveDidQuery(name)
const {
data: profile,
dataUpdatedAt,
error: profileError,
refetch: refetchProfile,
isFetching: isFetchingProfile,
@ -100,7 +99,6 @@ export const ProfileScreen = withAuthRequired(
return (
<ProfileScreenLoaded
profile={profile}
dataUpdatedAt={dataUpdatedAt}
moderationOpts={moderationOpts}
hideBackButton={!!route.params.hideBackButton}
/>
@ -125,16 +123,14 @@ export const ProfileScreen = withAuthRequired(
function ProfileScreenLoaded({
profile: profileUnshadowed,
dataUpdatedAt,
moderationOpts,
hideBackButton,
}: {
profile: AppBskyActorDefs.ProfileViewDetailed
dataUpdatedAt: number
moderationOpts: ModerationOpts
hideBackButton: boolean
}) {
const profile = useProfileShadow(profileUnshadowed, dataUpdatedAt)
const profile = useProfileShadow(profileUnshadowed)
const {currentAccount} = useSession()
const setMinimalShellMode = useSetMinimalShellMode()
const {openComposer} = useComposerControls()

View File

@ -111,7 +111,6 @@ function EmptyState({message, error}: {message: string; error?: string}) {
function SearchScreenSuggestedFollows() {
const pal = usePalette('default')
const {currentAccount} = useSession()
const [dataUpdatedAt, setDataUpdatedAt] = React.useState(0)
const [suggestions, setSuggestions] = React.useState<
AppBskyActorDefs.ProfileViewBasic[]
>([])
@ -141,7 +140,6 @@ function SearchScreenSuggestedFollows() {
)
setSuggestions(Array.from(friendsOfFriends.values()))
setDataUpdatedAt(Date.now())
}
try {
@ -151,23 +149,12 @@ function SearchScreenSuggestedFollows() {
error: e,
})
}
}, [
currentAccount,
setSuggestions,
setDataUpdatedAt,
getSuggestedFollowsByActor,
])
}, [currentAccount, setSuggestions, getSuggestedFollowsByActor])
return suggestions.length ? (
<FlatList
data={suggestions}
renderItem={({item}) => (
<ProfileCardWithFollowBtn
profile={item}
noBg
dataUpdatedAt={dataUpdatedAt}
/>
)}
renderItem={({item}) => <ProfileCardWithFollowBtn profile={item} noBg />}
keyExtractor={item => item.did}
// @ts-ignore web only -prf
desktopFixedHeight
@ -205,7 +192,6 @@ function SearchScreenPostResults({query}: {query: string}) {
fetchNextPage,
isFetchingNextPage,
hasNextPage,
dataUpdatedAt,
} = useSearchPostsQuery({query})
const onPullToRefresh = React.useCallback(async () => {
@ -258,7 +244,7 @@ function SearchScreenPostResults({query}: {query: string}) {
data={items}
renderItem={({item}) => {
if (item.type === 'post') {
return <Post post={item.post} dataUpdatedAt={dataUpdatedAt} />
return <Post post={item.post} />
} else {
return <Loader />
}
@ -291,7 +277,6 @@ function SearchScreenPostResults({query}: {query: string}) {
function SearchScreenUserResults({query}: {query: string}) {
const {_} = useLingui()
const [isFetched, setIsFetched] = React.useState(false)
const [dataUpdatedAt, setDataUpdatedAt] = React.useState(0)
const [results, setResults] = React.useState<
AppBskyActorDefs.ProfileViewBasic[]
>([])
@ -302,7 +287,6 @@ function SearchScreenUserResults({query}: {query: string}) {
const searchResults = await search({query, limit: 30})
if (searchResults) {
setDataUpdatedAt(Date.now())
setResults(results)
setIsFetched(true)
}
@ -314,7 +298,7 @@ function SearchScreenUserResults({query}: {query: string}) {
setResults([])
setIsFetched(false)
}
}, [query, setDataUpdatedAt, search, results])
}, [query, search, results])
return isFetched ? (
<>
@ -322,11 +306,7 @@ function SearchScreenUserResults({query}: {query: string}) {
<FlatList
data={results}
renderItem={({item}) => (
<ProfileCardWithFollowBtn
profile={item}
noBg
dataUpdatedAt={dataUpdatedAt}
/>
<ProfileCardWithFollowBtn profile={item} noBg />
)}
keyExtractor={item => item.did}
// @ts-ignore web only -prf