From 8e4846741457c01161499509b55c5022c1425dfd Mon Sep 17 00:00:00 2001 From: jjbrunton Date: Sun, 6 Sep 2026 12:56:10 +0100 Subject: [PATCH] fix(mobile): preserve joined invites on cancellation --- apps/mobile/app/(app)/paired.tsx | 2 +- apps/mobile/app/(app)/pairing.tsx | 67 +++++++++++++++++++++---------- 2 files changed, 47 insertions(+), 22 deletions(-) diff --git a/apps/mobile/app/(app)/paired.tsx b/apps/mobile/app/(app)/paired.tsx index 09e8d4f..769bb13 100644 --- a/apps/mobile/app/(app)/paired.tsx +++ b/apps/mobile/app/(app)/paired.tsx @@ -31,7 +31,7 @@ export default function PairedScreen() { if (heading.current) AccessibilityInfo.setAccessibilityFocus(heading.current as never); }, 250); return () => clearTimeout(timer); - }, [ready, user, couple, newMatchesCount]); + }, [ready, error, user, couple, newMatchesCount]); const partnerName = partner?.name?.split(" ")[0] || "your partner"; const viewMatches = () => { Events.pairingUnlockCtaTapped(role, "view_matches"); router.replace("/(app)/matches"); }; diff --git a/apps/mobile/app/(app)/pairing.tsx b/apps/mobile/app/(app)/pairing.tsx index 3e8fb8e..8122d75 100755 --- a/apps/mobile/app/(app)/pairing.tsx +++ b/apps/mobile/app/(app)/pairing.tsx @@ -237,9 +237,41 @@ export default function PairingScreen() { }; const shareViaSMS = async () => { - const url = `sms:&body=${encodeURIComponent(shareMessage)}`; - await Linking.openURL(url); - Events.codeShared(); + const url = `sms:&body=${encodeURIComponent(shareMessage)}`; + await Linking.openURL(url); + Events.codeShared(); + }; + + const completeJoinedInvite = async () => { + await fetchUser(); + await fetchCouple(); + await useMatchStore.getState().fetchMatches(true); + router.replace("/(app)/paired" as any); + }; + + const cancelInvite = async (afterCancel?: () => Promise) => { + try { + await coupleApi.cancelInvite(); + await fetchUser(); + await fetchCouple(); + if (afterCancel) await afterCancel(); + Events.pairingCancelled(); + return true; + } catch (error: any) { + const apiCode = error instanceof ApiError + ? (error.details as { error?: { code?: string } } | undefined)?.error?.code + : undefined; + if (apiCode === "invite_already_joined") { + await completeJoinedInvite(); + return false; + } + Alert.alert( + "Couldn't cancel invite", + "Your invite may still be active. Check your connection and try again.", + [{ text: "Not now", style: "cancel" }, { text: "Try again", onPress: () => { void cancelInvite(afterCancel); } }], + ); + return false; + } }; const copyInviteLink = async () => { @@ -256,18 +288,12 @@ export default function PairingScreen() { { text: "Keep invite", style: "cancel" }, { text: "Cancel invite", - style: "destructive", - onPress: async () => { - setIsSubmitting(true); - try { - await coupleApi.cancel(); - - await fetchUser(); - await fetchCouple(); - Events.pairingCancelled(); - } catch (error: any) { - Alert.alert("Error", getPairingError(error)); - } finally { + style: "destructive", + onPress: async () => { + setIsSubmitting(true); + try { + await cancelInvite(); + } finally { setIsSubmitting(false); } }, @@ -291,12 +317,11 @@ export default function PairingScreen() { text: "Cancel mine and join", style: "destructive", onPress: async () => { setIsSubmitting(true); try { - await coupleApi.cancel(); - await fetchUser(); - await fetchCouple(); - setInviteCode(incomingCode); - setWasPrefilled(true); - await clearPendingInviteCode(); + await cancelInvite(async () => { + setInviteCode(incomingCode); + setWasPrefilled(true); + await clearPendingInviteCode(); + }); } finally { setIsSubmitting(false); }