Prefetch data needed for interaction settings

This commit is contained in:
Eric Bailey
2024-08-12 16:18:06 -05:00
parent 5d78f69d95
commit 4004d9eeda
7 changed files with 169 additions and 46 deletions
+55 -25
View File
@@ -1,6 +1,8 @@
import React from 'react'
import {
AccessibilityProps,
GestureResponderEvent,
MouseEvent,
Pressable,
PressableProps,
StyleProp,
@@ -65,7 +67,15 @@ type NonTextElements =
export type ButtonProps = Pick<
PressableProps,
'disabled' | 'onPress' | 'testID' | 'onLongPress' | 'hitSlop'
| 'disabled'
| 'onPress'
| 'testID'
| 'onLongPress'
| 'hitSlop'
| 'onHoverIn'
| 'onHoverOut'
| 'onPressIn'
| 'onPressOut'
> &
AccessibilityProps &
VariantProps & {
@@ -115,30 +125,50 @@ export const Button = React.forwardRef<View, ButtonProps>(
focused: false,
})
const onPressIn = React.useCallback(() => {
setState(s => ({
...s,
pressed: true,
}))
}, [setState])
const onPressOut = React.useCallback(() => {
setState(s => ({
...s,
pressed: false,
}))
}, [setState])
const onHoverIn = React.useCallback(() => {
setState(s => ({
...s,
hovered: true,
}))
}, [setState])
const onHoverOut = React.useCallback(() => {
setState(s => ({
...s,
hovered: false,
}))
}, [setState])
const onPressInOuter = rest.onPressIn
const onPressIn = React.useCallback(
(e: GestureResponderEvent) => {
setState(s => ({
...s,
pressed: true,
}))
onPressInOuter?.(e)
},
[setState, onPressInOuter],
)
const onPressOutOuter = rest.onPressOut
const onPressOut = React.useCallback(
(e: GestureResponderEvent) => {
setState(s => ({
...s,
pressed: false,
}))
onPressOutOuter?.(e)
},
[setState, onPressOutOuter],
)
const onHoverInOuter = rest.onHoverIn
const onHoverIn = React.useCallback(
(e: MouseEvent) => {
setState(s => ({
...s,
hovered: true,
}))
onHoverInOuter?.(e)
},
[setState, onHoverInOuter],
)
const onHoverOutOuter = rest.onHoverOut
const onHoverOut = React.useCallback(
(e: MouseEvent) => {
setState(s => ({
...s,
hovered: false,
}))
onHoverOutOuter?.(e)
},
[setState, onHoverOutOuter],
)
const onFocus = React.useCallback(() => {
setState(s => ({
...s,
+8 -2
View File
@@ -125,8 +125,14 @@ export function Item({children, label, style, onPress, ...rest}: ItemProps) {
}}
onFocus={onFocus}
onBlur={onBlur}
onPressIn={onPressIn}
onPressOut={onPressOut}
onPressIn={e => {
onPressIn()
rest.onPressIn?.(e)
}}
onPressOut={e => {
onPressOut()
rest.onPressOut?.(e)
}}
style={[
a.flex_row,
a.align_center,
+20 -2
View File
@@ -1,5 +1,5 @@
import React from 'react'
import {Keyboard, StyleProp, View, ViewStyle} from 'react-native'
import {Keyboard, Platform, StyleProp, View, ViewStyle} from 'react-native'
import {
AppBskyFeedDefs,
AppBskyFeedPost,
@@ -20,12 +20,15 @@ import {atoms as a, useTheme} from '#/alf'
import {Button} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import {useDialogControl} from '#/components/Dialog'
import {
PostInteractionSettingsDialog,
usePrefetchPostInteractionSettings,
} from '#/components/dialogs/PostInteractionSettingsDialog'
import {CircleBanSign_Stroke2_Corner0_Rounded as CircleBanSign} from '#/components/icons/CircleBanSign'
import {Earth_Stroke2_Corner0_Rounded as Earth} from '#/components/icons/Globe'
import {Group3_Stroke2_Corner0_Rounded as Group} from '#/components/icons/Group'
import {InlineLinkText} from '#/components/Link'
import {Text} from '#/components/Typography'
import {PostInteractionSettingsDialog} from './dialogs/PostInteractionSettingsDialog'
import {PencilLine_Stroke2_Corner0_Rounded as PencilLine} from './icons/Pencil'
interface WhoCanReplyProps {
@@ -52,6 +55,11 @@ export function WhoCanReply({post, isThreadAuthor, style}: WhoCanReplyProps) {
return threadgateViewToAllowUISetting(post.threadgate)
}, [post.threadgate])
const prefetchPostInteractionSettings = usePrefetchPostInteractionSettings({
postUri: post.uri,
rootPostUri: rootUri,
})
const anyoneCanReply =
settings.length === 1 && settings[0].type === 'everybody'
const noOneCanReply = settings.length === 1 && settings[0].type === 'nobody'
@@ -79,6 +87,16 @@ export function WhoCanReply({post, isThreadAuthor, style}: WhoCanReplyProps) {
isThreadAuthor ? _(msg`Edit who can reply`) : _(msg`Who can reply`)
}
onPress={onPressOpen}
{...(isThreadAuthor
? Platform.select({
web: {
onHoverIn: prefetchPostInteractionSettings,
},
native: {
onPressIn: prefetchPostInteractionSettings,
},
})
: {})}
hitSlop={HITSLOP_10}>
{({hovered}) => (
<View style={[a.flex_row, a.align_center, a.gap_xs, style]}>
@@ -8,11 +8,15 @@ import {
} from '@atproto/api'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useQueryClient} from '@tanstack/react-query'
import isEqual from 'lodash.isequal'
import {logger} from '#/logger'
import {STALE} from '#/state/queries'
import {useMyListsQuery} from '#/state/queries/my-lists'
import {
createPostgateQueryKey,
getPostgateRecord,
usePostgateQuery,
useWritePostgateMutation,
} from '#/state/queries/postgate'
@@ -21,18 +25,19 @@ import {
embeddingRules,
} from '#/state/queries/postgate/util'
import {
createThreadgateViewQueryKey,
getThreadgateView,
ThreadgateAllowUISetting,
threadgateViewToAllowUISetting,
useSetThreadgateAllowMutation,
useThreadgateViewQuery,
} from '#/state/queries/threadgate'
import {useSession} from '#/state/session'
import {useAgent, useSession} from '#/state/session'
import * as Toast from '#/view/com/util/toast'
import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import {Divider} from '#/components/Divider'
import {useDelayedLoading} from '#/components/hooks/useDelayedLoading'
import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
import {Loader} from '#/components/Loader'
@@ -116,14 +121,13 @@ export function PostInteractionSettingsDialogControlledInner(
const {mutateAsync: writePostgateRecord} = useWritePostgateMutation()
const {mutateAsync: setThreadgateAllow} = useSetThreadgateAllowMutation()
const naturalLoading = useDelayedLoading(500) // TODO
const [editedPostgate, setEditedPostgate] =
React.useState<AppBskyFeedPostgate.Record>()
const [editedAllowUISettings, setEditedAllowUISettings] =
React.useState<ThreadgateAllowUISetting[]>()
const isLoading = isLoadingThreadgate || isLoadingPostgate || naturalLoading
const isLoading = isLoadingThreadgate || isLoadingPostgate
const threadgateView = threadgateViewLoaded || props.initialThreadgateView
const isThreadgateOwnedByViewer = React.useMemo(() => {
if (AppBskyFeedThreadgate.isRecord(threadgateView?.record)) {
@@ -504,3 +508,35 @@ function Selectable({
</Button>
)
}
export function usePrefetchPostInteractionSettings({
postUri,
rootPostUri,
}: {
postUri: string
rootPostUri: string
}) {
const queryClient = useQueryClient()
const agent = useAgent()
return React.useCallback(async () => {
try {
await Promise.all([
queryClient.prefetchQuery({
queryKey: createPostgateQueryKey(postUri),
queryFn: () => getPostgateRecord({agent, postUri}),
staleTime: STALE.SECONDS.THIRTY,
}),
queryClient.prefetchQuery({
queryKey: createThreadgateViewQueryKey(rootPostUri),
queryFn: () => getThreadgateView({agent, postUri: rootPostUri}),
staleTime: STALE.SECONDS.THIRTY,
}),
])
} catch (e: any) {
logger.error(`Failed to prefetch post interaction settings`, {
safeMessage: e.message,
})
}
}, [queryClient, agent, postUri, rootPostUri])
}
+3 -1
View File
@@ -9,6 +9,7 @@ import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
import {networkRetry, retry} from '#/lib/async/retry'
import {logger} from '#/logger'
import {updatePostShadow} from '#/state/cache/post-shadow'
import {STALE} from '#/state/queries'
import {useGetPosts} from '#/state/queries/post'
import {
createMaybeDetachedQuoteEmbed,
@@ -128,6 +129,7 @@ export const createPostgateQueryKey = (postUri: string) => [
export function usePostgateQuery({postUri}: {postUri: string}) {
const agent = useAgent()
return useQuery({
staleTime: STALE.SECONDS.THIRTY,
queryKey: createPostgateQueryKey(postUri),
async queryFn() {
return (await getPostgateRecord({agent, postUri})) ?? null
@@ -154,7 +156,7 @@ export function useWritePostgateMutation() {
},
onSuccess(_, {postUri}) {
queryClient.invalidateQueries({
queryKey: [createPostgateQueryKey(postUri)],
queryKey: createPostgateQueryKey(postUri),
})
},
})
+22 -10
View File
@@ -72,21 +72,33 @@ export function useThreadgateViewQuery({
placeholderData: initialData,
staleTime: STALE.MINUTES.ONE,
async queryFn() {
const {data} = await agent.app.bsky.feed.getPostThread({
uri: postUri!,
depth: 0,
return getThreadgateView({
agent,
postUri: postUri!,
})
console.log(data.thread)
if (AppBskyFeedDefs.isThreadViewPost(data.thread)) {
return data.thread.post.threadgate ?? null
}
return null
},
})
}
export async function getThreadgateView({
agent,
postUri,
}: {
agent: BskyAgent
postUri: string
}) {
const {data} = await agent.app.bsky.feed.getPostThread({
uri: postUri!,
depth: 0,
})
if (AppBskyFeedDefs.isThreadViewPost(data.thread)) {
return data.thread.post.threadgate ?? null
}
return null
}
export async function getThreadgateRecord({
agent,
postUri,
+21 -2
View File
@@ -1,5 +1,6 @@
import React, {memo} from 'react'
import {
Platform,
Pressable,
type PressableProps,
type StyleProp,
@@ -44,7 +45,10 @@ import {atoms as a, useBreakpoints, useTheme as useAlf} from '#/alf'
import {useDialogControl} from '#/components/Dialog'
import {useGlobalDialogsControlContext} from '#/components/dialogs/Context'
import {EmbedDialog} from '#/components/dialogs/Embed'
import {PostInteractionSettingsDialog} from '#/components/dialogs/PostInteractionSettingsDialog'
import {
PostInteractionSettingsDialog,
usePrefetchPostInteractionSettings,
} from '#/components/dialogs/PostInteractionSettingsDialog'
import {SendViaChatDialog} from '#/components/dms/dialogs/ShareViaChatDialog'
import {ArrowOutOfBox_Stroke2_Corner0_Rounded as Share} from '#/components/icons/ArrowOutOfBox'
import {BubbleQuestion_Stroke2_Corner0_Rounded as Translate} from '#/components/icons/Bubble'
@@ -144,6 +148,11 @@ let PostDropdownBtn = ({
const {mutateAsync: toggleQuoteDetachment, isPending} =
useToggleQuoteDetachmentMutation()
const prefetchPostInteractionSettings = usePrefetchPostInteractionSettings({
postUri: post.uri,
rootPostUri: rootUri,
})
const href = React.useMemo(() => {
const urip = new AtUri(postUri)
return makeProfileLink(postAuthor, 'post', urip.rkey)
@@ -556,7 +565,17 @@ let PostDropdownBtn = ({
<Menu.Item
testID="postDropdownEditPostInteractions"
label={_(msg`Edit interaction settings`)}
onPress={postInteractionSettingsDialogControl.open}>
onPress={postInteractionSettingsDialogControl.open}
{...(isAuthor
? Platform.select({
web: {
onHoverIn: prefetchPostInteractionSettings,
},
native: {
onPressIn: prefetchPostInteractionSettings,
},
})
: {})}>
<Menu.ItemText>
{_(msg`Edit interaction settings`)}
</Menu.ItemText>