Add Shadow type (#1900)
parent
00f8c8b06d
commit
8e4a3ad5b6
|
@ -1,6 +1,8 @@
|
||||||
import {useEffect, useState, useCallback, useRef} from 'react'
|
import {useEffect, useState, useCallback, useRef} from 'react'
|
||||||
import EventEmitter from 'eventemitter3'
|
import EventEmitter from 'eventemitter3'
|
||||||
import {AppBskyFeedDefs} from '@atproto/api'
|
import {AppBskyFeedDefs} from '@atproto/api'
|
||||||
|
import {Shadow} from './types'
|
||||||
|
export type {Shadow} from './types'
|
||||||
|
|
||||||
const emitter = new EventEmitter()
|
const emitter = new EventEmitter()
|
||||||
|
|
||||||
|
@ -22,7 +24,7 @@ interface CacheEntry {
|
||||||
export function usePostShadow(
|
export function usePostShadow(
|
||||||
post: AppBskyFeedDefs.PostView,
|
post: AppBskyFeedDefs.PostView,
|
||||||
ifAfterTS: number,
|
ifAfterTS: number,
|
||||||
): AppBskyFeedDefs.PostView | typeof POST_TOMBSTONE {
|
): Shadow<AppBskyFeedDefs.PostView> | typeof POST_TOMBSTONE {
|
||||||
const [state, setState] = useState<CacheEntry>({
|
const [state, setState] = useState<CacheEntry>({
|
||||||
ts: Date.now(),
|
ts: Date.now(),
|
||||||
value: fromPost(post),
|
value: fromPost(post),
|
||||||
|
@ -53,13 +55,21 @@ export function usePostShadow(
|
||||||
firstRun.current = false
|
firstRun.current = false
|
||||||
}, [post])
|
}, [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<PostShadow>) {
|
export function updatePostShadow(uri: string, value: Partial<PostShadow>) {
|
||||||
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 {
|
||||||
return {
|
return {
|
||||||
likeUri: post.viewer?.like,
|
likeUri: post.viewer?.like,
|
||||||
|
@ -73,7 +83,7 @@ function fromPost(post: AppBskyFeedDefs.PostView): PostShadow {
|
||||||
function mergeShadow(
|
function mergeShadow(
|
||||||
post: AppBskyFeedDefs.PostView,
|
post: AppBskyFeedDefs.PostView,
|
||||||
shadow: PostShadow,
|
shadow: PostShadow,
|
||||||
): AppBskyFeedDefs.PostView | typeof POST_TOMBSTONE {
|
): Shadow<AppBskyFeedDefs.PostView> | typeof POST_TOMBSTONE {
|
||||||
if (shadow.isDeleted) {
|
if (shadow.isDeleted) {
|
||||||
return POST_TOMBSTONE
|
return POST_TOMBSTONE
|
||||||
}
|
}
|
||||||
|
@ -86,5 +96,6 @@ function mergeShadow(
|
||||||
like: shadow.likeUri,
|
like: shadow.likeUri,
|
||||||
repost: shadow.repostUri,
|
repost: shadow.repostUri,
|
||||||
},
|
},
|
||||||
|
isShadowed: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import {useEffect, useState, useCallback, useRef} from 'react'
|
import {useEffect, useState, useCallback, useRef} from 'react'
|
||||||
import EventEmitter from 'eventemitter3'
|
import EventEmitter from 'eventemitter3'
|
||||||
import {AppBskyActorDefs} from '@atproto/api'
|
import {AppBskyActorDefs} from '@atproto/api'
|
||||||
|
import {Shadow} from './types'
|
||||||
|
export type {Shadow} from './types'
|
||||||
|
|
||||||
const emitter = new EventEmitter()
|
const emitter = new EventEmitter()
|
||||||
|
|
||||||
|
@ -20,10 +22,10 @@ type ProfileView =
|
||||||
| AppBskyActorDefs.ProfileViewBasic
|
| AppBskyActorDefs.ProfileViewBasic
|
||||||
| AppBskyActorDefs.ProfileViewDetailed
|
| AppBskyActorDefs.ProfileViewDetailed
|
||||||
|
|
||||||
export function useProfileShadow<T extends ProfileView>(
|
export function useProfileShadow(
|
||||||
profile: T,
|
profile: ProfileView,
|
||||||
ifAfterTS: number,
|
ifAfterTS: number,
|
||||||
): T {
|
): Shadow<ProfileView> {
|
||||||
const [state, setState] = useState<CacheEntry>({
|
const [state, setState] = useState<CacheEntry>({
|
||||||
ts: Date.now(),
|
ts: Date.now(),
|
||||||
value: fromProfile(profile),
|
value: fromProfile(profile),
|
||||||
|
@ -54,7 +56,9 @@ export function useProfileShadow<T extends ProfileView>(
|
||||||
firstRun.current = false
|
firstRun.current = false
|
||||||
}, [profile])
|
}, [profile])
|
||||||
|
|
||||||
return state.ts > ifAfterTS ? mergeShadow(profile, state.value) : profile
|
return state.ts > ifAfterTS
|
||||||
|
? mergeShadow(profile, state.value)
|
||||||
|
: {...profile, isShadowed: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateProfileShadow(
|
export function updateProfileShadow(
|
||||||
|
@ -64,6 +68,12 @@ export function updateProfileShadow(
|
||||||
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 {
|
||||||
return {
|
return {
|
||||||
followingUri: profile.viewer?.following,
|
followingUri: profile.viewer?.following,
|
||||||
|
@ -72,10 +82,10 @@ function fromProfile(profile: ProfileView): ProfileShadow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mergeShadow<T extends ProfileView>(
|
function mergeShadow(
|
||||||
profile: T,
|
profile: ProfileView,
|
||||||
shadow: ProfileShadow,
|
shadow: ProfileShadow,
|
||||||
): T {
|
): Shadow<ProfileView> {
|
||||||
return {
|
return {
|
||||||
...profile,
|
...profile,
|
||||||
viewer: {
|
viewer: {
|
||||||
|
@ -84,5 +94,6 @@ function mergeShadow<T extends ProfileView>(
|
||||||
muted: shadow.muted,
|
muted: shadow.muted,
|
||||||
blocking: shadow.blockingUri,
|
blocking: shadow.blockingUri,
|
||||||
},
|
},
|
||||||
|
isShadowed: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
export type Shadow<T> = T & {isShadowed: true}
|
|
@ -13,7 +13,7 @@ import Animated, {FadeInRight} from 'react-native-reanimated'
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
import {useAnalytics} from 'lib/analytics/analytics'
|
import {useAnalytics} from 'lib/analytics/analytics'
|
||||||
import {Trans} from '@lingui/macro'
|
import {Trans} from '@lingui/macro'
|
||||||
import {useProfileShadow} from '#/state/cache/profile-shadow'
|
import {Shadow, useProfileShadow} from '#/state/cache/profile-shadow'
|
||||||
import {
|
import {
|
||||||
useProfileFollowMutation,
|
useProfileFollowMutation,
|
||||||
useProfileUnfollowMutation,
|
useProfileUnfollowMutation,
|
||||||
|
@ -66,7 +66,14 @@ export function ProfileCard({
|
||||||
profile,
|
profile,
|
||||||
onFollowStateChange,
|
onFollowStateChange,
|
||||||
moderation,
|
moderation,
|
||||||
}: Omit<Props, 'dataUpdatedAt'>) {
|
}: {
|
||||||
|
profile: Shadow<SuggestedActor>
|
||||||
|
moderation: ProfileModeration
|
||||||
|
onFollowStateChange: (props: {
|
||||||
|
did: string
|
||||||
|
following: boolean
|
||||||
|
}) => Promise<void>
|
||||||
|
}) {
|
||||||
const {track} = useAnalytics()
|
const {track} = useAnalytics()
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const [addingMoreSuggestions, setAddingMoreSuggestions] =
|
const [addingMoreSuggestions, setAddingMoreSuggestions] =
|
||||||
|
|
|
@ -37,9 +37,9 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
import {MAX_POST_LINES} from 'lib/constants'
|
import {MAX_POST_LINES} from 'lib/constants'
|
||||||
import {Trans} from '@lingui/macro'
|
import {Trans} from '@lingui/macro'
|
||||||
import {useLanguagePrefs} from '#/state/preferences'
|
import {useLanguagePrefs} from '#/state/preferences'
|
||||||
import {usePostShadow, POST_TOMBSTONE} from '#/state/cache/post-shadow'
|
|
||||||
import {useComposerControls} from '#/state/shell/composer'
|
import {useComposerControls} from '#/state/shell/composer'
|
||||||
import {useModerationOpts} from '#/state/queries/preferences'
|
import {useModerationOpts} from '#/state/queries/preferences'
|
||||||
|
import {Shadow, usePostShadow, POST_TOMBSTONE} from '#/state/cache/post-shadow'
|
||||||
|
|
||||||
export function PostThreadItem({
|
export function PostThreadItem({
|
||||||
post,
|
post,
|
||||||
|
@ -132,7 +132,7 @@ function PostThreadItemLoaded({
|
||||||
hasPrecedingItem,
|
hasPrecedingItem,
|
||||||
onPostReply,
|
onPostReply,
|
||||||
}: {
|
}: {
|
||||||
post: AppBskyFeedDefs.PostView
|
post: Shadow<AppBskyFeedDefs.PostView>
|
||||||
record: AppBskyFeedPost.Record
|
record: AppBskyFeedPost.Record
|
||||||
richText: RichTextAPI
|
richText: RichTextAPI
|
||||||
moderation: PostModeration
|
moderation: PostModeration
|
||||||
|
|
|
@ -25,8 +25,8 @@ import {makeProfileLink} from 'lib/routes/links'
|
||||||
import {MAX_POST_LINES} from 'lib/constants'
|
import {MAX_POST_LINES} from 'lib/constants'
|
||||||
import {countLines} from 'lib/strings/helpers'
|
import {countLines} from 'lib/strings/helpers'
|
||||||
import {useModerationOpts} from '#/state/queries/preferences'
|
import {useModerationOpts} from '#/state/queries/preferences'
|
||||||
import {usePostShadow, POST_TOMBSTONE} from '#/state/cache/post-shadow'
|
|
||||||
import {useComposerControls} from '#/state/shell/composer'
|
import {useComposerControls} from '#/state/shell/composer'
|
||||||
|
import {Shadow, usePostShadow, POST_TOMBSTONE} from '#/state/cache/post-shadow'
|
||||||
|
|
||||||
export function Post({
|
export function Post({
|
||||||
post,
|
post,
|
||||||
|
@ -89,7 +89,7 @@ function PostInner({
|
||||||
showReplyLine,
|
showReplyLine,
|
||||||
style,
|
style,
|
||||||
}: {
|
}: {
|
||||||
post: AppBskyFeedDefs.PostView
|
post: Shadow<AppBskyFeedDefs.PostView>
|
||||||
record: AppBskyFeedPost.Record
|
record: AppBskyFeedPost.Record
|
||||||
richText: RichTextAPI
|
richText: RichTextAPI
|
||||||
moderation: PostModeration
|
moderation: PostModeration
|
||||||
|
|
|
@ -32,8 +32,8 @@ import {makeProfileLink} from 'lib/routes/links'
|
||||||
import {isEmbedByEmbedder} from 'lib/embeds'
|
import {isEmbedByEmbedder} from 'lib/embeds'
|
||||||
import {MAX_POST_LINES} from 'lib/constants'
|
import {MAX_POST_LINES} from 'lib/constants'
|
||||||
import {countLines} from 'lib/strings/helpers'
|
import {countLines} from 'lib/strings/helpers'
|
||||||
import {usePostShadow, POST_TOMBSTONE} from '#/state/cache/post-shadow'
|
|
||||||
import {useComposerControls} from '#/state/shell/composer'
|
import {useComposerControls} from '#/state/shell/composer'
|
||||||
|
import {Shadow, usePostShadow, POST_TOMBSTONE} from '#/state/cache/post-shadow'
|
||||||
|
|
||||||
export function FeedItem({
|
export function FeedItem({
|
||||||
post,
|
post,
|
||||||
|
@ -93,7 +93,7 @@ function FeedItemInner({
|
||||||
isThreadLastChild,
|
isThreadLastChild,
|
||||||
isThreadParent,
|
isThreadParent,
|
||||||
}: {
|
}: {
|
||||||
post: AppBskyFeedDefs.PostView
|
post: Shadow<AppBskyFeedDefs.PostView>
|
||||||
record: AppBskyFeedPost.Record
|
record: AppBskyFeedPost.Record
|
||||||
reason: AppBskyFeedDefs.ReasonRepost | ReasonFeedSource | undefined
|
reason: AppBskyFeedDefs.ReasonRepost | ReasonFeedSource | undefined
|
||||||
richText: RichTextAPI
|
richText: RichTextAPI
|
||||||
|
|
|
@ -54,9 +54,10 @@ import {shareUrl} from 'lib/sharing'
|
||||||
import {s, colors} from 'lib/styles'
|
import {s, colors} from 'lib/styles'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
|
import {Shadow} from '#/state/cache/types'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
profile: AppBskyActorDefs.ProfileViewDetailed
|
profile: Shadow<AppBskyActorDefs.ProfileViewDetailed>
|
||||||
moderation: ProfileModeration
|
moderation: ProfileModeration
|
||||||
hideBackButton?: boolean
|
hideBackButton?: boolean
|
||||||
isProfilePreview?: boolean
|
isProfilePreview?: boolean
|
||||||
|
|
|
@ -20,6 +20,7 @@ import {usePostDeleteMutation} from '#/state/queries/post'
|
||||||
import {useMutedThreads, useToggleThreadMute} from '#/state/muted-threads'
|
import {useMutedThreads, useToggleThreadMute} from '#/state/muted-threads'
|
||||||
import {useLanguagePrefs} from '#/state/preferences'
|
import {useLanguagePrefs} from '#/state/preferences'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
|
import {Shadow} from '#/state/cache/types'
|
||||||
|
|
||||||
export function PostDropdownBtn({
|
export function PostDropdownBtn({
|
||||||
testID,
|
testID,
|
||||||
|
@ -28,7 +29,7 @@ export function PostDropdownBtn({
|
||||||
style,
|
style,
|
||||||
}: {
|
}: {
|
||||||
testID: string
|
testID: string
|
||||||
post: AppBskyFeedDefs.PostView
|
post: Shadow<AppBskyFeedDefs.PostView>
|
||||||
record: AppBskyFeedPost.Record
|
record: AppBskyFeedPost.Record
|
||||||
style?: StyleProp<ViewStyle>
|
style?: StyleProp<ViewStyle>
|
||||||
}) {
|
}) {
|
||||||
|
|
|
@ -24,6 +24,7 @@ import {
|
||||||
usePostUnrepostMutation,
|
usePostUnrepostMutation,
|
||||||
} from '#/state/queries/post'
|
} from '#/state/queries/post'
|
||||||
import {useComposerControls} from '#/state/shell/composer'
|
import {useComposerControls} from '#/state/shell/composer'
|
||||||
|
import {Shadow} from '#/state/cache/types'
|
||||||
|
|
||||||
export function PostCtrls({
|
export function PostCtrls({
|
||||||
big,
|
big,
|
||||||
|
@ -33,7 +34,7 @@ export function PostCtrls({
|
||||||
onPressReply,
|
onPressReply,
|
||||||
}: {
|
}: {
|
||||||
big?: boolean
|
big?: boolean
|
||||||
post: AppBskyFeedDefs.PostView
|
post: Shadow<AppBskyFeedDefs.PostView>
|
||||||
record: AppBskyFeedPost.Record
|
record: AppBskyFeedPost.Record
|
||||||
style?: StyleProp<ViewStyle>
|
style?: StyleProp<ViewStyle>
|
||||||
onPressReply: () => void
|
onPressReply: () => void
|
||||||
|
|
Loading…
Reference in New Issue