diff --git a/src/state/cache/post-shadow.ts b/src/state/cache/post-shadow.ts index c06ed60c4e..c490977008 100644 --- a/src/state/cache/post-shadow.ts +++ b/src/state/cache/post-shadow.ts @@ -1,6 +1,8 @@ import {useEffect, useState, useCallback, useRef} from 'react' import EventEmitter from 'eventemitter3' import {AppBskyFeedDefs} from '@atproto/api' +import {Shadow} from './types' +export type {Shadow} from './types' const emitter = new EventEmitter() @@ -22,7 +24,7 @@ interface CacheEntry { export function usePostShadow( post: AppBskyFeedDefs.PostView, ifAfterTS: number, -): AppBskyFeedDefs.PostView | typeof POST_TOMBSTONE { +): Shadow | typeof POST_TOMBSTONE { const [state, setState] = useState({ ts: Date.now(), value: fromPost(post), @@ -53,13 +55,21 @@ export function usePostShadow( firstRun.current = false }, [post]) - return state.ts > ifAfterTS ? mergeShadow(post, state.value) : post + return state.ts > ifAfterTS + ? mergeShadow(post, state.value) + : {...post, isShadowed: true} } export function updatePostShadow(uri: string, value: Partial) { emitter.emit(uri, value) } +export function isPostShadowed( + v: AppBskyFeedDefs.PostView | Shadow, +): v is Shadow { + return 'isShadowed' in v && !!v.isShadowed +} + function fromPost(post: AppBskyFeedDefs.PostView): PostShadow { return { likeUri: post.viewer?.like, @@ -73,7 +83,7 @@ function fromPost(post: AppBskyFeedDefs.PostView): PostShadow { function mergeShadow( post: AppBskyFeedDefs.PostView, shadow: PostShadow, -): AppBskyFeedDefs.PostView | typeof POST_TOMBSTONE { +): Shadow | typeof POST_TOMBSTONE { if (shadow.isDeleted) { return POST_TOMBSTONE } @@ -86,5 +96,6 @@ function mergeShadow( like: shadow.likeUri, repost: shadow.repostUri, }, + isShadowed: true, } } diff --git a/src/state/cache/profile-shadow.ts b/src/state/cache/profile-shadow.ts index a1cf59954f..59f79634d1 100644 --- a/src/state/cache/profile-shadow.ts +++ b/src/state/cache/profile-shadow.ts @@ -1,6 +1,8 @@ import {useEffect, useState, useCallback, useRef} from 'react' import EventEmitter from 'eventemitter3' import {AppBskyActorDefs} from '@atproto/api' +import {Shadow} from './types' +export type {Shadow} from './types' const emitter = new EventEmitter() @@ -20,10 +22,10 @@ type ProfileView = | AppBskyActorDefs.ProfileViewBasic | AppBskyActorDefs.ProfileViewDetailed -export function useProfileShadow( - profile: T, +export function useProfileShadow( + profile: ProfileView, ifAfterTS: number, -): T { +): Shadow { const [state, setState] = useState({ ts: Date.now(), value: fromProfile(profile), @@ -54,7 +56,9 @@ export function useProfileShadow( firstRun.current = false }, [profile]) - return state.ts > ifAfterTS ? mergeShadow(profile, state.value) : profile + return state.ts > ifAfterTS + ? mergeShadow(profile, state.value) + : {...profile, isShadowed: true} } export function updateProfileShadow( @@ -64,6 +68,12 @@ export function updateProfileShadow( emitter.emit(uri, value) } +export function isProfileShadowed( + v: T | Shadow, +): v is Shadow { + return 'isShadowed' in v && !!v.isShadowed +} + function fromProfile(profile: ProfileView): ProfileShadow { return { followingUri: profile.viewer?.following, @@ -72,10 +82,10 @@ function fromProfile(profile: ProfileView): ProfileShadow { } } -function mergeShadow( - profile: T, +function mergeShadow( + profile: ProfileView, shadow: ProfileShadow, -): T { +): Shadow { return { ...profile, viewer: { @@ -84,5 +94,6 @@ function mergeShadow( muted: shadow.muted, blocking: shadow.blockingUri, }, + isShadowed: true, } } diff --git a/src/state/cache/types.ts b/src/state/cache/types.ts new file mode 100644 index 0000000000..8bfcc867c9 --- /dev/null +++ b/src/state/cache/types.ts @@ -0,0 +1 @@ +export type Shadow = T & {isShadowed: true} diff --git a/src/view/com/auth/onboarding/RecommendedFollowsItem.tsx b/src/view/com/auth/onboarding/RecommendedFollowsItem.tsx index f52b312136..144cc6cd4f 100644 --- a/src/view/com/auth/onboarding/RecommendedFollowsItem.tsx +++ b/src/view/com/auth/onboarding/RecommendedFollowsItem.tsx @@ -13,7 +13,7 @@ import Animated, {FadeInRight} from 'react-native-reanimated' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {useAnalytics} from 'lib/analytics/analytics' import {Trans} from '@lingui/macro' -import {useProfileShadow} from '#/state/cache/profile-shadow' +import {Shadow, useProfileShadow} from '#/state/cache/profile-shadow' import { useProfileFollowMutation, useProfileUnfollowMutation, @@ -66,7 +66,14 @@ export function ProfileCard({ profile, onFollowStateChange, moderation, -}: Omit) { +}: { + profile: Shadow + moderation: ProfileModeration + onFollowStateChange: (props: { + did: string + following: boolean + }) => Promise +}) { const {track} = useAnalytics() const pal = usePalette('default') const [addingMoreSuggestions, setAddingMoreSuggestions] = diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index c81b762c33..0e2fb7080c 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -37,9 +37,9 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {MAX_POST_LINES} from 'lib/constants' import {Trans} from '@lingui/macro' import {useLanguagePrefs} from '#/state/preferences' -import {usePostShadow, POST_TOMBSTONE} from '#/state/cache/post-shadow' import {useComposerControls} from '#/state/shell/composer' import {useModerationOpts} from '#/state/queries/preferences' +import {Shadow, usePostShadow, POST_TOMBSTONE} from '#/state/cache/post-shadow' export function PostThreadItem({ post, @@ -132,7 +132,7 @@ function PostThreadItemLoaded({ hasPrecedingItem, onPostReply, }: { - post: AppBskyFeedDefs.PostView + post: Shadow record: AppBskyFeedPost.Record richText: RichTextAPI moderation: PostModeration diff --git a/src/view/com/post/Post.tsx b/src/view/com/post/Post.tsx index 09edbe12f6..a2a0e62e60 100644 --- a/src/view/com/post/Post.tsx +++ b/src/view/com/post/Post.tsx @@ -25,8 +25,8 @@ import {makeProfileLink} from 'lib/routes/links' import {MAX_POST_LINES} from 'lib/constants' import {countLines} from 'lib/strings/helpers' import {useModerationOpts} from '#/state/queries/preferences' -import {usePostShadow, POST_TOMBSTONE} from '#/state/cache/post-shadow' import {useComposerControls} from '#/state/shell/composer' +import {Shadow, usePostShadow, POST_TOMBSTONE} from '#/state/cache/post-shadow' export function Post({ post, @@ -89,7 +89,7 @@ function PostInner({ showReplyLine, style, }: { - post: AppBskyFeedDefs.PostView + post: Shadow record: AppBskyFeedPost.Record richText: RichTextAPI moderation: PostModeration diff --git a/src/view/com/posts/FeedItem.tsx b/src/view/com/posts/FeedItem.tsx index 31981cc549..5bc173f007 100644 --- a/src/view/com/posts/FeedItem.tsx +++ b/src/view/com/posts/FeedItem.tsx @@ -32,8 +32,8 @@ import {makeProfileLink} from 'lib/routes/links' import {isEmbedByEmbedder} from 'lib/embeds' import {MAX_POST_LINES} from 'lib/constants' import {countLines} from 'lib/strings/helpers' -import {usePostShadow, POST_TOMBSTONE} from '#/state/cache/post-shadow' import {useComposerControls} from '#/state/shell/composer' +import {Shadow, usePostShadow, POST_TOMBSTONE} from '#/state/cache/post-shadow' export function FeedItem({ post, @@ -93,7 +93,7 @@ function FeedItemInner({ isThreadLastChild, isThreadParent, }: { - post: AppBskyFeedDefs.PostView + post: Shadow record: AppBskyFeedPost.Record reason: AppBskyFeedDefs.ReasonRepost | ReasonFeedSource | undefined richText: RichTextAPI diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx index a228891a42..92d51ac701 100644 --- a/src/view/com/profile/ProfileHeader.tsx +++ b/src/view/com/profile/ProfileHeader.tsx @@ -54,9 +54,10 @@ import {shareUrl} from 'lib/sharing' import {s, colors} from 'lib/styles' import {logger} from '#/logger' import {useSession} from '#/state/session' +import {Shadow} from '#/state/cache/types' interface Props { - profile: AppBskyActorDefs.ProfileViewDetailed + profile: Shadow moderation: ProfileModeration hideBackButton?: boolean isProfilePreview?: boolean diff --git a/src/view/com/util/forms/PostDropdownBtn.tsx b/src/view/com/util/forms/PostDropdownBtn.tsx index c457e0a461..dfaa77c1c0 100644 --- a/src/view/com/util/forms/PostDropdownBtn.tsx +++ b/src/view/com/util/forms/PostDropdownBtn.tsx @@ -20,6 +20,7 @@ import {usePostDeleteMutation} from '#/state/queries/post' import {useMutedThreads, useToggleThreadMute} from '#/state/muted-threads' import {useLanguagePrefs} from '#/state/preferences' import {logger} from '#/logger' +import {Shadow} from '#/state/cache/types' export function PostDropdownBtn({ testID, @@ -28,7 +29,7 @@ export function PostDropdownBtn({ style, }: { testID: string - post: AppBskyFeedDefs.PostView + post: Shadow record: AppBskyFeedPost.Record style?: StyleProp }) { diff --git a/src/view/com/util/post-ctrls/PostCtrls.tsx b/src/view/com/util/post-ctrls/PostCtrls.tsx index 7e95bde873..120aecf45e 100644 --- a/src/view/com/util/post-ctrls/PostCtrls.tsx +++ b/src/view/com/util/post-ctrls/PostCtrls.tsx @@ -24,6 +24,7 @@ import { usePostUnrepostMutation, } from '#/state/queries/post' import {useComposerControls} from '#/state/shell/composer' +import {Shadow} from '#/state/cache/types' export function PostCtrls({ big, @@ -33,7 +34,7 @@ export function PostCtrls({ onPressReply, }: { big?: boolean - post: AppBskyFeedDefs.PostView + post: Shadow record: AppBskyFeedPost.Record style?: StyleProp onPressReply: () => void