Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

using Next App Directory #457

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,8 @@ create-test-app
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
!.yarn/versions


.idea
.idea/**
3 changes: 3 additions & 0 deletions src/router/next-app-router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Router from 'next/navigation'

export const NextAppRouter: typeof Router | undefined = Router
51 changes: 51 additions & 0 deletions src/router/parse-app-next-path.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// import type { NextRouter } from 'next/router'
import {AppRouterInstance} from "next/dist/shared/lib/app-router-context.shared-runtime";
import {usePathname} from "next/navigation";

const parseNextAppPath = (from: Parameters<AppRouterInstance['push']>[0]) => {

console.log(from);
// 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 }
// replace dynamic routes
// and [...param] syntax
for (const key in query) {
if (path.includes(`[${key}]`)) {
path = path.replace(`[${key}]`, `${query[key] ?? ''}`)
delete query[key]
} else if (path.includes(`[...${key}]`)) {
const values = query[key]
if (Array.isArray(values)) {
path = path.replace(`[...${key}]`, values.join('/'))
delete query[key]
}
}
}

if (Object.keys(query).length) {
// add query param separator
path += '?'
for (const key in query) {
const value = query[key]
if (Array.isArray(value)) {
value.forEach((item) => {
path += `${key}=${item}&`
})
} else if (value != null) {
path += `${key}=${value}&`
}
}
if (path.endsWith('&') || path.endsWith('?')) {
path = path.slice(0, -1)
}
}
}

return path
}

export { parseNextAppPath }
138 changes: 138 additions & 0 deletions src/router/use-app-router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import {AppRouterInstance} from "next/dist/shared/lib/app-router-context.shared-runtime";
type NextRouterType = AppRouterInstance

import { Platform } from 'react-native'
import { useContext, useMemo } from 'react'

import { parseNextAppPath } from './parse-app-next-path'
import {
getActionFromState,
getStateFromPath,
LinkingContext,
StackActions,
} from './replace-helpers'

import { useLinkTo } from './use-link-to'
import { useNavigation } from './use-navigation'
import {useNextAppRouter} from "./use-next-app-router.web";
import {usePathname} from "next/navigation";

// copied from next/router to appease typescript error
// if we don't manually write this here, then we get some ReturnType error on build
// 🤷‍♂️
interface TransitionOptions {
shallow?: boolean
locale?: string | false
scroll?: boolean
}

export function useAppRouter() {
const linkTo = useLinkTo()
const navigation = useNavigation()
const nextRouter: AppRouterInstance = useNextAppRouter()
const linking = useContext(LinkingContext)
const pathName = usePathname()

return useMemo(
() => ({
push: (
url: Parameters<NextRouterType['push']>[0],
transitionOptions?: TransitionOptions
) => {
if (Platform.OS === 'web') {
nextRouter?.push(url, transitionOptions)
} else {
const to = parseNextAppPath(url) // get the link to be redirected to in react native.

if (to) {
linkTo(to)
}
}
},
replace: (
url: Parameters<NextRouterType['replace']>[0],
as?: Parameters<NextRouterType['replace']>[1],
transitionOptions?: TransitionOptions & {
experimental?:
| {
nativeBehavior?: undefined
}
| {
nativeBehavior: 'stack-replace'
isNestedNavigator: boolean
}
}
) => {
if (Platform.OS === 'web') {
nextRouter?.replace(url, transitionOptions)
} else {
const to = parseNextAppPath(url)

if (to) {
if (transitionOptions?.experimental?.nativeBehavior === 'stack-replace') {
if (linking?.options) {
// custom logic to create a replace() from a URL on native
// https://github.com/react-navigation/react-navigation/discussions/10517
const { options } = linking

const state = options?.getStateFromPath
? options.getStateFromPath(to, options.config)
: getStateFromPath(to, options?.config)

if (state) {
const action = getActionFromState(state, options?.config)

if (action !== undefined) {
if (
'payload' in action &&
action.payload &&
'name' in action.payload &&
action.payload.name
) {
const { name, params } = action.payload
if (
transitionOptions?.experimental?.isNestedNavigator &&
params &&
'screen' in params &&
params.screen
) {
navigation?.dispatch(
StackActions.replace(
params.screen,
params.params as object | undefined
)
)
} else {
navigation?.dispatch(StackActions.replace(name, params))
}
} else {
navigation?.dispatch(action)
}
} else {
navigation?.reset(state)
}
}
} else {
// fallback in case the linking context didn't work
console.warn(`[solito] replace("${to}") faced an issue. You should still see your new screen, but it probably didn't replace the previous one. This may be due to a breaking change in React Navigation.
Please open an issue at https://github.com/nandorojo/solito and report how this happened. Thanks!`)
linkTo(to)
}
} else {
linkTo(to)
}
}
}
},
back: () => {
if (Platform.OS === 'web') {
nextRouter?.back()
} else {
navigation?.goBack()
}
},
parseNextAppPath,
}),
[linkTo, navigation]
)
}
1 change: 1 addition & 0 deletions src/router/use-navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
} from '@react-navigation/core'
import { useContext } from 'react'


export const useNavigation = () => {
const root = useContext(NavigationContainerRefContext)
const navigation = useContext(NavigationContext)
Expand Down
4 changes: 4 additions & 0 deletions src/router/use-next-app-router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { useRouter } from 'next/navigation'

export const useNextRouter = (): ReturnType<typeof useRouter> | undefined =>
undefined
1 change: 1 addition & 0 deletions src/router/use-next-app-router.web.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { useRouter as useNextAppRouter } from 'next/navigation'
3 changes: 1 addition & 2 deletions src/router/use-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,10 @@ interface TransitionOptions {
export function useRouter() {
const linkTo = useLinkTo()
const navigation = useNavigation()

const nextRouter = useNextRouter()

const linking = useContext(LinkingContext)


return useMemo(
() => ({
push: (
Expand Down