Skip to content
This repository has been archived by the owner on Dec 21, 2023. It is now read-only.

Commit

Permalink
improve link component + navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
axeldelafosse committed Dec 7, 2021
1 parent 6b43aa8 commit 3829095
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 47 deletions.
65 changes: 65 additions & 0 deletions packages/app/navigation/link.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import type { ComponentProps, ComponentType } from 'react'
import { Platform, TextProps, ViewProps, View } from 'react-native'
import { useLinkProps } from '@react-navigation/native'
import NextLink from 'next/link'

import { Pressable, Text } from 'design-system'
import { parseNextPath } from './parse-next-path'

type Props = {
children: React.ReactNode
} & Omit<ComponentProps<typeof NextLink>, 'passHref'>

function LinkCore({
children,
href,
as,
componentProps,
Component,
...props
}: Props & {
Component: ComponentType<any>
componentProps?: any
}) {
if (Platform.OS === 'web') {
return (
<NextLink {...props} href={href} as={as} passHref>
<Component {...componentProps}>{children}</Component>
</NextLink>
)
}

// eslint-disable-next-line react-hooks/rules-of-hooks
const linkProps = useLinkProps({
to: parseNextPath(as ?? href), // TODO: should this prefer href or as?
})

return (
<Component {...componentProps} {...linkProps}>
{children}
</Component>
)
}

type LinkProps = Props & { viewProps?: ViewProps }

function Link({ viewProps, ...props }: LinkProps) {
return (
<LinkCore
{...props}
Component={Platform.select({
web: View,
default: Pressable as any,
})}
componentProps={viewProps}
/>
)
}

type TextLinkProps = Props & { textProps?: TextProps }

function TextLink({ textProps, ...props }: TextLinkProps) {
return <LinkCore {...props} Component={Text} componentProps={textProps} />
}

export { Link, TextLink, LinkCore }
24 changes: 0 additions & 24 deletions packages/app/navigation/link/index.tsx

This file was deleted.

3 changes: 0 additions & 3 deletions packages/app/navigation/link/index.web.tsx

This file was deleted.

34 changes: 34 additions & 0 deletions packages/app/navigation/parse-next-path.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { NextRouter } from 'next/router'

const parseNextPath = (from: Parameters<NextRouter['push']>[0]) => {
let path = (typeof from == 'string' ? from : from.pathname) || ''

// replace each instance of [key] with the corresponding value from query[key]
// this ensures we're navigating to the correct URL
// it currently ignores [...param]
// but I can't see why you would use this with RN + Next.js
if (typeof from == 'object' && from.query && typeof from.query == 'object') {
const query = { ...from.query }
for (const key in query) {
if (path.includes(`[${key}]`)) {
path = path.replace(`[${key}]`, `${query[key] ?? ''}`)
delete query[key]
}
}
if (Object.keys(query).length) {
path += '?'
for (const key in query) {
if (query[key] != null) {
path += `${key}=${query[key]}&`
}
}
if (path.endsWith('&')) {
path = path.slice(0, -1)
}
}
}

return path
}

export { parseNextPath }
24 changes: 4 additions & 20 deletions packages/app/navigation/use-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,12 @@ import { reloadAsync } from 'expo-updates'
import LinkingContext from '@react-navigation/native/lib/module/LinkingContext'
import type { LinkingOptions } from '@react-navigation/native'

import { parseNextPath } from './parse-next-path'

// hack to access getStateForAction from react-navigation's stack
// https://github.com/react-navigation/react-navigation/blob/main/packages/routers/src/StackRouter.tsx#L224
const stack = StackRouter({})

const path = (from: Parameters<ReturnType<typeof useRouter>['push']>[0]) => {
let path = (typeof from == 'string' ? from : from.pathname) || ''

// replace each instance of [key] with the corresponding value from query[key]
// this ensures we're navigating to the correct URL
// it currently ignores [...param]
// but I can't see why you would use this with RN + Next.js
if (typeof from == 'object' && from.query && typeof from.query == 'object') {
for (const key in from.query) {
if (from.query[key] != null) {
path = path.replace(`[${key}]`, `${from.query[key]}`)
}
}
}

return path
}

const getPath = (navigationState: NavigationState) => {
return (
navigationState?.routes?.[navigationState?.index]?.path ??
Expand All @@ -57,7 +41,7 @@ export function useRouter() {
} else {
const [url, as] = nextProps

const to = as ? path(as) : path(url)
const to = as ? parseNextPath(as) : parseNextPath(url)

if (to) {
linkTo(to)
Expand All @@ -73,7 +57,7 @@ export function useRouter() {
} else {
const [url, as] = nextProps

const to = as ? path(as) : path(url)
const to = as ? parseNextPath(as) : parseNextPath(url)

if (to) {
linkTo(to)
Expand Down

0 comments on commit 3829095

Please sign in to comment.