From 73f7503c91ee3935b17bc1ac47ac9fdbb2300aa1 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 7 Apr 2026 14:58:46 +0000 Subject: [PATCH] fix: make @react-navigation/native an optional peer dependency Users who do not use the IterableInbox component (or who use a different navigation library) should not be forced to install @react-navigation/native. - Mark @react-navigation/native, react-native-safe-area-context, and react-native-webview as optional in peerDependenciesMeta - Create a safe useIsFocused hook wrapper that gracefully falls back to returning true when @react-navigation/native is not installed - Update IterableInbox to use the wrapper instead of importing directly Closes #770 https://claude.ai/code/session_01SsKoAbA48hJk6cDmbrRGqc --- package.json | 9 ++++++ src/inbox/components/IterableInbox.tsx | 2 +- src/inbox/hooks/index.ts | 1 + src/inbox/hooks/useIsFocused.ts | 41 ++++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/inbox/hooks/index.ts create mode 100644 src/inbox/hooks/useIsFocused.ts diff --git a/package.json b/package.json index 296749be5..a285bff7e 100644 --- a/package.json +++ b/package.json @@ -118,8 +118,17 @@ "react-native-webview": "*" }, "peerDependenciesMeta": { + "@react-navigation/native": { + "optional": true + }, "expo": { "optional": true + }, + "react-native-safe-area-context": { + "optional": true + }, + "react-native-webview": { + "optional": true } }, "sideEffects": false, diff --git a/src/inbox/components/IterableInbox.tsx b/src/inbox/components/IterableInbox.tsx index 3cf44d829..74acef045 100644 --- a/src/inbox/components/IterableInbox.tsx +++ b/src/inbox/components/IterableInbox.tsx @@ -1,4 +1,3 @@ -import { useIsFocused } from '@react-navigation/native'; import { useEffect, useState } from 'react'; import { Animated, @@ -19,6 +18,7 @@ import { Iterable } from '../../core/classes/Iterable'; import { IterableInAppDeleteSource, IterableInAppLocation } from '../../inApp'; import { IterableInboxDataModel } from '../classes'; +import { useIsFocused } from '../hooks'; import { ITERABLE_INBOX_COLORS } from '../constants'; import type { IterableInboxCustomizations, diff --git a/src/inbox/hooks/index.ts b/src/inbox/hooks/index.ts new file mode 100644 index 000000000..1b36c891a --- /dev/null +++ b/src/inbox/hooks/index.ts @@ -0,0 +1 @@ +export { useIsFocused } from './useIsFocused'; diff --git a/src/inbox/hooks/useIsFocused.ts b/src/inbox/hooks/useIsFocused.ts new file mode 100644 index 000000000..ca191ff54 --- /dev/null +++ b/src/inbox/hooks/useIsFocused.ts @@ -0,0 +1,41 @@ +/** + * Safe wrapper around `@react-navigation/native`'s `useIsFocused` hook. + * + * If `@react-navigation/native` is not installed (i.e., the consumer does not + * use React Navigation), the hook falls back to always returning `true` — + * meaning the component behaves as if it is always focused. + * + * This makes `@react-navigation/native` an optional peer dependency so that + * consumers who don't use the Inbox UI (or who use a different navigation + * library) are not forced to install it. + * + * @see https://github.com/Iterable/react-native-sdk/issues/770 + */ + +// Use globalThis.require to avoid needing @types/node in the tsconfig while +// still performing a synchronous optional require at module-load time. +declare const globalThis: { require?: (id: string) => unknown }; + +let useIsFocusedFromNav: (() => boolean) | undefined; + +try { + const reactNavigation = globalThis.require?.('@react-navigation/native') as + | { useIsFocused?: () => boolean } + | undefined; + useIsFocusedFromNav = reactNavigation?.useIsFocused; +} catch { + // @react-navigation/native is not installed — that's fine. +} + +/** + * Returns whether the screen is currently focused. + * + * Uses React Navigation's `useIsFocused` when available, otherwise defaults + * to `true`. + */ +export function useIsFocused(): boolean { + if (useIsFocusedFromNav) { + return useIsFocusedFromNav(); + } + return true; +}