[Chat] Add chat invite as message embed type (#10728)

This commit is contained in:
Samuel Newman
2026-06-04 23:11:03 +03:00
committed by GitHub
parent 09fa93553b
commit ded2ca3744
20 changed files with 813 additions and 316 deletions
+7 -2
View File
@@ -6,6 +6,7 @@ import {
ChatBskyConvoDefs,
type ChatBskyConvoGetLog,
type ChatBskyConvoSendMessage,
type ChatBskyEmbedJoinLink,
type ChatBskyGroupDefs,
} from '@atproto/api'
import {XRPCError} from '@atproto/api'
@@ -109,7 +110,9 @@ export class Convo {
{
id: string
message: ChatBskyConvoSendMessage.InputSchema['message']
optimisticEmbedView?: $Typed<AppBskyEmbedRecord.View>
optimisticEmbedView?:
| $Typed<AppBskyEmbedRecord.View>
| $Typed<ChatBskyEmbedJoinLink.View>
}
> = new Map()
private deletedMessages: Set<string> = new Set()
@@ -942,7 +945,9 @@ export class Convo {
sendMessage(
message: ChatBskyConvoSendMessage.InputSchema['message'],
optimisticEmbedView?: $Typed<AppBskyEmbedRecord.View>,
optimisticEmbedView?:
| $Typed<AppBskyEmbedRecord.View>
| $Typed<ChatBskyEmbedJoinLink.View>,
) {
// Ignore empty messages for now since they have no other purpose atm
if (!message.text.trim() && !message.embed) return
+5 -1
View File
@@ -5,6 +5,7 @@ import {
type ChatBskyActorDefs,
type ChatBskyConvoDefs,
type ChatBskyConvoSendMessage,
type ChatBskyEmbedJoinLink,
} from '@atproto/api'
import {type MessagesEventBus} from '#/state/messages/events/agent'
@@ -108,7 +109,10 @@ export type ConvoItem =
type DeleteMessage = (messageId: string) => Promise<void>
type SendMessage = (
message: ChatBskyConvoSendMessage.InputSchema['message'],
optimisticEmbedView: $Typed<AppBskyEmbedRecord.View> | undefined,
optimisticEmbedView:
| $Typed<AppBskyEmbedRecord.View>
| $Typed<ChatBskyEmbedJoinLink.View>
| undefined,
) => void
type FetchMessageHistory = () => Promise<void>
type MarkConvoAccepted = () => void
+68 -19
View File
@@ -1,4 +1,9 @@
import {AtpAgent} from '@atproto/api'
import {useCallback} from 'react'
import {
AtpAgent,
type ChatBskyGroupDefs,
type ChatBskyGroupGetJoinLinkPreviews,
} from '@atproto/api'
import {useQuery, useQueryClient} from '@tanstack/react-query'
import {CHAT_SERVICE, DM_SERVICE_HEADERS} from '#/lib/constants'
@@ -17,14 +22,39 @@ export const createJoinLinkPreviewQueryKey = (args: {
persistedVersion: 1,
})
async function fetchJoinLinkPreviews({
agent,
codes,
hasSession,
}: {
agent: AtpAgent
codes: string[]
hasSession: boolean
}) {
const previewAgent = new AtpAgent({service: CHAT_SERVICE})
const res = hasSession
? await agent.chat.bsky.group.getJoinLinkPreviews(
{codes},
{headers: DM_SERVICE_HEADERS},
)
: await previewAgent.chat.bsky.group.getJoinLinkPreviews({codes})
return res.data
}
export function useJoinLinkPreviewsQuery({
codes,
hasSession,
staleTime = STALE.MINUTES.ONE,
initialData,
}: {
codes?: string[]
hasSession: boolean
staleTime?: number
/**
* Seed the query with an already-known preview (e.g. a DM message embed
* already carries the resolved view), avoiding a duplicate fetch.
*/
initialData?: ChatBskyGroupGetJoinLinkPreviews.OutputSchema
}) {
const agent = useAgent()
@@ -33,14 +63,7 @@ export function useJoinLinkPreviewsQuery({
queryFn: async () => {
if (!codes) throw new Error('No invite code')
try {
const previewAgent = new AtpAgent({service: CHAT_SERVICE})
const res = hasSession
? await agent.chat.bsky.group.getJoinLinkPreviews(
{codes},
{headers: DM_SERVICE_HEADERS},
)
: await previewAgent.chat.bsky.group.getJoinLinkPreviews({codes})
return res.data
return await fetchJoinLinkPreviews({agent, codes, hasSession})
} catch (error) {
logger.error('Failed to fetch join link preview', {safeMessage: error})
throw error
@@ -48,6 +71,7 @@ export function useJoinLinkPreviewsQuery({
},
enabled: codes != null && codes.length > 0,
staleTime,
initialData,
})
}
@@ -58,17 +82,42 @@ export function usePrefetchJoinLinkPreviews() {
return ({codes, hasSession}: {codes: string[]; hasSession: boolean}) => {
return queryClient.prefetchQuery({
queryKey: createJoinLinkPreviewQueryKey({codes, hasSession}),
queryFn: async () => {
const previewAgent = new AtpAgent({service: CHAT_SERVICE})
const res = hasSession
? await agent.chat.bsky.group.getJoinLinkPreviews(
{codes},
{headers: DM_SERVICE_HEADERS},
)
: await previewAgent.chat.bsky.group.getJoinLinkPreviews({codes})
return res.data
},
queryFn: () => fetchJoinLinkPreviews({agent, codes, hasSession}),
staleTime: STALE.MINUTES.ONE,
})
}
}
/**
* Imperatively fetch (or read from cache) a single join link preview by code.
* Used when sending a DM invite embed so we can build an optimistic view.
* Returns undefined if the preview can't be resolved.
*/
export function useGetJoinLinkPreview() {
const agent = useAgent()
const queryClient = useQueryClient()
return useCallback(
async ({
code,
hasSession,
}: {
code: string
hasSession: boolean
}): Promise<ChatBskyGroupDefs.JoinLinkPreviewView | undefined> => {
try {
const data = await queryClient.fetchQuery({
queryKey: createJoinLinkPreviewQueryKey({codes: [code], hasSession}),
queryFn: () =>
fetchJoinLinkPreviews({agent, codes: [code], hasSession}),
staleTime: STALE.MINUTES.ONE,
})
return data.joinLinkPreviews[0]
} catch (error) {
logger.error('Failed to fetch join link preview', {safeMessage: error})
return undefined
}
},
[agent, queryClient],
)
}