diff --git a/packages/expo-router/CHANGELOG.md b/packages/expo-router/CHANGELOG.md index 88a8108524df9e..772bc0c7773e86 100644 --- a/packages/expo-router/CHANGELOG.md +++ b/packages/expo-router/CHANGELOG.md @@ -15,6 +15,7 @@ - [web] Fix route URL detection in `useLoaderData` ([#42912](https://github.com/expo/expo/pull/42912) by [@hassankhan](https://github.com/hassankhan)) - [web] Key loader data by `contextKey` instead of URL pathname ([#43017](https://github.com/expo/expo/pull/43017) by [@hassankhan] +- [ios] fix immediate navigation when opening Link.Preview ([#43071](https://github.com/expo/expo/pull/43071) by [@Ubax](https://github.com/Ubax)) ### 💡 Others diff --git a/packages/expo-router/build/link/BaseExpoRouterLink.d.ts.map b/packages/expo-router/build/link/BaseExpoRouterLink.d.ts.map index e806f8693191c0..63076a149512c4 100644 --- a/packages/expo-router/build/link/BaseExpoRouterLink.d.ts.map +++ b/packages/expo-router/build/link/BaseExpoRouterLink.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"BaseExpoRouterLink.d.ts","sourceRoot":"","sources":["../../src/link/BaseExpoRouterLink.tsx"],"names":[],"mappings":"AAGA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAInD,OAAO,EAAqC,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAK9E,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,OAAO,EACP,IAAI,EACJ,SAAS,EAET,mBAAmB,EACnB,OAAO,EACP,GAAG,EACH,MAAM,EACN,QAAQ,EACR,UAAU,EACV,mBAAmB,EAAE,QAAQ,EAC7B,QAAQ,EACR,GAAG,IAAI,EACR,EAAE,SAAS,qBAkEX"} \ No newline at end of file +{"version":3,"file":"BaseExpoRouterLink.d.ts","sourceRoot":"","sources":["../../src/link/BaseExpoRouterLink.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAmC,MAAM,OAAO,CAAC;AAKxD,OAAO,EAAqC,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAK9E,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,OAAO,EACP,IAAI,EACJ,SAAS,EAET,mBAAmB,EACnB,OAAO,EACP,GAAG,EACH,MAAM,EACN,QAAQ,EACR,UAAU,EACV,mBAAmB,EAAE,QAAQ,EAC7B,QAAQ,EACR,GAAG,IAAI,EACR,EAAE,SAAS,qBAuEX"} \ No newline at end of file diff --git a/packages/expo-router/build/link/BaseExpoRouterLink.js b/packages/expo-router/build/link/BaseExpoRouterLink.js index ce95c8906481b6..d193a9395db1c6 100644 --- a/packages/expo-router/build/link/BaseExpoRouterLink.js +++ b/packages/expo-router/build/link/BaseExpoRouterLink.js @@ -42,6 +42,7 @@ exports.BaseExpoRouterLink = BaseExpoRouterLink; // `to` / `action` support removed. const react_1 = __importStar(require("react")); const react_native_1 = require("react-native"); +const InternalLinkPreviewContext_1 = require("./InternalLinkPreviewContext"); const href_1 = require("./href"); const useLinkHooks_1 = require("./useLinkHooks"); const useLinkToPathProps_1 = __importDefault(require("./useLinkToPathProps")); @@ -67,6 +68,7 @@ relativeToDirectory, asChild, rel, target, download, withAnchor, dangerouslySing event = 'REPLACE'; if (dismissTo) event = 'POP_TO'; + const previewContext = (0, react_1.use)(InternalLinkPreviewContext_1.InternalLinkPreviewContext); const props = (0, useLinkToPathProps_1.default)({ href: resolvedHref, event, @@ -75,6 +77,9 @@ relativeToDirectory, asChild, rel, target, download, withAnchor, dangerouslySing dangerouslySingular: singular, }); const onPress = (e) => { + if (previewContext?.blockPressRef.current) { + return; + } if ('onPress' in rest) { rest.onPress?.(e); } diff --git a/packages/expo-router/build/link/BaseExpoRouterLink.js.map b/packages/expo-router/build/link/BaseExpoRouterLink.js.map index eb7adf459788ed..1ce7d4905de907 100644 --- a/packages/expo-router/build/link/BaseExpoRouterLink.js.map +++ b/packages/expo-router/build/link/BaseExpoRouterLink.js.map @@ -1 +1 @@ -{"version":3,"file":"BaseExpoRouterLink.js","sourceRoot":"","sources":["../../src/link/BaseExpoRouterLink.tsx"],"names":[],"mappings":";AAAA,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAYb,gDAiFC;AA5FD,wFAAwF;AACxF,mCAAmC;AACnC,+CAAmD;AACnD,+CAAqE;AAErE,iCAAqC;AACrC,iDAA8E;AAC9E,8EAAsD;AACtD,0CAAuC;AACvC,qCAAkC;AAElC,SAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,OAAO,EACP,IAAI,EACJ,SAAS;AACT,yDAAyD;AACzD,mBAAmB,EACnB,OAAO,EACP,GAAG,EACH,MAAM,EACN,QAAQ,EACR,UAAU,EACV,mBAAmB,EAAE,QAAQ,EAC7B,QAAQ,EACR,GAAG,IAAI,EACG;IACV,qDAAqD;IACrD,MAAM,KAAK,GAAG,IAAA,kCAAmB,EAAC,IAAI,CAAC,CAAC;IAExC,+GAA+G;IAC/G,MAAM,SAAS,GAAG,IAAA,2BAAY,EAAC,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IAEnE,MAAM,YAAY,GAAG,IAAA,eAAO,EAAC,GAAG,EAAE;QAChC,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,IAAA,kBAAW,EAAC,IAAI,CAAC,CAAC;IAC3B,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,IAAI,KAAK,CAAC;IACV,IAAI,IAAI;QAAE,KAAK,GAAG,MAAM,CAAC;IACzB,IAAI,OAAO;QAAE,KAAK,GAAG,SAAS,CAAC;IAC/B,IAAI,SAAS;QAAE,KAAK,GAAG,QAAQ,CAAC;IAEhC,MAAM,KAAK,GAAG,IAAA,4BAAkB,EAAC;QAC/B,IAAI,EAAE,YAAY;QAClB,KAAK;QACL,mBAAmB;QACnB,UAAU;QACV,mBAAmB,EAAE,QAAQ;KAC9B,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,CAAC,CAAwD,EAAE,EAAE;QAC3E,IAAI,SAAS,IAAI,IAAI,EAAE,CAAC;YACtB,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;QACD,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC,CAAC;IAEF,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,WAAI,CAAC,CAAC,CAAC,mBAAI,CAAC;IAExC,IAAI,OAAO,IAAI,eAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACvD,MAAM,IAAI,KAAK,CACb,sGAAsG,CACvG,CAAC;IACJ,CAAC;IAED,6HAA6H;IAC7H,MAAM,OAAO,GAAG,CACd,CAAC,SAAS,CACR,IAAI,KAAK,CAAC,CACV,IAAI,SAAS,CAAC,CACd,IAAI,IAAI,CAAC,CACT,KAAK,CAAC,CAAC,KAAK,CAAC,CACb,IAAI,uBAAQ,CAAC,MAAM,CAAC;QAClB,GAAG,EAAE;YACH,OAAO,EAAE,OAAO;SACV;QACR,OAAO,EAAE,EAAE,OAAO,EAAE;KACrB,CAAC,CAAC,EACH,CACH,CAAC;IAEF,OAAO,QAAQ,CAAC,CAAC,CAAC,CAChB,EACE;MAAA,CAAC,mBAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EACrB;MAAA,CAAC,OAAO,CACV;IAAA,GAAG,CACJ,CAAC,CAAC,CAAC,CACF,OAAO,CACR,CAAC;AACJ,CAAC","sourcesContent":["'use client';\n// Fork of @react-navigation/native Link.tsx with `href` and `replace` support added and\n// `to` / `action` support removed.\nimport React, { useMemo, MouseEvent } from 'react';\nimport { Text, GestureResponderEvent, Platform } from 'react-native';\n\nimport { resolveHref } from './href';\nimport { useInteropClassName, useHrefAttrs, LinkProps } from './useLinkHooks';\nimport useLinkToPathProps from './useLinkToPathProps';\nimport { Prefetch } from '../Prefetch';\nimport { Slot } from '../ui/Slot';\n\nexport function BaseExpoRouterLink({\n href,\n replace,\n push,\n dismissTo,\n // TODO: This does not prevent default on the anchor tag.\n relativeToDirectory,\n asChild,\n rel,\n target,\n download,\n withAnchor,\n dangerouslySingular: singular,\n prefetch,\n ...rest\n}: LinkProps) {\n // Mutate the style prop to add the className on web.\n const style = useInteropClassName(rest);\n\n // If not passing asChild, we need to forward the props to the anchor tag using React Native Web's `hrefAttrs`.\n const hrefAttrs = useHrefAttrs({ asChild, rel, target, download });\n\n const resolvedHref = useMemo(() => {\n if (href == null) {\n throw new Error('Link: href is required');\n }\n return resolveHref(href);\n }, [href]);\n\n let event;\n if (push) event = 'PUSH';\n if (replace) event = 'REPLACE';\n if (dismissTo) event = 'POP_TO';\n\n const props = useLinkToPathProps({\n href: resolvedHref,\n event,\n relativeToDirectory,\n withAnchor,\n dangerouslySingular: singular,\n });\n\n const onPress = (e: MouseEvent | GestureResponderEvent) => {\n if ('onPress' in rest) {\n rest.onPress?.(e);\n }\n props.onPress(e);\n };\n\n const Component = asChild ? Slot : Text;\n\n if (asChild && React.Children.count(rest.children) > 1) {\n throw new Error(\n 'Link: When using `asChild`, you must pass a single child element that will emit the `onPress` event.'\n );\n }\n\n // Avoid using createElement directly, favoring JSX, to allow tools like NativeWind to perform custom JSX handling on native.\n const element = (\n \n );\n\n return prefetch ? (\n <>\n \n {element}\n \n ) : (\n element\n );\n}\n"]} \ No newline at end of file +{"version":3,"file":"BaseExpoRouterLink.js","sourceRoot":"","sources":["../../src/link/BaseExpoRouterLink.tsx"],"names":[],"mappings":";AAAA,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAab,gDAsFC;AAlGD,wFAAwF;AACxF,mCAAmC;AACnC,+CAAwD;AACxD,+CAAqE;AAErE,6EAA0E;AAC1E,iCAAqC;AACrC,iDAA8E;AAC9E,8EAAsD;AACtD,0CAAuC;AACvC,qCAAkC;AAElC,SAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,OAAO,EACP,IAAI,EACJ,SAAS;AACT,yDAAyD;AACzD,mBAAmB,EACnB,OAAO,EACP,GAAG,EACH,MAAM,EACN,QAAQ,EACR,UAAU,EACV,mBAAmB,EAAE,QAAQ,EAC7B,QAAQ,EACR,GAAG,IAAI,EACG;IACV,qDAAqD;IACrD,MAAM,KAAK,GAAG,IAAA,kCAAmB,EAAC,IAAI,CAAC,CAAC;IAExC,+GAA+G;IAC/G,MAAM,SAAS,GAAG,IAAA,2BAAY,EAAC,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IAEnE,MAAM,YAAY,GAAG,IAAA,eAAO,EAAC,GAAG,EAAE;QAChC,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,IAAA,kBAAW,EAAC,IAAI,CAAC,CAAC;IAC3B,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,IAAI,KAAK,CAAC;IACV,IAAI,IAAI;QAAE,KAAK,GAAG,MAAM,CAAC;IACzB,IAAI,OAAO;QAAE,KAAK,GAAG,SAAS,CAAC;IAC/B,IAAI,SAAS;QAAE,KAAK,GAAG,QAAQ,CAAC;IAEhC,MAAM,cAAc,GAAG,IAAA,WAAG,EAAC,uDAA0B,CAAC,CAAC;IAEvD,MAAM,KAAK,GAAG,IAAA,4BAAkB,EAAC;QAC/B,IAAI,EAAE,YAAY;QAClB,KAAK;QACL,mBAAmB;QACnB,UAAU;QACV,mBAAmB,EAAE,QAAQ;KAC9B,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,CAAC,CAAwD,EAAE,EAAE;QAC3E,IAAI,cAAc,EAAE,aAAa,CAAC,OAAO,EAAE,CAAC;YAC1C,OAAO;QACT,CAAC;QACD,IAAI,SAAS,IAAI,IAAI,EAAE,CAAC;YACtB,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;QACD,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC,CAAC;IAEF,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,WAAI,CAAC,CAAC,CAAC,mBAAI,CAAC;IAExC,IAAI,OAAO,IAAI,eAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACvD,MAAM,IAAI,KAAK,CACb,sGAAsG,CACvG,CAAC;IACJ,CAAC;IAED,6HAA6H;IAC7H,MAAM,OAAO,GAAG,CACd,CAAC,SAAS,CACR,IAAI,KAAK,CAAC,CACV,IAAI,SAAS,CAAC,CACd,IAAI,IAAI,CAAC,CACT,KAAK,CAAC,CAAC,KAAK,CAAC,CACb,IAAI,uBAAQ,CAAC,MAAM,CAAC;QAClB,GAAG,EAAE;YACH,OAAO,EAAE,OAAO;SACV;QACR,OAAO,EAAE,EAAE,OAAO,EAAE;KACrB,CAAC,CAAC,EACH,CACH,CAAC;IAEF,OAAO,QAAQ,CAAC,CAAC,CAAC,CAChB,EACE;MAAA,CAAC,mBAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EACrB;MAAA,CAAC,OAAO,CACV;IAAA,GAAG,CACJ,CAAC,CAAC,CAAC,CACF,OAAO,CACR,CAAC;AACJ,CAAC","sourcesContent":["'use client';\n// Fork of @react-navigation/native Link.tsx with `href` and `replace` support added and\n// `to` / `action` support removed.\nimport React, { useMemo, MouseEvent, use } from 'react';\nimport { Text, GestureResponderEvent, Platform } from 'react-native';\n\nimport { InternalLinkPreviewContext } from './InternalLinkPreviewContext';\nimport { resolveHref } from './href';\nimport { useInteropClassName, useHrefAttrs, LinkProps } from './useLinkHooks';\nimport useLinkToPathProps from './useLinkToPathProps';\nimport { Prefetch } from '../Prefetch';\nimport { Slot } from '../ui/Slot';\n\nexport function BaseExpoRouterLink({\n href,\n replace,\n push,\n dismissTo,\n // TODO: This does not prevent default on the anchor tag.\n relativeToDirectory,\n asChild,\n rel,\n target,\n download,\n withAnchor,\n dangerouslySingular: singular,\n prefetch,\n ...rest\n}: LinkProps) {\n // Mutate the style prop to add the className on web.\n const style = useInteropClassName(rest);\n\n // If not passing asChild, we need to forward the props to the anchor tag using React Native Web's `hrefAttrs`.\n const hrefAttrs = useHrefAttrs({ asChild, rel, target, download });\n\n const resolvedHref = useMemo(() => {\n if (href == null) {\n throw new Error('Link: href is required');\n }\n return resolveHref(href);\n }, [href]);\n\n let event;\n if (push) event = 'PUSH';\n if (replace) event = 'REPLACE';\n if (dismissTo) event = 'POP_TO';\n\n const previewContext = use(InternalLinkPreviewContext);\n\n const props = useLinkToPathProps({\n href: resolvedHref,\n event,\n relativeToDirectory,\n withAnchor,\n dangerouslySingular: singular,\n });\n\n const onPress = (e: MouseEvent | GestureResponderEvent) => {\n if (previewContext?.blockPressRef.current) {\n return;\n }\n if ('onPress' in rest) {\n rest.onPress?.(e);\n }\n props.onPress(e);\n };\n\n const Component = asChild ? Slot : Text;\n\n if (asChild && React.Children.count(rest.children) > 1) {\n throw new Error(\n 'Link: When using `asChild`, you must pass a single child element that will emit the `onPress` event.'\n );\n }\n\n // Avoid using createElement directly, favoring JSX, to allow tools like NativeWind to perform custom JSX handling on native.\n const element = (\n \n );\n\n return prefetch ? (\n <>\n \n {element}\n \n ) : (\n element\n );\n}\n"]} \ No newline at end of file diff --git a/packages/expo-router/build/link/InternalLinkPreviewContext.d.ts b/packages/expo-router/build/link/InternalLinkPreviewContext.d.ts index 31afb39d9aafb6..d947c453429e13 100644 --- a/packages/expo-router/build/link/InternalLinkPreviewContext.d.ts +++ b/packages/expo-router/build/link/InternalLinkPreviewContext.d.ts @@ -1,6 +1,9 @@ +import { type RefObject } from 'react'; import type { LinkProps } from './useLinkHooks'; -export declare const InternalLinkPreviewContext: import("react").Context<{ +export type InternalLinkPreviewContextValue = { isVisible: boolean; - href: LinkProps["href"]; -} | undefined>; + href: LinkProps['href']; + blockPressRef: RefObject; +}; +export declare const InternalLinkPreviewContext: import("react").Context; //# sourceMappingURL=InternalLinkPreviewContext.d.ts.map \ No newline at end of file diff --git a/packages/expo-router/build/link/InternalLinkPreviewContext.d.ts.map b/packages/expo-router/build/link/InternalLinkPreviewContext.d.ts.map index c53f445ec09987..fe8db1d56d7911 100644 --- a/packages/expo-router/build/link/InternalLinkPreviewContext.d.ts.map +++ b/packages/expo-router/build/link/InternalLinkPreviewContext.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"InternalLinkPreviewContext.d.ts","sourceRoot":"","sources":["../../src/link/InternalLinkPreviewContext.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,eAAO,MAAM,0BAA0B;eACxB,OAAO;UAAQ,SAAS,CAAC,MAAM,CAAC;cACnC,CAAC"} \ No newline at end of file +{"version":3,"file":"InternalLinkPreviewContext.d.ts","sourceRoot":"","sources":["../../src/link/InternalLinkPreviewContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,MAAM,+BAA+B,GAAG;IAC5C,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IACxB,aAAa,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;CACnC,CAAC;AAEF,eAAO,MAAM,0BAA0B,sEAE3B,CAAC"} \ No newline at end of file diff --git a/packages/expo-router/build/link/InternalLinkPreviewContext.js.map b/packages/expo-router/build/link/InternalLinkPreviewContext.js.map index 519b7621402b8a..e7240de30d5384 100644 --- a/packages/expo-router/build/link/InternalLinkPreviewContext.js.map +++ b/packages/expo-router/build/link/InternalLinkPreviewContext.js.map @@ -1 +1 @@ -{"version":3,"file":"InternalLinkPreviewContext.js","sourceRoot":"","sources":["../../src/link/InternalLinkPreviewContext.ts"],"names":[],"mappings":";;;AAAA,iCAAsC;AAIzB,QAAA,0BAA0B,GAAG,IAAA,qBAAa,EAErD,SAAS,CAAC,CAAC","sourcesContent":["import { createContext } from 'react';\n\nimport type { LinkProps } from './useLinkHooks';\n\nexport const InternalLinkPreviewContext = createContext<\n { isVisible: boolean; href: LinkProps['href'] } | undefined\n>(undefined);\n"]} \ No newline at end of file +{"version":3,"file":"InternalLinkPreviewContext.js","sourceRoot":"","sources":["../../src/link/InternalLinkPreviewContext.ts"],"names":[],"mappings":";;;AAAA,iCAAsD;AAUzC,QAAA,0BAA0B,GAAG,IAAA,qBAAa,EAErD,SAAS,CAAC,CAAC","sourcesContent":["import { createContext, type RefObject } from 'react';\n\nimport type { LinkProps } from './useLinkHooks';\n\nexport type InternalLinkPreviewContextValue = {\n isVisible: boolean;\n href: LinkProps['href'];\n blockPressRef: RefObject;\n};\n\nexport const InternalLinkPreviewContext = createContext<\n InternalLinkPreviewContextValue | undefined\n>(undefined);\n"]} \ No newline at end of file diff --git a/packages/expo-router/build/link/LinkWithPreview.d.ts.map b/packages/expo-router/build/link/LinkWithPreview.d.ts.map index 59d11d2a76b1f8..2e0b79dc29db2c 100644 --- a/packages/expo-router/build/link/LinkWithPreview.d.ts.map +++ b/packages/expo-router/build/link/LinkWithPreview.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"LinkWithPreview.d.ts","sourceRoot":"","sources":["../../src/link/LinkWithPreview.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA+C,MAAM,OAAO,CAAC;AASpE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAIrC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAM3C,UAAU,oBAAqB,SAAQ,SAAS;IAC9C,wBAAwB,EAAE,IAAI,CAAC;CAChC;AAED,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,oBAAoB,qBA2H1E"} \ No newline at end of file +{"version":3,"file":"LinkWithPreview.d.ts","sourceRoot":"","sources":["../../src/link/LinkWithPreview.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA+C,MAAM,OAAO,CAAC;AASpE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAIrC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAM3C,UAAU,oBAAqB,SAAQ,SAAS;IAC9C,wBAAwB,EAAE,IAAI,CAAC;CAChC;AAED,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,oBAAoB,qBAkI1E"} \ No newline at end of file diff --git a/packages/expo-router/build/link/LinkWithPreview.js b/packages/expo-router/build/link/LinkWithPreview.js index 0f483f85d0fe6b..e38801d4e620a7 100644 --- a/packages/expo-router/build/link/LinkWithPreview.js +++ b/packages/expo-router/build/link/LinkWithPreview.js @@ -90,6 +90,7 @@ function LinkWithPreview({ children, ...rest }) { const trigger = react_1.default.useMemo(() => triggerElement ?? {children}, [triggerElement, children]); const preview = react_1.default.useMemo(() => ((0, url_1.shouldLinkExternally)(String(rest.href)) || !previewElement ? null : previewElement), [previewElement, rest.href]); const isPreviewTapped = (0, react_1.useRef)(false); + const blockPressRef = (0, react_1.useRef)(false); const tabPathValue = (0, react_1.useMemo)(() => ({ path: tabPath, }), [tabPath]); @@ -99,6 +100,7 @@ function LinkWithPreview({ children, ...rest }) { } return ( { if (hasPreview) { + blockPressRef.current = true; isPreviewTapped.current = false; prefetch(rest.hrefForPreviewNavigation); setIsCurrenPreviewOpen(true); @@ -113,6 +115,7 @@ function LinkWithPreview({ children, ...rest }) { } } }} onPreviewDidClose={() => { + blockPressRef.current = false; if (hasPreview && isPreviewTapped.current && isPad) { router.navigate(rest.hrefForPreviewNavigation, { __internal__PreviewKey: nextScreenId }); } @@ -123,7 +126,11 @@ function LinkWithPreview({ children, ...rest }) { } }} style={{ display: 'contents' }} disableForceFlatten> - + {preview} {menuElement} diff --git a/packages/expo-router/build/link/LinkWithPreview.js.map b/packages/expo-router/build/link/LinkWithPreview.js.map index b3a7c60a3e54d6..86812663ba421b 100644 --- a/packages/expo-router/build/link/LinkWithPreview.js.map +++ b/packages/expo-router/build/link/LinkWithPreview.js.map @@ -1 +1 @@ -{"version":3,"file":"LinkWithPreview.js","sourceRoot":"","sources":["../../src/link/LinkWithPreview.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport React, { useEffect, useMemo, useRef, useState } from 'react';\nimport { Platform } from 'react-native';\n\nimport { useRouter } from '../hooks';\nimport { BaseExpoRouterLink } from './BaseExpoRouterLink';\nimport { InternalLinkPreviewContext } from './InternalLinkPreviewContext';\nimport { NativeMenuContext } from './NativeMenuContext';\nimport { LinkMenu, LinkPreview, LinkTrigger } from './elements';\nimport { resolveHref } from './href';\nimport type { Href } from '../types';\nimport { useLinkPreviewContext } from './preview/LinkPreviewContext';\nimport { NativeLinkPreview } from './preview/native';\nimport { useNextScreenId } from './preview/useNextScreenId';\nimport { LinkProps } from './useLinkHooks';\nimport { getFirstChildOfType } from '../utils/children';\nimport { shouldLinkExternally } from '../utils/url';\n\nconst isPad = Platform.OS === 'ios' && Platform.isPad;\n\ninterface LinkWithPreviewProps extends LinkProps {\n hrefForPreviewNavigation: Href;\n}\n\nexport function LinkWithPreview({ children, ...rest }: LinkWithPreviewProps) {\n const router = useRouter();\n const { setOpenPreviewKey } = useLinkPreviewContext();\n const [isCurrentPreviewOpen, setIsCurrenPreviewOpen] = useState(false);\n\n const hrefWithoutQuery = resolveHref(rest.hrefForPreviewNavigation).split('?')[0];\n const prevHrefWithoutQuery = useRef(hrefWithoutQuery);\n\n useEffect(() => {\n if (isCurrentPreviewOpen) {\n if (prevHrefWithoutQuery.current !== hrefWithoutQuery) {\n throw new Error(\n 'Link does not support changing the href prop after the preview has been opened. Please ensure that the href prop is stable and does not change between renders.'\n );\n }\n } else {\n prevHrefWithoutQuery.current = hrefWithoutQuery;\n }\n }, [hrefWithoutQuery]);\n\n const [{ nextScreenId, tabPath }, prefetch] = useNextScreenId();\n\n useEffect(() => {\n if (rest.replace) {\n if (process.env.NODE_ENV !== 'production') {\n throw new Error('Using replace links with preview is not supported');\n } else {\n console.warn('Using replace links with preview is not supported');\n }\n }\n }, [rest.href, rest.replace]);\n\n const triggerElement = React.useMemo(\n () => getFirstChildOfType(children, LinkTrigger),\n [children]\n );\n const menuElement = React.useMemo(() => getFirstChildOfType(children, LinkMenu), [children]);\n const previewElement = React.useMemo(\n () => getFirstChildOfType(children, LinkPreview),\n [children]\n );\n\n if ((previewElement || menuElement) && !triggerElement) {\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n 'When you use Link.Preview, you must use Link.Trigger to specify the trigger element.'\n );\n } else {\n console.warn(\n 'When you use Link.Preview, you must use Link.Trigger to specify the trigger element.'\n );\n }\n }\n\n const trigger = React.useMemo(\n () => triggerElement ?? {children},\n [triggerElement, children]\n );\n\n const preview = React.useMemo(\n () => (shouldLinkExternally(String(rest.href)) || !previewElement ? null : previewElement),\n [previewElement, rest.href]\n );\n\n const isPreviewTapped = useRef(false);\n\n const tabPathValue = useMemo(\n () => ({\n path: tabPath,\n }),\n [tabPath]\n );\n\n const hasPreview = !!previewElement;\n\n if (rest.replace) {\n return ;\n }\n\n return (\n {\n if (hasPreview) {\n isPreviewTapped.current = false;\n prefetch(rest.hrefForPreviewNavigation);\n setIsCurrenPreviewOpen(true);\n }\n }}\n onPreviewWillClose={() => {\n if (hasPreview) {\n setIsCurrenPreviewOpen(false);\n // When preview was not tapped, then we need to enable the screen stack animation\n // Otherwise this will happen in StackNavigator, when new screen is opened\n if (!isPreviewTapped.current || isPad) {\n setOpenPreviewKey(undefined);\n }\n }\n }}\n onPreviewDidClose={() => {\n if (hasPreview && isPreviewTapped.current && isPad) {\n router.navigate(rest.hrefForPreviewNavigation, { __internal__PreviewKey: nextScreenId });\n }\n }}\n onPreviewTapped={() => {\n isPreviewTapped.current = true;\n if (!isPad) {\n router.navigate(rest.hrefForPreviewNavigation, { __internal__PreviewKey: nextScreenId });\n }\n }}\n style={{ display: 'contents' }}\n disableForceFlatten>\n \n \n \n {preview}\n {menuElement}\n \n \n \n );\n}\n"]} \ No newline at end of file +{"version":3,"file":"LinkWithPreview.js","sourceRoot":"","sources":["../../src/link/LinkWithPreview.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport React, { useEffect, useMemo, useRef, useState } from 'react';\nimport { Platform } from 'react-native';\n\nimport { useRouter } from '../hooks';\nimport { BaseExpoRouterLink } from './BaseExpoRouterLink';\nimport { InternalLinkPreviewContext } from './InternalLinkPreviewContext';\nimport { NativeMenuContext } from './NativeMenuContext';\nimport { LinkMenu, LinkPreview, LinkTrigger } from './elements';\nimport { resolveHref } from './href';\nimport type { Href } from '../types';\nimport { useLinkPreviewContext } from './preview/LinkPreviewContext';\nimport { NativeLinkPreview } from './preview/native';\nimport { useNextScreenId } from './preview/useNextScreenId';\nimport { LinkProps } from './useLinkHooks';\nimport { getFirstChildOfType } from '../utils/children';\nimport { shouldLinkExternally } from '../utils/url';\n\nconst isPad = Platform.OS === 'ios' && Platform.isPad;\n\ninterface LinkWithPreviewProps extends LinkProps {\n hrefForPreviewNavigation: Href;\n}\n\nexport function LinkWithPreview({ children, ...rest }: LinkWithPreviewProps) {\n const router = useRouter();\n const { setOpenPreviewKey } = useLinkPreviewContext();\n const [isCurrentPreviewOpen, setIsCurrenPreviewOpen] = useState(false);\n\n const hrefWithoutQuery = resolveHref(rest.hrefForPreviewNavigation).split('?')[0];\n const prevHrefWithoutQuery = useRef(hrefWithoutQuery);\n\n useEffect(() => {\n if (isCurrentPreviewOpen) {\n if (prevHrefWithoutQuery.current !== hrefWithoutQuery) {\n throw new Error(\n 'Link does not support changing the href prop after the preview has been opened. Please ensure that the href prop is stable and does not change between renders.'\n );\n }\n } else {\n prevHrefWithoutQuery.current = hrefWithoutQuery;\n }\n }, [hrefWithoutQuery]);\n\n const [{ nextScreenId, tabPath }, prefetch] = useNextScreenId();\n\n useEffect(() => {\n if (rest.replace) {\n if (process.env.NODE_ENV !== 'production') {\n throw new Error('Using replace links with preview is not supported');\n } else {\n console.warn('Using replace links with preview is not supported');\n }\n }\n }, [rest.href, rest.replace]);\n\n const triggerElement = React.useMemo(\n () => getFirstChildOfType(children, LinkTrigger),\n [children]\n );\n const menuElement = React.useMemo(() => getFirstChildOfType(children, LinkMenu), [children]);\n const previewElement = React.useMemo(\n () => getFirstChildOfType(children, LinkPreview),\n [children]\n );\n\n if ((previewElement || menuElement) && !triggerElement) {\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n 'When you use Link.Preview, you must use Link.Trigger to specify the trigger element.'\n );\n } else {\n console.warn(\n 'When you use Link.Preview, you must use Link.Trigger to specify the trigger element.'\n );\n }\n }\n\n const trigger = React.useMemo(\n () => triggerElement ?? {children},\n [triggerElement, children]\n );\n\n const preview = React.useMemo(\n () => (shouldLinkExternally(String(rest.href)) || !previewElement ? null : previewElement),\n [previewElement, rest.href]\n );\n\n const isPreviewTapped = useRef(false);\n const blockPressRef = useRef(false);\n\n const tabPathValue = useMemo(\n () => ({\n path: tabPath,\n }),\n [tabPath]\n );\n\n const hasPreview = !!previewElement;\n\n if (rest.replace) {\n return ;\n }\n\n return (\n {\n if (hasPreview) {\n blockPressRef.current = true;\n isPreviewTapped.current = false;\n prefetch(rest.hrefForPreviewNavigation);\n setIsCurrenPreviewOpen(true);\n }\n }}\n onPreviewWillClose={() => {\n if (hasPreview) {\n setIsCurrenPreviewOpen(false);\n // When preview was not tapped, then we need to enable the screen stack animation\n // Otherwise this will happen in StackNavigator, when new screen is opened\n if (!isPreviewTapped.current || isPad) {\n setOpenPreviewKey(undefined);\n }\n }\n }}\n onPreviewDidClose={() => {\n blockPressRef.current = false;\n if (hasPreview && isPreviewTapped.current && isPad) {\n router.navigate(rest.hrefForPreviewNavigation, { __internal__PreviewKey: nextScreenId });\n }\n }}\n onPreviewTapped={() => {\n isPreviewTapped.current = true;\n if (!isPad) {\n router.navigate(rest.hrefForPreviewNavigation, { __internal__PreviewKey: nextScreenId });\n }\n }}\n style={{ display: 'contents' }}\n disableForceFlatten>\n \n \n \n {preview}\n {menuElement}\n \n \n \n );\n}\n"]} \ No newline at end of file diff --git a/packages/expo-router/src/link/BaseExpoRouterLink.tsx b/packages/expo-router/src/link/BaseExpoRouterLink.tsx index ae98069a436578..3799bbbf87ebd8 100644 --- a/packages/expo-router/src/link/BaseExpoRouterLink.tsx +++ b/packages/expo-router/src/link/BaseExpoRouterLink.tsx @@ -1,9 +1,10 @@ 'use client'; // Fork of @react-navigation/native Link.tsx with `href` and `replace` support added and // `to` / `action` support removed. -import React, { useMemo, MouseEvent } from 'react'; +import React, { useMemo, MouseEvent, use } from 'react'; import { Text, GestureResponderEvent, Platform } from 'react-native'; +import { InternalLinkPreviewContext } from './InternalLinkPreviewContext'; import { resolveHref } from './href'; import { useInteropClassName, useHrefAttrs, LinkProps } from './useLinkHooks'; import useLinkToPathProps from './useLinkToPathProps'; @@ -44,6 +45,8 @@ export function BaseExpoRouterLink({ if (replace) event = 'REPLACE'; if (dismissTo) event = 'POP_TO'; + const previewContext = use(InternalLinkPreviewContext); + const props = useLinkToPathProps({ href: resolvedHref, event, @@ -53,6 +56,9 @@ export function BaseExpoRouterLink({ }); const onPress = (e: MouseEvent | GestureResponderEvent) => { + if (previewContext?.blockPressRef.current) { + return; + } if ('onPress' in rest) { rest.onPress?.(e); } diff --git a/packages/expo-router/src/link/InternalLinkPreviewContext.ts b/packages/expo-router/src/link/InternalLinkPreviewContext.ts index ffd1727d40c5c3..78bd4c32189c67 100644 --- a/packages/expo-router/src/link/InternalLinkPreviewContext.ts +++ b/packages/expo-router/src/link/InternalLinkPreviewContext.ts @@ -1,7 +1,13 @@ -import { createContext } from 'react'; +import { createContext, type RefObject } from 'react'; import type { LinkProps } from './useLinkHooks'; +export type InternalLinkPreviewContextValue = { + isVisible: boolean; + href: LinkProps['href']; + blockPressRef: RefObject; +}; + export const InternalLinkPreviewContext = createContext< - { isVisible: boolean; href: LinkProps['href'] } | undefined + InternalLinkPreviewContextValue | undefined >(undefined); diff --git a/packages/expo-router/src/link/LinkWithPreview.tsx b/packages/expo-router/src/link/LinkWithPreview.tsx index a6bafa4f86abdf..80949d8d40318f 100644 --- a/packages/expo-router/src/link/LinkWithPreview.tsx +++ b/packages/expo-router/src/link/LinkWithPreview.tsx @@ -88,6 +88,7 @@ export function LinkWithPreview({ children, ...rest }: LinkWithPreviewProps) { ); const isPreviewTapped = useRef(false); + const blockPressRef = useRef(false); const tabPathValue = useMemo( () => ({ @@ -108,6 +109,7 @@ export function LinkWithPreview({ children, ...rest }: LinkWithPreviewProps) { tabPath={isPad ? undefined : tabPathValue} onWillPreviewOpen={() => { if (hasPreview) { + blockPressRef.current = true; isPreviewTapped.current = false; prefetch(rest.hrefForPreviewNavigation); setIsCurrenPreviewOpen(true); @@ -124,6 +126,7 @@ export function LinkWithPreview({ children, ...rest }: LinkWithPreviewProps) { } }} onPreviewDidClose={() => { + blockPressRef.current = false; if (hasPreview && isPreviewTapped.current && isPad) { router.navigate(rest.hrefForPreviewNavigation, { __internal__PreviewKey: nextScreenId }); } @@ -138,7 +141,11 @@ export function LinkWithPreview({ children, ...rest }: LinkWithPreviewProps) { disableForceFlatten> + value={{ + isVisible: isCurrentPreviewOpen, + href: rest.hrefForPreviewNavigation, + blockPressRef, + }}> {preview} {menuElement} diff --git a/packages/expo-router/src/link/preview/__tests__/menu.test.ios.tsx b/packages/expo-router/src/link/preview/__tests__/menu.test.ios.tsx index a0f6be5d51571d..3cc752cb63b2b5 100644 --- a/packages/expo-router/src/link/preview/__tests__/menu.test.ios.tsx +++ b/packages/expo-router/src/link/preview/__tests__/menu.test.ios.tsx @@ -31,6 +31,7 @@ describe('LinkMenu', () => { const mockContext = { isVisible: true, href: '/test', + blockPressRef: { current: false }, }; const wrapper = ({ children }: { children: React.ReactNode }) => (