Skip to content

Commit

Permalink
feat(linking): add optional hosts + error callback
Browse files Browse the repository at this point in the history
  • Loading branch information
crherman7 committed Nov 14, 2024
1 parent 5b37ce4 commit 6bff390
Showing 1 changed file with 31 additions and 13 deletions.
44 changes: 31 additions & 13 deletions packages/app-router/src/hooks/useLinking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,57 @@ import {useNavigator} from './useNavigator';
* A custom hook that manages deep linking by listening for URL changes
* and navigating accordingly using a navigator.
*
* @param {string[]} hosts - An array of valid hostnames to match against the incoming URL.
* Only URLs with these hosts will be parsed; otherwise, the URL will be navigated directly.
* @param {(e: Error) => void} [onError] - Optional error handler invoked if URL parsing or navigation fails.
*
* @example
* function App() {
* useLinking();
* useLinking(['app'], (e) => console.error(e));
*
* return <YourAppComponent />;
* }
*
* // This hook will automatically handle incoming deep links and navigate
* // based on the URL, such as `yourapp://path?query=param`.
* @remarks
* This hook relies on the provided `hosts` to determine if the host component of a URL
* should be trusted as valid. Due to how `url-parse` handles URLs, segments of a path may be
* parsed as the host. For instance, in `yourapp://app/path`, `url-parse` will interpret `app`
* as the host. This behavior may lead to ambiguity, as `app` could be intended as part of the path
* rather than the host. By specifying `hosts`, the hook can validate the host component and ensure
* that only recognized hosts are parsed, improving navigation accuracy.
*/
export function useLinking() {
export function useLinking(hosts: string[] = [], onError?: (e: Error) => void) {
const navigator = useNavigator();

/**
* Handles the URL passed from deep linking and navigates to the correct screen.
* Processes the provided URL and navigates to the corresponding screen.
*
* @param {Object} params - Parameters object.
* @param {string | null} params.url - The URL to be processed.
* @param {Object} params - The parameters for URL handling.
* @param {string | null} params.url - The URL to be processed and navigated to, if applicable.
*
* @example
* callback({ url: 'yourapp://profile?user=123' });
*
* @remarks
* This function checks if the provided `url`'s host is within the specified `hosts`.
* If the host is included, it navigates directly to the `pathname` and `query` parameters of the URL.
* Otherwise, it prepends the host to the `pathname` and `query`, allowing for flexible navigation handling.
*/
function callback({url}: {url: string | null}) {
if (!url) return;

try {
const {pathname, query} = urlParse(url);
const {host, pathname, query} = urlParse(url);

// Navigates to the parsed URL's pathname and search params
navigator.open(pathname + query);
if (hosts.includes(host)) {
// Navigates to the parsed URL's pathname and query parameters if the host is recognized
navigator.open(pathname + query);
} else {
// If no hosts are specified, navigate using the full URL structure as a fallback
navigator.open(`/${host}${pathname}${query}`);
}
} catch (e) {
// Handle the error (e.g., log it)
onError?.(e as Error);
}
}

Expand All @@ -48,10 +67,9 @@ export function useLinking() {
(async function () {
try {
const url = await Linking.getInitialURL();

callback({url});
} catch (e) {
// Handle the error (e.g., log it)
onError?.(e as Error);
}
})();

Expand Down

0 comments on commit 6bff390

Please sign in to comment.