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 consistentlyzio/stable
parent
f18b9b32b0
commit
4c4ba553bd
|
@ -0,0 +1 @@
|
||||||
|
export {unstable_batchedUpdates as batchedUpdates} from 'react-native'
|
|
@ -0,0 +1,2 @@
|
||||||
|
// @ts-ignore
|
||||||
|
export {unstable_batchedUpdates as batchedUpdates} from 'react-dom'
|
|
@ -1,7 +1,8 @@
|
||||||
import {useEffect, useState, useMemo, useCallback, useRef} from 'react'
|
import {useEffect, useState, useMemo, useCallback} from 'react'
|
||||||
import EventEmitter from 'eventemitter3'
|
import EventEmitter from 'eventemitter3'
|
||||||
import {AppBskyFeedDefs} from '@atproto/api'
|
import {AppBskyFeedDefs} from '@atproto/api'
|
||||||
import {Shadow} from './types'
|
import {batchedUpdates} from '#/lib/batchedUpdates'
|
||||||
|
import {Shadow, castAsShadow} from './types'
|
||||||
export type {Shadow} from './types'
|
export type {Shadow} from './types'
|
||||||
|
|
||||||
const emitter = new EventEmitter()
|
const emitter = new EventEmitter()
|
||||||
|
@ -21,15 +22,36 @@ interface CacheEntry {
|
||||||
value: PostShadow
|
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(
|
export function usePostShadow(
|
||||||
post: AppBskyFeedDefs.PostView,
|
post: AppBskyFeedDefs.PostView,
|
||||||
ifAfterTS: number,
|
|
||||||
): Shadow<AppBskyFeedDefs.PostView> | typeof POST_TOMBSTONE {
|
): Shadow<AppBskyFeedDefs.PostView> | typeof POST_TOMBSTONE {
|
||||||
const [state, setState] = useState<CacheEntry>({
|
const postSeenTS = getFirstSeenTS(post)
|
||||||
ts: Date.now(),
|
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),
|
value: fromPost(post),
|
||||||
})
|
})
|
||||||
const firstRun = useRef(true)
|
}
|
||||||
|
|
||||||
const onUpdate = useCallback(
|
const onUpdate = useCallback(
|
||||||
(value: Partial<PostShadow>) => {
|
(value: Partial<PostShadow>) => {
|
||||||
|
@ -46,30 +68,17 @@ export function usePostShadow(
|
||||||
}
|
}
|
||||||
}, [post.uri, onUpdate])
|
}, [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 useMemo(() => {
|
||||||
return state.ts > ifAfterTS
|
return state.ts > postSeenTS
|
||||||
? mergeShadow(post, state.value)
|
? mergeShadow(post, state.value)
|
||||||
: {...post, isShadowed: true}
|
: castAsShadow(post)
|
||||||
}, [post, state, ifAfterTS])
|
}, [post, state, postSeenTS])
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updatePostShadow(uri: string, value: Partial<PostShadow>) {
|
export function updatePostShadow(uri: string, value: Partial<PostShadow>) {
|
||||||
|
batchedUpdates(() => {
|
||||||
emitter.emit(uri, value)
|
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 {
|
function fromPost(post: AppBskyFeedDefs.PostView): PostShadow {
|
||||||
|
@ -89,7 +98,7 @@ function mergeShadow(
|
||||||
if (shadow.isDeleted) {
|
if (shadow.isDeleted) {
|
||||||
return POST_TOMBSTONE
|
return POST_TOMBSTONE
|
||||||
}
|
}
|
||||||
return {
|
return castAsShadow({
|
||||||
...post,
|
...post,
|
||||||
likeCount: shadow.likeCount,
|
likeCount: shadow.likeCount,
|
||||||
repostCount: shadow.repostCount,
|
repostCount: shadow.repostCount,
|
||||||
|
@ -98,6 +107,5 @@ function mergeShadow(
|
||||||
like: shadow.likeUri,
|
like: shadow.likeUri,
|
||||||
repost: shadow.repostUri,
|
repost: shadow.repostUri,
|
||||||
},
|
},
|
||||||
isShadowed: true,
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import {useEffect, useState, useMemo, useCallback, useRef} from 'react'
|
import {useEffect, useState, useMemo, useCallback} from 'react'
|
||||||
import EventEmitter from 'eventemitter3'
|
import EventEmitter from 'eventemitter3'
|
||||||
import {AppBskyActorDefs} from '@atproto/api'
|
import {AppBskyActorDefs} from '@atproto/api'
|
||||||
import {Shadow} from './types'
|
import {batchedUpdates} from '#/lib/batchedUpdates'
|
||||||
|
import {Shadow, castAsShadow} from './types'
|
||||||
export type {Shadow} from './types'
|
export type {Shadow} from './types'
|
||||||
|
|
||||||
const emitter = new EventEmitter()
|
const emitter = new EventEmitter()
|
||||||
|
@ -22,15 +23,34 @@ type ProfileView =
|
||||||
| AppBskyActorDefs.ProfileViewBasic
|
| AppBskyActorDefs.ProfileViewBasic
|
||||||
| AppBskyActorDefs.ProfileViewDetailed
|
| AppBskyActorDefs.ProfileViewDetailed
|
||||||
|
|
||||||
export function useProfileShadow(
|
const firstSeenMap = new WeakMap<ProfileView, number>()
|
||||||
profile: ProfileView,
|
function getFirstSeenTS(profile: ProfileView): number {
|
||||||
ifAfterTS: number,
|
let timeStamp = firstSeenMap.get(profile)
|
||||||
): Shadow<ProfileView> {
|
if (timeStamp !== undefined) {
|
||||||
const [state, setState] = useState<CacheEntry>({
|
return timeStamp
|
||||||
ts: Date.now(),
|
}
|
||||||
|
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),
|
value: fromProfile(profile),
|
||||||
})
|
})
|
||||||
const firstRun = useRef(true)
|
}
|
||||||
|
|
||||||
const onUpdate = useCallback(
|
const onUpdate = useCallback(
|
||||||
(value: Partial<ProfileShadow>) => {
|
(value: Partial<ProfileShadow>) => {
|
||||||
|
@ -47,33 +67,20 @@ export function useProfileShadow(
|
||||||
}
|
}
|
||||||
}, [profile.did, onUpdate])
|
}, [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 useMemo(() => {
|
||||||
return state.ts > ifAfterTS
|
return state.ts > profileSeenTS
|
||||||
? mergeShadow(profile, state.value)
|
? mergeShadow(profile, state.value)
|
||||||
: {...profile, isShadowed: true}
|
: castAsShadow(profile)
|
||||||
}, [profile, state, ifAfterTS])
|
}, [profile, state, profileSeenTS])
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateProfileShadow(
|
export function updateProfileShadow(
|
||||||
uri: string,
|
uri: string,
|
||||||
value: Partial<ProfileShadow>,
|
value: Partial<ProfileShadow>,
|
||||||
) {
|
) {
|
||||||
|
batchedUpdates(() => {
|
||||||
emitter.emit(uri, value)
|
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 {
|
function fromProfile(profile: ProfileView): ProfileShadow {
|
||||||
|
@ -88,7 +95,7 @@ function mergeShadow(
|
||||||
profile: ProfileView,
|
profile: ProfileView,
|
||||||
shadow: ProfileShadow,
|
shadow: ProfileShadow,
|
||||||
): Shadow<ProfileView> {
|
): Shadow<ProfileView> {
|
||||||
return {
|
return castAsShadow({
|
||||||
...profile,
|
...profile,
|
||||||
viewer: {
|
viewer: {
|
||||||
...(profile.viewer || {}),
|
...(profile.viewer || {}),
|
||||||
|
@ -96,6 +103,5 @@ function mergeShadow(
|
||||||
muted: shadow.muted,
|
muted: shadow.muted,
|
||||||
blocking: shadow.blockingUri,
|
blocking: shadow.blockingUri,
|
||||||
},
|
},
|
||||||
isShadowed: true,
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ export function RecommendedFollows({next}: Props) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const {isTabletOrMobile} = useWebMediaQueries()
|
const {isTabletOrMobile} = useWebMediaQueries()
|
||||||
const {data: suggestedFollows, dataUpdatedAt} = useSuggestedFollowsQuery()
|
const {data: suggestedFollows} = useSuggestedFollowsQuery()
|
||||||
const getSuggestedFollowsByActor = useGetSuggestedFollowersByActor()
|
const getSuggestedFollowsByActor = useGetSuggestedFollowersByActor()
|
||||||
const [additionalSuggestions, setAdditionalSuggestions] = React.useState<{
|
const [additionalSuggestions, setAdditionalSuggestions] = React.useState<{
|
||||||
[did: string]: AppBskyActorDefs.ProfileView[]
|
[did: string]: AppBskyActorDefs.ProfileView[]
|
||||||
|
@ -162,7 +162,6 @@ export function RecommendedFollows({next}: Props) {
|
||||||
renderItem={({item}) => (
|
renderItem={({item}) => (
|
||||||
<RecommendedFollowsItem
|
<RecommendedFollowsItem
|
||||||
profile={item}
|
profile={item}
|
||||||
dataUpdatedAt={dataUpdatedAt}
|
|
||||||
onFollowStateChange={onFollowStateChange}
|
onFollowStateChange={onFollowStateChange}
|
||||||
moderation={moderateProfile(item, moderationOpts)}
|
moderation={moderateProfile(item, moderationOpts)}
|
||||||
/>
|
/>
|
||||||
|
@ -197,7 +196,6 @@ export function RecommendedFollows({next}: Props) {
|
||||||
renderItem={({item}) => (
|
renderItem={({item}) => (
|
||||||
<RecommendedFollowsItem
|
<RecommendedFollowsItem
|
||||||
profile={item}
|
profile={item}
|
||||||
dataUpdatedAt={dataUpdatedAt}
|
|
||||||
onFollowStateChange={onFollowStateChange}
|
onFollowStateChange={onFollowStateChange}
|
||||||
moderation={moderateProfile(item, moderationOpts)}
|
moderation={moderateProfile(item, moderationOpts)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -18,7 +18,6 @@ import {logger} from '#/logger'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
profile: AppBskyActorDefs.ProfileViewBasic
|
profile: AppBskyActorDefs.ProfileViewBasic
|
||||||
dataUpdatedAt: number
|
|
||||||
moderation: ProfileModeration
|
moderation: ProfileModeration
|
||||||
onFollowStateChange: (props: {
|
onFollowStateChange: (props: {
|
||||||
did: string
|
did: string
|
||||||
|
@ -28,13 +27,12 @@ type Props = {
|
||||||
|
|
||||||
export function RecommendedFollowsItem({
|
export function RecommendedFollowsItem({
|
||||||
profile,
|
profile,
|
||||||
dataUpdatedAt,
|
|
||||||
moderation,
|
moderation,
|
||||||
onFollowStateChange,
|
onFollowStateChange,
|
||||||
}: React.PropsWithChildren<Props>) {
|
}: React.PropsWithChildren<Props>) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const {isMobile} = useWebMediaQueries()
|
const {isMobile} = useWebMediaQueries()
|
||||||
const shadowedProfile = useProfileShadow(profile, dataUpdatedAt)
|
const shadowedProfile = useProfileShadow(profile)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Animated.View
|
<Animated.View
|
||||||
|
|
|
@ -64,7 +64,6 @@ export function ListMembers({
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
dataUpdatedAt,
|
|
||||||
isFetching,
|
isFetching,
|
||||||
isFetched,
|
isFetched,
|
||||||
isError,
|
isError,
|
||||||
|
@ -185,7 +184,6 @@ export function ListMembers({
|
||||||
(item as AppBskyGraphDefs.ListItemView).subject.handle
|
(item as AppBskyGraphDefs.ListItemView).subject.handle
|
||||||
}`}
|
}`}
|
||||||
profile={(item as AppBskyGraphDefs.ListItemView).subject}
|
profile={(item as AppBskyGraphDefs.ListItemView).subject}
|
||||||
dataUpdatedAt={dataUpdatedAt}
|
|
||||||
renderButton={renderMemberButton}
|
renderButton={renderMemberButton}
|
||||||
style={{paddingHorizontal: isMobile ? 8 : 14, paddingVertical: 4}}
|
style={{paddingHorizontal: isMobile ? 8 : 14, paddingVertical: 4}}
|
||||||
/>
|
/>
|
||||||
|
@ -198,7 +196,6 @@ export function ListMembers({
|
||||||
onPressTryAgain,
|
onPressTryAgain,
|
||||||
onPressRetryLoadMore,
|
onPressRetryLoadMore,
|
||||||
isMobile,
|
isMobile,
|
||||||
dataUpdatedAt,
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,6 @@ export function Component({did}: {did: string}) {
|
||||||
const moderationOpts = useModerationOpts()
|
const moderationOpts = useModerationOpts()
|
||||||
const {
|
const {
|
||||||
data: profile,
|
data: profile,
|
||||||
dataUpdatedAt,
|
|
||||||
error: profileError,
|
error: profileError,
|
||||||
refetch: refetchProfile,
|
refetch: refetchProfile,
|
||||||
isFetching: isFetchingProfile,
|
isFetching: isFetchingProfile,
|
||||||
|
@ -51,13 +50,7 @@ export function Component({did}: {did: string}) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (profile && moderationOpts) {
|
if (profile && moderationOpts) {
|
||||||
return (
|
return <ComponentLoaded profile={profile} moderationOpts={moderationOpts} />
|
||||||
<ComponentLoaded
|
|
||||||
profile={profile}
|
|
||||||
dataUpdatedAt={dataUpdatedAt}
|
|
||||||
moderationOpts={moderationOpts}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
// should never happen
|
// should never happen
|
||||||
return (
|
return (
|
||||||
|
@ -71,15 +64,13 @@ export function Component({did}: {did: string}) {
|
||||||
|
|
||||||
function ComponentLoaded({
|
function ComponentLoaded({
|
||||||
profile: profileUnshadowed,
|
profile: profileUnshadowed,
|
||||||
dataUpdatedAt,
|
|
||||||
moderationOpts,
|
moderationOpts,
|
||||||
}: {
|
}: {
|
||||||
profile: AppBskyActorDefs.ProfileViewDetailed
|
profile: AppBskyActorDefs.ProfileViewDetailed
|
||||||
dataUpdatedAt: number
|
|
||||||
moderationOpts: ModerationOpts
|
moderationOpts: ModerationOpts
|
||||||
}) {
|
}) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const profile = useProfileShadow(profileUnshadowed, dataUpdatedAt)
|
const profile = useProfileShadow(profileUnshadowed)
|
||||||
const {screen} = useAnalytics()
|
const {screen} = useAnalytics()
|
||||||
const moderation = React.useMemo(
|
const moderation = React.useMemo(
|
||||||
() => moderateProfile(profile, moderationOpts),
|
() => moderateProfile(profile, moderationOpts),
|
||||||
|
|
|
@ -38,7 +38,6 @@ export function Feed({
|
||||||
const {markAllRead} = useUnreadNotificationsApi()
|
const {markAllRead} = useUnreadNotificationsApi()
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
dataUpdatedAt,
|
|
||||||
isLoading,
|
isLoading,
|
||||||
isFetching,
|
isFetching,
|
||||||
isFetched,
|
isFetched,
|
||||||
|
@ -132,15 +131,9 @@ export function Feed({
|
||||||
} else if (item === LOADING_ITEM) {
|
} else if (item === LOADING_ITEM) {
|
||||||
return <NotificationFeedLoadingPlaceholder />
|
return <NotificationFeedLoadingPlaceholder />
|
||||||
}
|
}
|
||||||
return (
|
return <FeedItem item={item} moderationOpts={moderationOpts!} />
|
||||||
<FeedItem
|
|
||||||
item={item}
|
|
||||||
dataUpdatedAt={dataUpdatedAt}
|
|
||||||
moderationOpts={moderationOpts!}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
[onPressRetryLoadMore, dataUpdatedAt, moderationOpts],
|
[onPressRetryLoadMore, moderationOpts],
|
||||||
)
|
)
|
||||||
|
|
||||||
const showHeaderSpinner = !isPTRing && isFetching && !isLoading
|
const showHeaderSpinner = !isPTRing && isFetching && !isLoading
|
||||||
|
|
|
@ -58,11 +58,9 @@ interface Author {
|
||||||
|
|
||||||
let FeedItem = ({
|
let FeedItem = ({
|
||||||
item,
|
item,
|
||||||
dataUpdatedAt,
|
|
||||||
moderationOpts,
|
moderationOpts,
|
||||||
}: {
|
}: {
|
||||||
item: FeedNotification
|
item: FeedNotification
|
||||||
dataUpdatedAt: number
|
|
||||||
moderationOpts: ModerationOpts
|
moderationOpts: ModerationOpts
|
||||||
}): React.ReactNode => {
|
}): React.ReactNode => {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
|
@ -135,7 +133,6 @@ let FeedItem = ({
|
||||||
accessible={false}>
|
accessible={false}>
|
||||||
<Post
|
<Post
|
||||||
post={item.subject}
|
post={item.subject}
|
||||||
dataUpdatedAt={dataUpdatedAt}
|
|
||||||
style={
|
style={
|
||||||
item.notification.isRead
|
item.notification.isRead
|
||||||
? undefined
|
? undefined
|
||||||
|
|
|
@ -20,7 +20,6 @@ export function PostLikedBy({uri}: {uri: string}) {
|
||||||
} = useResolveUriQuery(uri)
|
} = useResolveUriQuery(uri)
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
dataUpdatedAt,
|
|
||||||
isFetching,
|
isFetching,
|
||||||
isFetched,
|
isFetched,
|
||||||
isFetchingNextPage,
|
isFetchingNextPage,
|
||||||
|
@ -55,18 +54,11 @@ export function PostLikedBy({uri}: {uri: string}) {
|
||||||
}
|
}
|
||||||
}, [isFetching, hasNextPage, isError, fetchNextPage])
|
}, [isFetching, hasNextPage, isError, fetchNextPage])
|
||||||
|
|
||||||
const renderItem = useCallback(
|
const renderItem = useCallback(({item}: {item: GetLikes.Like}) => {
|
||||||
({item}: {item: GetLikes.Like}) => {
|
|
||||||
return (
|
return (
|
||||||
<ProfileCardWithFollowBtn
|
<ProfileCardWithFollowBtn key={item.actor.did} profile={item.actor} />
|
||||||
key={item.actor.did}
|
|
||||||
profile={item.actor}
|
|
||||||
dataUpdatedAt={dataUpdatedAt}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
[dataUpdatedAt],
|
|
||||||
)
|
)
|
||||||
|
}, [])
|
||||||
|
|
||||||
if (isFetchingResolvedUri || !isFetched) {
|
if (isFetchingResolvedUri || !isFetched) {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -20,7 +20,6 @@ export function PostRepostedBy({uri}: {uri: string}) {
|
||||||
} = useResolveUriQuery(uri)
|
} = useResolveUriQuery(uri)
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
dataUpdatedAt,
|
|
||||||
isFetching,
|
isFetching,
|
||||||
isFetched,
|
isFetched,
|
||||||
isFetchingNextPage,
|
isFetchingNextPage,
|
||||||
|
@ -57,15 +56,9 @@ export function PostRepostedBy({uri}: {uri: string}) {
|
||||||
|
|
||||||
const renderItem = useCallback(
|
const renderItem = useCallback(
|
||||||
({item}: {item: ActorDefs.ProfileViewBasic}) => {
|
({item}: {item: ActorDefs.ProfileViewBasic}) => {
|
||||||
return (
|
return <ProfileCardWithFollowBtn key={item.did} profile={item} />
|
||||||
<ProfileCardWithFollowBtn
|
|
||||||
key={item.did}
|
|
||||||
profile={item}
|
|
||||||
dataUpdatedAt={dataUpdatedAt}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
[dataUpdatedAt],
|
[],
|
||||||
)
|
)
|
||||||
|
|
||||||
if (isFetchingResolvedUri || !isFetched) {
|
if (isFetchingResolvedUri || !isFetched) {
|
||||||
|
|
|
@ -73,7 +73,6 @@ export function PostThread({
|
||||||
refetch,
|
refetch,
|
||||||
isRefetching,
|
isRefetching,
|
||||||
data: thread,
|
data: thread,
|
||||||
dataUpdatedAt,
|
|
||||||
} = usePostThreadQuery(uri)
|
} = usePostThreadQuery(uri)
|
||||||
const {data: preferences} = usePreferencesQuery()
|
const {data: preferences} = usePreferencesQuery()
|
||||||
const rootPost = thread?.type === 'post' ? thread.post : undefined
|
const rootPost = thread?.type === 'post' ? thread.post : undefined
|
||||||
|
@ -111,7 +110,6 @@ export function PostThread({
|
||||||
<PostThreadLoaded
|
<PostThreadLoaded
|
||||||
thread={thread}
|
thread={thread}
|
||||||
isRefetching={isRefetching}
|
isRefetching={isRefetching}
|
||||||
dataUpdatedAt={dataUpdatedAt}
|
|
||||||
threadViewPrefs={preferences.threadViewPrefs}
|
threadViewPrefs={preferences.threadViewPrefs}
|
||||||
onRefresh={refetch}
|
onRefresh={refetch}
|
||||||
onPressReply={onPressReply}
|
onPressReply={onPressReply}
|
||||||
|
@ -122,14 +120,12 @@ export function PostThread({
|
||||||
function PostThreadLoaded({
|
function PostThreadLoaded({
|
||||||
thread,
|
thread,
|
||||||
isRefetching,
|
isRefetching,
|
||||||
dataUpdatedAt,
|
|
||||||
threadViewPrefs,
|
threadViewPrefs,
|
||||||
onRefresh,
|
onRefresh,
|
||||||
onPressReply,
|
onPressReply,
|
||||||
}: {
|
}: {
|
||||||
thread: ThreadNode
|
thread: ThreadNode
|
||||||
isRefetching: boolean
|
isRefetching: boolean
|
||||||
dataUpdatedAt: number
|
|
||||||
threadViewPrefs: UsePreferencesQueryResponse['threadViewPrefs']
|
threadViewPrefs: UsePreferencesQueryResponse['threadViewPrefs']
|
||||||
onRefresh: () => void
|
onRefresh: () => void
|
||||||
onPressReply: () => void
|
onPressReply: () => void
|
||||||
|
@ -295,7 +291,6 @@ function PostThreadLoaded({
|
||||||
<PostThreadItem
|
<PostThreadItem
|
||||||
post={item.post}
|
post={item.post}
|
||||||
record={item.record}
|
record={item.record}
|
||||||
dataUpdatedAt={dataUpdatedAt}
|
|
||||||
treeView={threadViewPrefs.lab_treeViewEnabled || false}
|
treeView={threadViewPrefs.lab_treeViewEnabled || false}
|
||||||
depth={item.ctx.depth}
|
depth={item.ctx.depth}
|
||||||
isHighlightedPost={item.ctx.isHighlightedPost}
|
isHighlightedPost={item.ctx.isHighlightedPost}
|
||||||
|
@ -322,7 +317,6 @@ function PostThreadLoaded({
|
||||||
posts,
|
posts,
|
||||||
onRefresh,
|
onRefresh,
|
||||||
threadViewPrefs.lab_treeViewEnabled,
|
threadViewPrefs.lab_treeViewEnabled,
|
||||||
dataUpdatedAt,
|
|
||||||
_,
|
_,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -45,7 +45,6 @@ import {Shadow, usePostShadow, POST_TOMBSTONE} from '#/state/cache/post-shadow'
|
||||||
export function PostThreadItem({
|
export function PostThreadItem({
|
||||||
post,
|
post,
|
||||||
record,
|
record,
|
||||||
dataUpdatedAt,
|
|
||||||
treeView,
|
treeView,
|
||||||
depth,
|
depth,
|
||||||
isHighlightedPost,
|
isHighlightedPost,
|
||||||
|
@ -57,7 +56,6 @@ export function PostThreadItem({
|
||||||
}: {
|
}: {
|
||||||
post: AppBskyFeedDefs.PostView
|
post: AppBskyFeedDefs.PostView
|
||||||
record: AppBskyFeedPost.Record
|
record: AppBskyFeedPost.Record
|
||||||
dataUpdatedAt: number
|
|
||||||
treeView: boolean
|
treeView: boolean
|
||||||
depth: number
|
depth: number
|
||||||
isHighlightedPost?: boolean
|
isHighlightedPost?: boolean
|
||||||
|
@ -68,7 +66,7 @@ export function PostThreadItem({
|
||||||
onPostReply: () => void
|
onPostReply: () => void
|
||||||
}) {
|
}) {
|
||||||
const moderationOpts = useModerationOpts()
|
const moderationOpts = useModerationOpts()
|
||||||
const postShadowed = usePostShadow(post, dataUpdatedAt)
|
const postShadowed = usePostShadow(post)
|
||||||
const richText = useMemo(
|
const richText = useMemo(
|
||||||
() =>
|
() =>
|
||||||
new RichTextAPI({
|
new RichTextAPI({
|
||||||
|
|
|
@ -30,12 +30,10 @@ import {Shadow, usePostShadow, POST_TOMBSTONE} from '#/state/cache/post-shadow'
|
||||||
|
|
||||||
export function Post({
|
export function Post({
|
||||||
post,
|
post,
|
||||||
dataUpdatedAt,
|
|
||||||
showReplyLine,
|
showReplyLine,
|
||||||
style,
|
style,
|
||||||
}: {
|
}: {
|
||||||
post: AppBskyFeedDefs.PostView
|
post: AppBskyFeedDefs.PostView
|
||||||
dataUpdatedAt: number
|
|
||||||
showReplyLine?: boolean
|
showReplyLine?: boolean
|
||||||
style?: StyleProp<ViewStyle>
|
style?: StyleProp<ViewStyle>
|
||||||
}) {
|
}) {
|
||||||
|
@ -48,7 +46,7 @@ export function Post({
|
||||||
: undefined,
|
: undefined,
|
||||||
[post],
|
[post],
|
||||||
)
|
)
|
||||||
const postShadowed = usePostShadow(post, dataUpdatedAt)
|
const postShadowed = usePostShadow(post)
|
||||||
const richText = useMemo(
|
const richText = useMemo(
|
||||||
() =>
|
() =>
|
||||||
record
|
record
|
||||||
|
|
|
@ -76,7 +76,6 @@ let Feed = ({
|
||||||
const opts = React.useMemo(() => ({enabled}), [enabled])
|
const opts = React.useMemo(() => ({enabled}), [enabled])
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
dataUpdatedAt,
|
|
||||||
isFetching,
|
isFetching,
|
||||||
isFetched,
|
isFetched,
|
||||||
isError,
|
isError,
|
||||||
|
@ -200,7 +199,6 @@ let Feed = ({
|
||||||
return (
|
return (
|
||||||
<FeedSlice
|
<FeedSlice
|
||||||
slice={item}
|
slice={item}
|
||||||
dataUpdatedAt={dataUpdatedAt}
|
|
||||||
// we check for this before creating the feedItems array
|
// we check for this before creating the feedItems array
|
||||||
moderationOpts={moderationOpts!}
|
moderationOpts={moderationOpts!}
|
||||||
/>
|
/>
|
||||||
|
@ -208,7 +206,6 @@ let Feed = ({
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
feed,
|
feed,
|
||||||
dataUpdatedAt,
|
|
||||||
error,
|
error,
|
||||||
onPressTryAgain,
|
onPressTryAgain,
|
||||||
onPressRetryLoadMore,
|
onPressRetryLoadMore,
|
||||||
|
|
|
@ -40,7 +40,6 @@ export function FeedItem({
|
||||||
record,
|
record,
|
||||||
reason,
|
reason,
|
||||||
moderation,
|
moderation,
|
||||||
dataUpdatedAt,
|
|
||||||
isThreadChild,
|
isThreadChild,
|
||||||
isThreadLastChild,
|
isThreadLastChild,
|
||||||
isThreadParent,
|
isThreadParent,
|
||||||
|
@ -49,12 +48,11 @@ export function FeedItem({
|
||||||
record: AppBskyFeedPost.Record
|
record: AppBskyFeedPost.Record
|
||||||
reason: AppBskyFeedDefs.ReasonRepost | ReasonFeedSource | undefined
|
reason: AppBskyFeedDefs.ReasonRepost | ReasonFeedSource | undefined
|
||||||
moderation: PostModeration
|
moderation: PostModeration
|
||||||
dataUpdatedAt: number
|
|
||||||
isThreadChild?: boolean
|
isThreadChild?: boolean
|
||||||
isThreadLastChild?: boolean
|
isThreadLastChild?: boolean
|
||||||
isThreadParent?: boolean
|
isThreadParent?: boolean
|
||||||
}) {
|
}) {
|
||||||
const postShadowed = usePostShadow(post, dataUpdatedAt)
|
const postShadowed = usePostShadow(post)
|
||||||
const richText = useMemo(
|
const richText = useMemo(
|
||||||
() =>
|
() =>
|
||||||
new RichTextAPI({
|
new RichTextAPI({
|
||||||
|
|
|
@ -11,12 +11,10 @@ import {makeProfileLink} from 'lib/routes/links'
|
||||||
|
|
||||||
let FeedSlice = ({
|
let FeedSlice = ({
|
||||||
slice,
|
slice,
|
||||||
dataUpdatedAt,
|
|
||||||
ignoreFilterFor,
|
ignoreFilterFor,
|
||||||
moderationOpts,
|
moderationOpts,
|
||||||
}: {
|
}: {
|
||||||
slice: FeedPostSlice
|
slice: FeedPostSlice
|
||||||
dataUpdatedAt: number
|
|
||||||
ignoreFilterFor?: string
|
ignoreFilterFor?: string
|
||||||
moderationOpts: ModerationOpts
|
moderationOpts: ModerationOpts
|
||||||
}): React.ReactNode => {
|
}): React.ReactNode => {
|
||||||
|
@ -44,7 +42,6 @@ let FeedSlice = ({
|
||||||
record={slice.items[0].record}
|
record={slice.items[0].record}
|
||||||
reason={slice.items[0].reason}
|
reason={slice.items[0].reason}
|
||||||
moderation={moderations[0]}
|
moderation={moderations[0]}
|
||||||
dataUpdatedAt={dataUpdatedAt}
|
|
||||||
isThreadParent={isThreadParentAt(slice.items, 0)}
|
isThreadParent={isThreadParentAt(slice.items, 0)}
|
||||||
isThreadChild={isThreadChildAt(slice.items, 0)}
|
isThreadChild={isThreadChildAt(slice.items, 0)}
|
||||||
/>
|
/>
|
||||||
|
@ -54,7 +51,6 @@ let FeedSlice = ({
|
||||||
record={slice.items[1].record}
|
record={slice.items[1].record}
|
||||||
reason={slice.items[1].reason}
|
reason={slice.items[1].reason}
|
||||||
moderation={moderations[1]}
|
moderation={moderations[1]}
|
||||||
dataUpdatedAt={dataUpdatedAt}
|
|
||||||
isThreadParent={isThreadParentAt(slice.items, 1)}
|
isThreadParent={isThreadParentAt(slice.items, 1)}
|
||||||
isThreadChild={isThreadChildAt(slice.items, 1)}
|
isThreadChild={isThreadChildAt(slice.items, 1)}
|
||||||
/>
|
/>
|
||||||
|
@ -65,7 +61,6 @@ let FeedSlice = ({
|
||||||
record={slice.items[last].record}
|
record={slice.items[last].record}
|
||||||
reason={slice.items[last].reason}
|
reason={slice.items[last].reason}
|
||||||
moderation={moderations[last]}
|
moderation={moderations[last]}
|
||||||
dataUpdatedAt={dataUpdatedAt}
|
|
||||||
isThreadParent={isThreadParentAt(slice.items, last)}
|
isThreadParent={isThreadParentAt(slice.items, last)}
|
||||||
isThreadChild={isThreadChildAt(slice.items, last)}
|
isThreadChild={isThreadChildAt(slice.items, last)}
|
||||||
isThreadLastChild
|
isThreadLastChild
|
||||||
|
@ -83,7 +78,6 @@ let FeedSlice = ({
|
||||||
record={slice.items[i].record}
|
record={slice.items[i].record}
|
||||||
reason={slice.items[i].reason}
|
reason={slice.items[i].reason}
|
||||||
moderation={moderations[i]}
|
moderation={moderations[i]}
|
||||||
dataUpdatedAt={dataUpdatedAt}
|
|
||||||
isThreadParent={isThreadParentAt(slice.items, i)}
|
isThreadParent={isThreadParentAt(slice.items, i)}
|
||||||
isThreadChild={isThreadChildAt(slice.items, i)}
|
isThreadChild={isThreadChildAt(slice.items, i)}
|
||||||
isThreadLastChild={
|
isThreadLastChild={
|
||||||
|
|
|
@ -27,7 +27,6 @@ import {useSession} from '#/state/session'
|
||||||
export function ProfileCard({
|
export function ProfileCard({
|
||||||
testID,
|
testID,
|
||||||
profile: profileUnshadowed,
|
profile: profileUnshadowed,
|
||||||
dataUpdatedAt,
|
|
||||||
noBg,
|
noBg,
|
||||||
noBorder,
|
noBorder,
|
||||||
followers,
|
followers,
|
||||||
|
@ -36,7 +35,6 @@ export function ProfileCard({
|
||||||
}: {
|
}: {
|
||||||
testID?: string
|
testID?: string
|
||||||
profile: AppBskyActorDefs.ProfileViewBasic
|
profile: AppBskyActorDefs.ProfileViewBasic
|
||||||
dataUpdatedAt: number
|
|
||||||
noBg?: boolean
|
noBg?: boolean
|
||||||
noBorder?: boolean
|
noBorder?: boolean
|
||||||
followers?: AppBskyActorDefs.ProfileView[] | undefined
|
followers?: AppBskyActorDefs.ProfileView[] | undefined
|
||||||
|
@ -46,7 +44,7 @@ export function ProfileCard({
|
||||||
style?: StyleProp<ViewStyle>
|
style?: StyleProp<ViewStyle>
|
||||||
}) {
|
}) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const profile = useProfileShadow(profileUnshadowed, dataUpdatedAt)
|
const profile = useProfileShadow(profileUnshadowed)
|
||||||
const moderationOpts = useModerationOpts()
|
const moderationOpts = useModerationOpts()
|
||||||
if (!moderationOpts) {
|
if (!moderationOpts) {
|
||||||
return null
|
return null
|
||||||
|
@ -202,13 +200,11 @@ export function ProfileCardWithFollowBtn({
|
||||||
noBg,
|
noBg,
|
||||||
noBorder,
|
noBorder,
|
||||||
followers,
|
followers,
|
||||||
dataUpdatedAt,
|
|
||||||
}: {
|
}: {
|
||||||
profile: AppBskyActorDefs.ProfileViewBasic
|
profile: AppBskyActorDefs.ProfileViewBasic
|
||||||
noBg?: boolean
|
noBg?: boolean
|
||||||
noBorder?: boolean
|
noBorder?: boolean
|
||||||
followers?: AppBskyActorDefs.ProfileView[] | undefined
|
followers?: AppBskyActorDefs.ProfileView[] | undefined
|
||||||
dataUpdatedAt: number
|
|
||||||
}) {
|
}) {
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const isMe = profile.did === currentAccount?.did
|
const isMe = profile.did === currentAccount?.did
|
||||||
|
@ -224,7 +220,6 @@ export function ProfileCardWithFollowBtn({
|
||||||
? undefined
|
? undefined
|
||||||
: profileShadow => <FollowButton profile={profileShadow} />
|
: profileShadow => <FollowButton profile={profileShadow} />
|
||||||
}
|
}
|
||||||
dataUpdatedAt={dataUpdatedAt}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ export function ProfileFollowers({name}: {name: string}) {
|
||||||
} = useResolveDidQuery(name)
|
} = useResolveDidQuery(name)
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
dataUpdatedAt,
|
|
||||||
isFetching,
|
isFetching,
|
||||||
isFetched,
|
isFetched,
|
||||||
isFetchingNextPage,
|
isFetchingNextPage,
|
||||||
|
@ -58,13 +57,9 @@ export function ProfileFollowers({name}: {name: string}) {
|
||||||
|
|
||||||
const renderItem = React.useCallback(
|
const renderItem = React.useCallback(
|
||||||
({item}: {item: ActorDefs.ProfileViewBasic}) => (
|
({item}: {item: ActorDefs.ProfileViewBasic}) => (
|
||||||
<ProfileCardWithFollowBtn
|
<ProfileCardWithFollowBtn key={item.did} profile={item} />
|
||||||
key={item.did}
|
|
||||||
profile={item}
|
|
||||||
dataUpdatedAt={dataUpdatedAt}
|
|
||||||
/>
|
|
||||||
),
|
),
|
||||||
[dataUpdatedAt],
|
[],
|
||||||
)
|
)
|
||||||
|
|
||||||
if (isFetchingDid || !isFetched) {
|
if (isFetchingDid || !isFetched) {
|
||||||
|
|
|
@ -20,7 +20,6 @@ export function ProfileFollows({name}: {name: string}) {
|
||||||
} = useResolveDidQuery(name)
|
} = useResolveDidQuery(name)
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
dataUpdatedAt,
|
|
||||||
isFetching,
|
isFetching,
|
||||||
isFetched,
|
isFetched,
|
||||||
isFetchingNextPage,
|
isFetchingNextPage,
|
||||||
|
@ -58,13 +57,9 @@ export function ProfileFollows({name}: {name: string}) {
|
||||||
|
|
||||||
const renderItem = React.useCallback(
|
const renderItem = React.useCallback(
|
||||||
({item}: {item: ActorDefs.ProfileViewBasic}) => (
|
({item}: {item: ActorDefs.ProfileViewBasic}) => (
|
||||||
<ProfileCardWithFollowBtn
|
<ProfileCardWithFollowBtn key={item.did} profile={item} />
|
||||||
key={item.did}
|
|
||||||
profile={item}
|
|
||||||
dataUpdatedAt={dataUpdatedAt}
|
|
||||||
/>
|
|
||||||
),
|
),
|
||||||
[dataUpdatedAt],
|
[],
|
||||||
)
|
)
|
||||||
|
|
||||||
if (isFetchingDid || !isFetched) {
|
if (isFetchingDid || !isFetched) {
|
||||||
|
|
|
@ -65,7 +65,7 @@ export function ProfileHeaderSuggestedFollows({
|
||||||
}
|
}
|
||||||
}, [active, animatedHeight, track])
|
}, [active, animatedHeight, track])
|
||||||
|
|
||||||
const {isLoading, data, dataUpdatedAt} = useSuggestedFollowsByActorQuery({
|
const {isLoading, data} = useSuggestedFollowsByActorQuery({
|
||||||
did: actorDid,
|
did: actorDid,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -127,11 +127,7 @@ export function ProfileHeaderSuggestedFollows({
|
||||||
</>
|
</>
|
||||||
) : data ? (
|
) : data ? (
|
||||||
data.suggestions.map(profile => (
|
data.suggestions.map(profile => (
|
||||||
<SuggestedFollow
|
<SuggestedFollow key={profile.did} profile={profile} />
|
||||||
key={profile.did}
|
|
||||||
profile={profile}
|
|
||||||
dataUpdatedAt={dataUpdatedAt}
|
|
||||||
/>
|
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<View />
|
<View />
|
||||||
|
@ -196,15 +192,13 @@ function SuggestedFollowSkeleton() {
|
||||||
|
|
||||||
function SuggestedFollow({
|
function SuggestedFollow({
|
||||||
profile: profileUnshadowed,
|
profile: profileUnshadowed,
|
||||||
dataUpdatedAt,
|
|
||||||
}: {
|
}: {
|
||||||
profile: AppBskyActorDefs.ProfileView
|
profile: AppBskyActorDefs.ProfileView
|
||||||
dataUpdatedAt: number
|
|
||||||
}) {
|
}) {
|
||||||
const {track} = useAnalytics()
|
const {track} = useAnalytics()
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const moderationOpts = useModerationOpts()
|
const moderationOpts = useModerationOpts()
|
||||||
const profile = useProfileShadow(profileUnshadowed, dataUpdatedAt)
|
const profile = useProfileShadow(profileUnshadowed)
|
||||||
const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(profile)
|
const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(profile)
|
||||||
|
|
||||||
const onPressFollow = React.useCallback(async () => {
|
const onPressFollow = React.useCallback(async () => {
|
||||||
|
|
|
@ -40,7 +40,6 @@ export const ModerationBlockedAccounts = withAuthRequired(
|
||||||
const [isPTRing, setIsPTRing] = React.useState(false)
|
const [isPTRing, setIsPTRing] = React.useState(false)
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
dataUpdatedAt,
|
|
||||||
isFetching,
|
isFetching,
|
||||||
isError,
|
isError,
|
||||||
error,
|
error,
|
||||||
|
@ -95,7 +94,6 @@ export const ModerationBlockedAccounts = withAuthRequired(
|
||||||
testID={`blockedAccount-${index}`}
|
testID={`blockedAccount-${index}`}
|
||||||
key={item.did}
|
key={item.did}
|
||||||
profile={item}
|
profile={item}
|
||||||
dataUpdatedAt={dataUpdatedAt}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -40,7 +40,6 @@ export const ModerationMutedAccounts = withAuthRequired(
|
||||||
const [isPTRing, setIsPTRing] = React.useState(false)
|
const [isPTRing, setIsPTRing] = React.useState(false)
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
dataUpdatedAt,
|
|
||||||
isFetching,
|
isFetching,
|
||||||
isError,
|
isError,
|
||||||
error,
|
error,
|
||||||
|
@ -95,7 +94,6 @@ export const ModerationMutedAccounts = withAuthRequired(
|
||||||
testID={`mutedAccount-${index}`}
|
testID={`mutedAccount-${index}`}
|
||||||
key={item.did}
|
key={item.did}
|
||||||
profile={item}
|
profile={item}
|
||||||
dataUpdatedAt={dataUpdatedAt}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -57,7 +57,6 @@ export const ProfileScreen = withAuthRequired(
|
||||||
} = useResolveDidQuery(name)
|
} = useResolveDidQuery(name)
|
||||||
const {
|
const {
|
||||||
data: profile,
|
data: profile,
|
||||||
dataUpdatedAt,
|
|
||||||
error: profileError,
|
error: profileError,
|
||||||
refetch: refetchProfile,
|
refetch: refetchProfile,
|
||||||
isFetching: isFetchingProfile,
|
isFetching: isFetchingProfile,
|
||||||
|
@ -100,7 +99,6 @@ export const ProfileScreen = withAuthRequired(
|
||||||
return (
|
return (
|
||||||
<ProfileScreenLoaded
|
<ProfileScreenLoaded
|
||||||
profile={profile}
|
profile={profile}
|
||||||
dataUpdatedAt={dataUpdatedAt}
|
|
||||||
moderationOpts={moderationOpts}
|
moderationOpts={moderationOpts}
|
||||||
hideBackButton={!!route.params.hideBackButton}
|
hideBackButton={!!route.params.hideBackButton}
|
||||||
/>
|
/>
|
||||||
|
@ -125,16 +123,14 @@ export const ProfileScreen = withAuthRequired(
|
||||||
|
|
||||||
function ProfileScreenLoaded({
|
function ProfileScreenLoaded({
|
||||||
profile: profileUnshadowed,
|
profile: profileUnshadowed,
|
||||||
dataUpdatedAt,
|
|
||||||
moderationOpts,
|
moderationOpts,
|
||||||
hideBackButton,
|
hideBackButton,
|
||||||
}: {
|
}: {
|
||||||
profile: AppBskyActorDefs.ProfileViewDetailed
|
profile: AppBskyActorDefs.ProfileViewDetailed
|
||||||
dataUpdatedAt: number
|
|
||||||
moderationOpts: ModerationOpts
|
moderationOpts: ModerationOpts
|
||||||
hideBackButton: boolean
|
hideBackButton: boolean
|
||||||
}) {
|
}) {
|
||||||
const profile = useProfileShadow(profileUnshadowed, dataUpdatedAt)
|
const profile = useProfileShadow(profileUnshadowed)
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const setMinimalShellMode = useSetMinimalShellMode()
|
const setMinimalShellMode = useSetMinimalShellMode()
|
||||||
const {openComposer} = useComposerControls()
|
const {openComposer} = useComposerControls()
|
||||||
|
|
|
@ -111,7 +111,6 @@ function EmptyState({message, error}: {message: string; error?: string}) {
|
||||||
function SearchScreenSuggestedFollows() {
|
function SearchScreenSuggestedFollows() {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const [dataUpdatedAt, setDataUpdatedAt] = React.useState(0)
|
|
||||||
const [suggestions, setSuggestions] = React.useState<
|
const [suggestions, setSuggestions] = React.useState<
|
||||||
AppBskyActorDefs.ProfileViewBasic[]
|
AppBskyActorDefs.ProfileViewBasic[]
|
||||||
>([])
|
>([])
|
||||||
|
@ -141,7 +140,6 @@ function SearchScreenSuggestedFollows() {
|
||||||
)
|
)
|
||||||
|
|
||||||
setSuggestions(Array.from(friendsOfFriends.values()))
|
setSuggestions(Array.from(friendsOfFriends.values()))
|
||||||
setDataUpdatedAt(Date.now())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -151,23 +149,12 @@ function SearchScreenSuggestedFollows() {
|
||||||
error: e,
|
error: e,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, [
|
}, [currentAccount, setSuggestions, getSuggestedFollowsByActor])
|
||||||
currentAccount,
|
|
||||||
setSuggestions,
|
|
||||||
setDataUpdatedAt,
|
|
||||||
getSuggestedFollowsByActor,
|
|
||||||
])
|
|
||||||
|
|
||||||
return suggestions.length ? (
|
return suggestions.length ? (
|
||||||
<FlatList
|
<FlatList
|
||||||
data={suggestions}
|
data={suggestions}
|
||||||
renderItem={({item}) => (
|
renderItem={({item}) => <ProfileCardWithFollowBtn profile={item} noBg />}
|
||||||
<ProfileCardWithFollowBtn
|
|
||||||
profile={item}
|
|
||||||
noBg
|
|
||||||
dataUpdatedAt={dataUpdatedAt}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
keyExtractor={item => item.did}
|
keyExtractor={item => item.did}
|
||||||
// @ts-ignore web only -prf
|
// @ts-ignore web only -prf
|
||||||
desktopFixedHeight
|
desktopFixedHeight
|
||||||
|
@ -205,7 +192,6 @@ function SearchScreenPostResults({query}: {query: string}) {
|
||||||
fetchNextPage,
|
fetchNextPage,
|
||||||
isFetchingNextPage,
|
isFetchingNextPage,
|
||||||
hasNextPage,
|
hasNextPage,
|
||||||
dataUpdatedAt,
|
|
||||||
} = useSearchPostsQuery({query})
|
} = useSearchPostsQuery({query})
|
||||||
|
|
||||||
const onPullToRefresh = React.useCallback(async () => {
|
const onPullToRefresh = React.useCallback(async () => {
|
||||||
|
@ -258,7 +244,7 @@ function SearchScreenPostResults({query}: {query: string}) {
|
||||||
data={items}
|
data={items}
|
||||||
renderItem={({item}) => {
|
renderItem={({item}) => {
|
||||||
if (item.type === 'post') {
|
if (item.type === 'post') {
|
||||||
return <Post post={item.post} dataUpdatedAt={dataUpdatedAt} />
|
return <Post post={item.post} />
|
||||||
} else {
|
} else {
|
||||||
return <Loader />
|
return <Loader />
|
||||||
}
|
}
|
||||||
|
@ -291,7 +277,6 @@ function SearchScreenPostResults({query}: {query: string}) {
|
||||||
function SearchScreenUserResults({query}: {query: string}) {
|
function SearchScreenUserResults({query}: {query: string}) {
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const [isFetched, setIsFetched] = React.useState(false)
|
const [isFetched, setIsFetched] = React.useState(false)
|
||||||
const [dataUpdatedAt, setDataUpdatedAt] = React.useState(0)
|
|
||||||
const [results, setResults] = React.useState<
|
const [results, setResults] = React.useState<
|
||||||
AppBskyActorDefs.ProfileViewBasic[]
|
AppBskyActorDefs.ProfileViewBasic[]
|
||||||
>([])
|
>([])
|
||||||
|
@ -302,7 +287,6 @@ function SearchScreenUserResults({query}: {query: string}) {
|
||||||
const searchResults = await search({query, limit: 30})
|
const searchResults = await search({query, limit: 30})
|
||||||
|
|
||||||
if (searchResults) {
|
if (searchResults) {
|
||||||
setDataUpdatedAt(Date.now())
|
|
||||||
setResults(results)
|
setResults(results)
|
||||||
setIsFetched(true)
|
setIsFetched(true)
|
||||||
}
|
}
|
||||||
|
@ -314,7 +298,7 @@ function SearchScreenUserResults({query}: {query: string}) {
|
||||||
setResults([])
|
setResults([])
|
||||||
setIsFetched(false)
|
setIsFetched(false)
|
||||||
}
|
}
|
||||||
}, [query, setDataUpdatedAt, search, results])
|
}, [query, search, results])
|
||||||
|
|
||||||
return isFetched ? (
|
return isFetched ? (
|
||||||
<>
|
<>
|
||||||
|
@ -322,11 +306,7 @@ function SearchScreenUserResults({query}: {query: string}) {
|
||||||
<FlatList
|
<FlatList
|
||||||
data={results}
|
data={results}
|
||||||
renderItem={({item}) => (
|
renderItem={({item}) => (
|
||||||
<ProfileCardWithFollowBtn
|
<ProfileCardWithFollowBtn profile={item} noBg />
|
||||||
profile={item}
|
|
||||||
noBg
|
|
||||||
dataUpdatedAt={dataUpdatedAt}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
keyExtractor={item => item.did}
|
keyExtractor={item => item.did}
|
||||||
// @ts-ignore web only -prf
|
// @ts-ignore web only -prf
|
||||||
|
|
Loading…
Reference in New Issue