diff --git a/.changeset/cool-ducks-march.md b/.changeset/cool-ducks-march.md new file mode 100644 index 00000000..d55fcef7 --- /dev/null +++ b/.changeset/cool-ducks-march.md @@ -0,0 +1,5 @@ +--- +"@xmtp/react-sdk": minor +--- + +Add `cancel` function to `useResendMessage` hook, rename `resendMessage` to `resend`. diff --git a/packages/react-sdk/src/hooks/useMessage.ts b/packages/react-sdk/src/hooks/useMessage.ts index 9d6c14ff..058d53fa 100644 --- a/packages/react-sdk/src/hooks/useMessage.ts +++ b/packages/react-sdk/src/hooks/useMessage.ts @@ -8,6 +8,7 @@ import { updateMessageAfterSending as _updateMessageAfterSending, prepareMessageForSending, getMessageByXmtpID as _getMessageByXmtpID, + deleteMessage as _deleteMessage, } from "@/helpers/caching/messages"; import type { CachedMessage, @@ -74,6 +75,11 @@ export const useMessage = () => { RemoveLastParameter >(async (xmtpID) => _getMessageByXmtpID(xmtpID, db), [db]); + const deleteMessage = useCallback>( + async (message) => _deleteMessage(message, db), + [db], + ); + /** * Send a message to a conversation on the XMTP network * @@ -210,6 +216,7 @@ export const useMessage = () => { ); return { + deleteMessage, getMessageByXmtpID, processMessage, resendMessage, diff --git a/packages/react-sdk/src/hooks/useResendMessage.test.ts b/packages/react-sdk/src/hooks/useResendMessage.test.ts index 0f9a61b9..826f5a0b 100644 --- a/packages/react-sdk/src/hooks/useResendMessage.test.ts +++ b/packages/react-sdk/src/hooks/useResendMessage.test.ts @@ -4,25 +4,25 @@ import { ContentTypeText } from "@xmtp/xmtp-js"; import { useResendMessage } from "@/hooks/useResendMessage"; import type { CachedMessageWithId } from "@/helpers/caching/messages"; -const resendMessageMock = vi.hoisted(() => vi.fn()); +const resendMock = vi.hoisted(() => vi.fn()); vi.mock("@/hooks/useMessage", async () => { const actual = await import("@/hooks/useMessage"); return { useMessage: () => ({ ...actual.useMessage, - resendMessage: resendMessageMock, + resendMessage: resendMock, }), }; }); describe("useResendMessage", () => { beforeEach(() => { - resendMessageMock.mockReset(); + resendMock.mockReset(); }); it("should resend a previously failed message", async () => { - resendMessageMock.mockResolvedValueOnce({ id: 1 }); + resendMock.mockResolvedValueOnce({ id: 1 }); const onSuccessMock = vi.fn(); const testMessage = { @@ -47,19 +47,19 @@ describe("useResendMessage", () => { ); await act(async () => { - const sentMessage = await result.current.resendMessage(testMessage); + const sentMessage = await result.current.resend(testMessage); expect(sentMessage).toEqual({ id: 1 }); expect(onSuccessMock).toHaveBeenCalledTimes(1); expect(onSuccessMock).toHaveBeenCalledWith(sentMessage); }); - expect(resendMessageMock).toHaveBeenCalledTimes(1); - expect(resendMessageMock).toHaveBeenCalledWith(testMessage); + expect(resendMock).toHaveBeenCalledTimes(1); + expect(resendMock).toHaveBeenCalledWith(testMessage); }); it("should have an error when resending fails", async () => { const testError = new Error("testError"); - resendMessageMock.mockRejectedValueOnce(testError); + resendMock.mockRejectedValueOnce(testError); const onErrorMock = vi.fn(); const testMessage = { @@ -85,11 +85,11 @@ describe("useResendMessage", () => { await act(async () => { try { - await result.current.resendMessage(testMessage); + await result.current.resend(testMessage); } catch (e) { expect(e).toEqual(testError); } finally { - expect(resendMessageMock).toHaveBeenCalledTimes(1); + expect(resendMock).toHaveBeenCalledTimes(1); expect(onErrorMock).toHaveBeenCalledTimes(1); expect(onErrorMock).toHaveBeenCalledWith(testError); } diff --git a/packages/react-sdk/src/hooks/useResendMessage.ts b/packages/react-sdk/src/hooks/useResendMessage.ts index a31ba072..65189dd9 100644 --- a/packages/react-sdk/src/hooks/useResendMessage.ts +++ b/packages/react-sdk/src/hooks/useResendMessage.ts @@ -4,23 +4,23 @@ import type { UseSendMessageOptions } from "@/hooks/useSendMessage"; import type { CachedMessageWithId } from "@/helpers/caching/messages"; /** - * This hook resends a cached message that previously failed to send. + * This hook can be used to resend a previously failed message, or cancel it. */ export const useResendMessage = (options?: UseSendMessageOptions) => { const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); - const { resendMessage: _resendMessage } = useMessage(); + const { resendMessage, deleteMessage } = useMessage(); // destructure options for more granular dependency array const { onError, onSuccess } = options ?? {}; - const resendMessage = useCallback( + const resend = useCallback( async (message: CachedMessageWithId) => { setIsLoading(true); setError(null); try { - const sentMessage = await _resendMessage(message); + const sentMessage = await resendMessage(message); onSuccess?.(sentMessage); return sentMessage; } catch (e) { @@ -32,12 +32,27 @@ export const useResendMessage = (options?: UseSendMessageOptions) => { setIsLoading(false); } }, - [_resendMessage, onError, onSuccess], + [resendMessage, onError, onSuccess], + ); + + const cancel = useCallback( + async (message: CachedMessageWithId) => { + try { + await deleteMessage(message); + } catch (e) { + setError(e as Error); + onError?.(e as Error); + // re-throw error for upstream consumption + throw e; + } + }, + [deleteMessage, onError], ); return { + cancel, error, isLoading, - resendMessage, + resend, }; };