Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 113 additions & 3 deletions components/debug-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,33 @@ import {
} from "@/features/notifications/notifications.service"
import { getXmtpConversationIdFromXmtpTopic } from "@/features/xmtp/xmtp-conversations/xmtp-conversation"
import { getXmtpConversations } from "@/features/xmtp/xmtp-conversations/xmtp-conversations-list"
import { syncAllXmtpConversations } from "@/features/xmtp/xmtp-conversations/xmtp-conversations-sync"
import {
syncAllXmtpConversations,
syncOneXmtpConversation,
} from "@/features/xmtp/xmtp-conversations/xmtp-conversations-sync"
import { getXmtpDisappearingMessageSettings } from "@/features/xmtp/xmtp-disappearing-messages/xmtp-disappearing-messages"
import {
clearXmtpLogFiles,
clearXmtpLogs,
getXmtpLogFile,
startXmtpFileLogging,
stopXmtpFileLogging,
} from "@/features/xmtp/xmtp-logs"
import type { IXmtpInboxId } from "@/features/xmtp/xmtp.types"
import { getXmtpConversationMessages } from "@/features/xmtp/xmtp-messages/xmtp-messages"
import type { IXmtpDecodedMessage, IXmtpInboxId } from "@/features/xmtp/xmtp.types"
import { translate } from "@/i18n"
import { navigate } from "@/navigation/navigation.utils"
import { getCurrentRouteParams, navigate } from "@/navigation/navigation.utils"
import { useAppStore } from "@/stores/app.store"
import { captureError } from "@/utils/capture-error"
import { convertNanosecondsToMilliseconds } from "@/utils/date"
import { GenericError } from "@/utils/error"
import { getEnv, isProd } from "@/utils/getEnv"
import { Haptics } from "@/utils/haptics"
import { clearImageCache } from "@/utils/image"
import { clearLogFile, LOG_FILE_PATH } from "@/utils/logger/logger"
import { clearReacyQueryQueriesAndCache } from "@/utils/react-query/react-query.utils"
import { shareContent } from "@/utils/share"
import { getHumanReadableTimeFromMs } from "@/utils/time.utils"
import { showActionSheet } from "./action-sheet"

export const DebugMenuWrapper = memo(function DebugWrapper(props: { children: React.ReactNode }) {
Expand Down Expand Up @@ -459,6 +466,109 @@ function useShowDebugMenu() {
const clientInboxId = currentSenderInboxId as IXmtpInboxId

const xmtpMethods = {
"Get last 10 XMTP messages": async () => {
try {
useAppStore.getState().actions.setFullScreenLoaderOptions({
isVisible: true,
texts: ["Loading messages..."],
})

const params = getCurrentRouteParams<"Conversation">()
const conversationId = params?.xmtpConversationId

if (!conversationId) {
Alert.alert("Error", "Select this debug option in a conversation")
return
}

await syncOneXmtpConversation({
clientInboxId,
conversationId,
caller: "debugMenu",
})

const messages = await getXmtpConversationMessages({
clientInboxId,
xmtpConversationId: conversationId,
limit: 10,
})

const messageContents = messages
.map((msg: IXmtpDecodedMessage) => JSON.stringify(msg.nativeContent))
.join("\n\n")
Alert.alert("Last few Messages", messageContents)
} catch (error) {
captureError(
new GenericError({
error,
additionalMessage: "Error getting messages",
}),
)
Alert.alert("Error", "Failed to get messages")
} finally {
useAppStore.getState().actions.setFullScreenLoaderOptions({
isVisible: false,
})
}
},
"Get disappearing message settings": async () => {
try {
useAppStore.getState().actions.setFullScreenLoaderOptions({
isVisible: true,
texts: ["Loading disappearing message settings..."],
})

const params = getCurrentRouteParams<"Conversation">()

const conversationId = params?.xmtpConversationId

if (!conversationId) {
Alert.alert("Error", "Select this debug option in a conversation")
return
}

await syncOneXmtpConversation({
clientInboxId,
conversationId,
caller: "debugMenu",
})

const settings = await getXmtpDisappearingMessageSettings({
clientInboxId,
conversationId,
})

if (!settings) {
Alert.alert("Error", "No disappearing message settings found for this conversation")
return
}

const formattedSettings = {
disappearStartingAt: new Date(
convertNanosecondsToMilliseconds(settings.disappearStartingAtNs),
).toLocaleString(),
retentionDuration: getHumanReadableTimeFromMs(
convertNanosecondsToMilliseconds(settings.retentionDurationInNs),
),
}
Alert.alert(
"Disappearing Message Settings",
`Messages will start disappearing at:\n${formattedSettings.disappearStartingAt}\n\nMessages will be retained for:\n${formattedSettings.retentionDuration}`,
)
} catch (error) {
captureError(
new GenericError({
error,
additionalMessage: "Error getting disappearing message settings",
}),
)
Alert.alert("Error", "Failed to get disappearing message settings")
} finally {
useAppStore.getState().actions.setFullScreenLoaderOptions({
isVisible: false,
})
}
},
"List Allowed XMTP Conversations": async () => {
try {
useAppStore.getState().actions.setFullScreenLoaderOptions({
Expand Down
51 changes: 51 additions & 0 deletions components/prompt-async.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Alert, AlertOptions } from "react-native"

/**
* Creates an Alert with a text input that returns a Promise which resolves with
* the input value when submitted, or undefined if canceled.
*/

export function promptAsync(args: {
title: string
message?: string
type?: "plain-text" | "secure-text"
defaultValue?: string
submitText?: string
cancelText?: string
keyboardType?: string
options?: AlertOptions
}) {
const {
title,
message,
type = "plain-text",
defaultValue = "",
submitText = "OK",
cancelText = "Cancel",
keyboardType,
options,
} = args

return new Promise<{ value: string | undefined }>((resolve) => {
Alert.prompt(
title,
message,
[
{
text: cancelText,
style: "cancel",
onPress: () => resolve({ value: undefined }),
},
{
text: submitText,
style: "default",
onPress: (value?: string) => resolve({ value }),
},
],
type,
defaultValue,
keyboardType,
options,
)
})
}
Comment on lines +8 to +51

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Add platform compatibility considerations.

Alert.prompt is iOS-only in React Native. Consider adding platform checks or documenting this limitation to prevent runtime errors on Android.

Add platform check or documentation:

+import { Platform } from "react-native"
+
 /**
  * Creates an Alert with a text input that returns a Promise which resolves with
  * the input value when submitted, or undefined if canceled.
+ * Note: iOS only - Alert.prompt is not available on Android
  */
 
 export function promptAsync(args: {
   // ... existing args
 }) {
+  if (Platform.OS !== 'ios') {
+    return Promise.resolve({ value: undefined })
+  }
+
   // ... rest of implementation
 }

🏁 Script executed:

#!/bin/bash
# Check if there are any cross-platform prompt solutions already in use
rg -A 5 -B 5 "Alert\.prompt|react-native-prompt" --type ts --type tsx

Length of output: 99


#!/bin/bash
# Search for usage of Alert.prompt in TS and TSX files
rg -A 5 -B 5 "Alert\.prompt" --glob "*.ts" --glob "*.tsx"
# Search for any import or reference to react-native-prompt
rg -A 5 -B 5 "react-native-prompt" --glob "*.ts" --glob "*.tsx"

#!/bin/bash
# Search for any usage of Alert.prompt across the repository
rg -A5 -B5 "Alert\.prompt" .

# Search for any references to react-native-prompt (cross-platform lib)
rg -A5 -B5 "react-native-prompt" .

# Check if Platform is already imported in prompt-async file
rg "import.*Platform" -g "*prompt-async.ts"

#!/bin/bash
# Search for any usage of Alert.prompt across the repository
rg -A5 -B5 "Alert\.prompt" .

# Search for any references to a cross-platform prompt library
rg -A5 -B5 "react-native-prompt" .

# Check if Platform is already imported in the prompt-async implementation
rg "import.*Platform" -g "components/prompt-async.ts"

🏁 Script executed:

#!/bin/bash
# Search for Alert.prompt usage in TypeScript files
rg -n "Alert\\.prompt" --glob "*.ts" --glob "*.tsx" .

# Check if Platform is imported in prompt-async.ts
rg -n "import .*Platform" components/prompt-async.ts

# Look for any cross-platform prompt libraries
rg -n "react-native-prompt" --glob "*.ts" --glob "*.tsx" .

Length of output: 218


Add iOS‐only guard around Alert.prompt

Alert.prompt is only supported on iOS. Without a Platform check, calling this on Android will crash at runtime.

Please update components/prompt-async.ts to:

• Import Platform from react-native.
• Short‐circuit and resolve immediately on non‐iOS platforms.
• Document the iOS‐only limitation in the JSDoc.

For example:

+ import { Platform } from "react-native"
  /**
   * Creates an Alert with a text input and returns a Promise.
+  * Note: Alert.prompt is iOS-only; on Android this returns { value: undefined } immediately.
   */
  export function promptAsync(args: { /* … */ }) {
+   if (Platform.OS !== "ios") {
+     return Promise.resolve({ value: undefined })
+   }
    return new Promise<{ value: string | undefined }>((resolve) => {
      Alert.prompt(
        /* … */
      )
    })
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function promptAsync(args: {
title: string
message?: string
type?: "plain-text" | "secure-text"
defaultValue?: string
submitText?: string
cancelText?: string
keyboardType?: string
options?: AlertOptions
}) {
const {
title,
message,
type = "plain-text",
defaultValue = "",
submitText = "OK",
cancelText = "Cancel",
keyboardType,
options,
} = args
return new Promise<{ value: string | undefined }>((resolve) => {
Alert.prompt(
title,
message,
[
{
text: cancelText,
style: "cancel",
onPress: () => resolve({ value: undefined }),
},
{
text: submitText,
style: "default",
onPress: (value?: string) => resolve({ value }),
},
],
type,
defaultValue,
keyboardType,
options,
)
})
}
import { Alert, Platform } from "react-native"
/**
* Creates an Alert with a text input and returns a Promise which resolves with
* the input value when submitted, or undefined if canceled.
* Note: Alert.prompt is iOS-only; on Android this returns { value: undefined } immediately.
*/
export function promptAsync(args: {
title: string
message?: string
type?: "plain-text" | "secure-text"
defaultValue?: string
submitText?: string
cancelText?: string
keyboardType?: string
options?: AlertOptions
}) {
// Short-circuit on non-iOS platforms since Alert.prompt is unavailable there
if (Platform.OS !== "ios") {
return Promise.resolve({ value: undefined })
}
const {
title,
message,
type = "plain-text",
defaultValue = "",
submitText = "OK",
cancelText = "Cancel",
keyboardType,
options,
} = args
return new Promise<{ value: string | undefined }>((resolve) => {
Alert.prompt(
title,
message,
[
{
text: cancelText,
style: "cancel",
onPress: () => resolve({ value: undefined }),
},
{
text: submitText,
style: "default",
onPress: (value?: string) => resolve({ value }),
},
],
type,
defaultValue,
keyboardType,
options,
)
})
}
🤖 Prompt for AI Agents
In components/prompt-async.ts lines 8 to 51, Alert.prompt is used without
checking platform compatibility, which causes runtime crashes on Android since
Alert.prompt is iOS-only. Fix this by importing Platform from react-native,
adding a check to short-circuit and resolve immediately with undefined on
non-iOS platforms, and update the JSDoc to document that this function is
iOS-only.

Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { translate } from "@i18n/index"
import React from "react"
import { Screen } from "@/components/screen/screen"
import { EmptyState } from "@/design-system/empty-state"
import { useBlockedConversationsForCurrentAccount } from "@/features/blocked-conversations/use-blocked-conversations-for-current-account"
import { useBlockedConversationsForCurrentAccount } from "@/features/archived-conversations/use-archived-conversations-for-current-sender"
import { ConversationList } from "@/features/conversation/conversation-list/conversation-list.component"
import { useHeader } from "@/navigation/use-header"
import { useRouter } from "@/navigation/use-navigation"
import { $globalStyles } from "@/theme/styles"

export function BlockedConversationsScreen() {
export function ArchivedConversationsScreen() {
const { data: blockedConversationsIds = [] } = useBlockedConversationsForCurrentAccount()
Comment on lines +11 to 12

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Address naming inconsistency between component and internal variables.

The component is renamed to ArchivedConversationsScreen but the internal hook is still called useBlockedConversationsForCurrentAccount and the variable is blockedConversationsIds. This creates confusion about whether the component handles archived or blocked conversations.

Consider updating the variable names to match the component's new purpose:

-export function ArchivedConversationsScreen() {
-  const { data: blockedConversationsIds = [] } = useBlockedConversationsForCurrentAccount()
+export function ArchivedConversationsScreen() {
+  const { data: archivedConversationsIds = [] } = useBlockedConversationsForCurrentAccount()

   // Update usage below
-  {blockedConversationsIds.length > 0 ? (
-    <ConversationList conversationsIds={blockedConversationsIds} />
+  {archivedConversationsIds.length > 0 ? (
+    <ConversationList conversationsIds={archivedConversationsIds} />

Also consider renaming the hook itself to reflect its new purpose if it indeed handles archived conversations.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function ArchivedConversationsScreen() {
const { data: blockedConversationsIds = [] } = useBlockedConversationsForCurrentAccount()
export function ArchivedConversationsScreen() {
const { data: archivedConversationsIds = [] } = useBlockedConversationsForCurrentAccount()
// Update usage below
{archivedConversationsIds.length > 0 ? (
<ConversationList conversationsIds={archivedConversationsIds} />
) : (
<EmptyState message="No archived conversations" />
)}
}
🤖 Prompt for AI Agents
In features/archived-conversations/archived-conversations.screen.tsx around
lines 11 to 12, the variable and hook names refer to blocked conversations while
the component is named ArchivedConversationsScreen, causing confusion. Rename
the variable from blockedConversationsIds to archivedConversationsIds and update
the hook name from useBlockedConversationsForCurrentAccount to
useArchivedConversationsForCurrentAccount (or a similar name reflecting archived
conversations). Ensure all references to these names in the file are updated
accordingly to maintain consistency.


const router = useRouter()
Expand Down
146 changes: 146 additions & 0 deletions features/archived-conversations/denied-conversations.query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { IXmtpConversationId, IXmtpInboxId } from "@features/xmtp/xmtp.types"
import { queryOptions, skipToken } from "@tanstack/react-query"
import { setConversationQueryData } from "@/features/conversation/queries/conversation.query"
import { convertXmtpConversationToConvosConversation } from "@/features/conversation/utils/convert-xmtp-conversation-to-convos-conversation"
import { getXmtpConversations } from "@/features/xmtp/xmtp-conversations/xmtp-conversations-list"
import { syncAllXmtpConversations } from "@/features/xmtp/xmtp-conversations/xmtp-conversations-sync"
import { reactQueryClient } from "@/utils/react-query/react-query.client"

export type IDeniedConversationsQueryData = Awaited<
ReturnType<typeof getDeniedConversationsQueryFn>
>

async function getDeniedConversationsQueryFn(args: { inboxId: IXmtpInboxId }) {
const { inboxId } = args

if (!inboxId) {
throw new Error("InboxId is required")
}

await syncAllXmtpConversations({
clientInboxId: inboxId,
caller: "getDeniedConversationsQueryFn",
consentStates: ["denied"],
})

const deniedConsentXmtpConversations = await getXmtpConversations({
clientInboxId: inboxId,
consentStates: ["denied"],
caller: "getDeniedConversationsQueryFn",
})

const convosConversations = await Promise.all(
deniedConsentXmtpConversations.map(convertXmtpConversationToConvosConversation),
)

for (const conversation of convosConversations) {
setConversationQueryData({
clientInboxId: inboxId,
xmtpConversationId: conversation.xmtpId,
conversation,
})
}

return convosConversations.map((c) => c.xmtpId)
}
Comment on lines +13 to +45

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling and consider performance optimization.

The core query function has good structure but needs improvements:

  1. Missing error handling: The async operations could fail and should be wrapped in try-catch blocks
  2. Performance consideration: Converting all conversations in parallel is good, but consider pagination for large datasets
 async function getDeniedConversationsQueryFn(args: { inboxId: IXmtpInboxId }) {
   const { inboxId } = args

   if (!inboxId) {
     throw new Error("InboxId is required")
   }

+  try {
     await syncAllXmtpConversations({
       clientInboxId: inboxId,
       caller: "getDeniedConversationsQueryFn",
       consentStates: ["denied"],
     })

     const deniedConsentXmtpConversations = await getXmtpConversations({
       clientInboxId: inboxId,
       consentStates: ["denied"],
       caller: "getDeniedConversationsQueryFn",
     })

     const convosConversations = await Promise.all(
       deniedConsentXmtpConversations.map(convertXmtpConversationToConvosConversation),
     )

     for (const conversation of convosConversations) {
       setConversationQueryData({
         clientInboxId: inboxId,
         xmtpConversationId: conversation.xmtpId,
         conversation,
       })
     }

     return convosConversations.map((c) => c.xmtpId)
+  } catch (error) {
+    console.error('Failed to fetch denied conversations:', error)
+    throw new Error(`Failed to fetch denied conversations: ${error instanceof Error ? error.message : 'Unknown error'}`)
+  }
 }
🤖 Prompt for AI Agents
In features/archived-conversations/denied-conversations.query.ts around lines 13
to 45, add try-catch blocks around the async operations to handle potential
errors gracefully. Wrap the entire function body in a try block and catch any
errors to throw or handle them appropriately. Additionally, implement pagination
when fetching and converting conversations to avoid performance issues with
large datasets, such as limiting the number of conversations processed at once
and supporting fetching subsequent pages.


export const getDeniedConsentConversationsQueryData = (args: { inboxId: IXmtpInboxId }) => {
return reactQueryClient.getQueryData(getDeniedConsentConversationsQueryOptions(args).queryKey)
}

export function addConversationToDeniedConsentConversationsQuery(args: {
clientInboxId: IXmtpInboxId
conversationId: IXmtpConversationId
}) {
const { clientInboxId, conversationId } = args

return reactQueryClient.setQueryData(
getDeniedConsentConversationsQueryOptions({
inboxId: clientInboxId,
caller: "addConversationToDeniedConsentConversationsQuery",
}).queryKey,
(previousConversationIds) => {
if (!previousConversationIds) {
return [conversationId]
}

const conversationExists = previousConversationIds.includes(conversationId)

if (conversationExists) {
return previousConversationIds
}

return [conversationId, ...previousConversationIds]
},
)
}

export function removeConversationFromDeniedConsentConversationsQuery(args: {
clientInboxId: IXmtpInboxId
conversationId: IXmtpConversationId
}) {
const { clientInboxId, conversationId } = args

return reactQueryClient.setQueryData(
getDeniedConsentConversationsQueryOptions({
inboxId: clientInboxId,
caller: "removeConversationFromDeniedConsentConversationsQuery",
}).queryKey,
(previousConversationIds) => {
if (!previousConversationIds) {
return []
}

return previousConversationIds.filter((id) => id !== conversationId)
},
)
}
Comment on lines +47 to +97

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add input validation and fix naming inconsistency.

The cache utility functions need improvements:

  1. Missing validation: Functions don't validate if inboxId is provided
  2. Naming inconsistency: The caller parameter in line 60 is not used in the query key
 export function addConversationToDeniedConsentConversationsQuery(args: {
   clientInboxId: IXmtpInboxId
   conversationId: IXmtpConversationId
 }) {
   const { clientInboxId, conversationId } = args

+  if (!clientInboxId || !conversationId) {
+    throw new Error("Both clientInboxId and conversationId are required")
+  }

   return reactQueryClient.setQueryData(
     getDeniedConsentConversationsQueryOptions({
       inboxId: clientInboxId,
-      caller: "addConversationToDeniedConsentConversationsQuery",
     }).queryKey,
     (previousConversationIds) => {
       if (!previousConversationIds) {
         return [conversationId]
       }

       const conversationExists = previousConversationIds.includes(conversationId)

       if (conversationExists) {
         return previousConversationIds
       }

       return [conversationId, ...previousConversationIds]
     },
   )
 }

 export function removeConversationFromDeniedConsentConversationsQuery(args: {
   clientInboxId: IXmtpInboxId
   conversationId: IXmtpConversationId
 }) {
   const { clientInboxId, conversationId } = args

+  if (!clientInboxId || !conversationId) {
+    throw new Error("Both clientInboxId and conversationId are required")
+  }

   return reactQueryClient.setQueryData(
     getDeniedConsentConversationsQueryOptions({
       inboxId: clientInboxId,
-      caller: "removeConversationFromDeniedConsentConversationsQuery",
     }).queryKey,
     (previousConversationIds) => {
       if (!previousConversationIds) {
         return []
       }

       return previousConversationIds.filter((id) => id !== conversationId)
     },
   )
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const getDeniedConsentConversationsQueryData = (args: { inboxId: IXmtpInboxId }) => {
return reactQueryClient.getQueryData(getDeniedConsentConversationsQueryOptions(args).queryKey)
}
export function addConversationToDeniedConsentConversationsQuery(args: {
clientInboxId: IXmtpInboxId
conversationId: IXmtpConversationId
}) {
const { clientInboxId, conversationId } = args
return reactQueryClient.setQueryData(
getDeniedConsentConversationsQueryOptions({
inboxId: clientInboxId,
caller: "addConversationToDeniedConsentConversationsQuery",
}).queryKey,
(previousConversationIds) => {
if (!previousConversationIds) {
return [conversationId]
}
const conversationExists = previousConversationIds.includes(conversationId)
if (conversationExists) {
return previousConversationIds
}
return [conversationId, ...previousConversationIds]
},
)
}
export function removeConversationFromDeniedConsentConversationsQuery(args: {
clientInboxId: IXmtpInboxId
conversationId: IXmtpConversationId
}) {
const { clientInboxId, conversationId } = args
return reactQueryClient.setQueryData(
getDeniedConsentConversationsQueryOptions({
inboxId: clientInboxId,
caller: "removeConversationFromDeniedConsentConversationsQuery",
}).queryKey,
(previousConversationIds) => {
if (!previousConversationIds) {
return []
}
return previousConversationIds.filter((id) => id !== conversationId)
},
)
}
export const getDeniedConsentConversationsQueryData = (args: { inboxId: IXmtpInboxId }) => {
return reactQueryClient.getQueryData(
getDeniedConsentConversationsQueryOptions(args).queryKey,
)
}
export function addConversationToDeniedConsentConversationsQuery(args: {
clientInboxId: IXmtpInboxId
conversationId: IXmtpConversationId
}) {
const { clientInboxId, conversationId } = args
if (!clientInboxId || !conversationId) {
throw new Error("Both clientInboxId and conversationId are required")
}
return reactQueryClient.setQueryData(
getDeniedConsentConversationsQueryOptions({
inboxId: clientInboxId,
}).queryKey,
(previousConversationIds) => {
if (!previousConversationIds) {
return [conversationId]
}
const conversationExists = previousConversationIds.includes(conversationId)
if (conversationExists) {
return previousConversationIds
}
return [conversationId, ...previousConversationIds]
},
)
}
export function removeConversationFromDeniedConsentConversationsQuery(args: {
clientInboxId: IXmtpInboxId
conversationId: IXmtpConversationId
}) {
const { clientInboxId, conversationId } = args
if (!clientInboxId || !conversationId) {
throw new Error("Both clientInboxId and conversationId are required")
}
return reactQueryClient.setQueryData(
getDeniedConsentConversationsQueryOptions({
inboxId: clientInboxId,
}).queryKey,
(previousConversationIds) => {
if (!previousConversationIds) {
return []
}
return previousConversationIds.filter((id) => id !== conversationId)
},
)
}
🤖 Prompt for AI Agents
In features/archived-conversations/denied-conversations.query.ts between lines
47 and 97, add validation to ensure the inboxId argument is provided before
proceeding in the cache utility functions. Also, fix the naming inconsistency by
ensuring the caller parameter used in setQueryData calls matches the expected
query key structure, so it is properly included and consistent across the
functions.


export function getDeniedConsentConversationsQueryOptions(args: {
inboxId: IXmtpInboxId
caller?: string
}) {
const { inboxId, caller } = args

const enabled = !!inboxId

return queryOptions({
enabled,
meta: {
caller,
},
queryKey: ["denied-consent-conversations", inboxId],
queryFn: enabled
? async () =>
getDeniedConversationsQueryFn({
inboxId,
})
: skipToken,
})
}

export function invalidateDeniedConsentConversationsQuery(args: {
inboxId: IXmtpInboxId
caller: string
}) {
const { inboxId, caller } = args
return reactQueryClient.invalidateQueries({
queryKey: getDeniedConsentConversationsQueryOptions({
inboxId,
caller,
}).queryKey,
})
}

export function refetchDeniedConsentConversationsQuery(args: {
inboxId: IXmtpInboxId
caller: string
}) {
const { inboxId, caller } = args
return reactQueryClient.refetchQueries({
queryKey: getDeniedConsentConversationsQueryOptions({
inboxId,
caller,
}).queryKey,
})
}
Loading