@@ -22,8 +22,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SO
2222*/
2323
2424import type { PropsWithChildren , ReactNode } from 'react' ;
25- import React , { useEffect } from 'react' ;
26- import { Link , NavigationContainer } from '@react-navigation/native' ;
25+ import React , { useEffect , useState } from 'react' ;
26+ import { Appearance , Linking , StatusBar } from 'react-native' ;
27+ import {
28+ Link ,
29+ NavigationContainer ,
30+ useNavigation ,
31+ type NavigationProp ,
32+ } from '@react-navigation/native' ;
2733import { createBottomTabNavigator } from '@react-navigation/bottom-tabs' ;
2834import { createNativeStackNavigator } from '@react-navigation/native-stack' ;
2935import { ApolloClient , InMemoryCache , ApolloProvider } from '@apollo/client' ;
@@ -46,7 +52,6 @@ import type {
4652} from '@shopify/checkout-sheet-kit' ;
4753import { ConfigProvider } from './context/Config' ;
4854import { ThemeProvider , getNavigationTheme , useTheme } from './context/Theme' ;
49- import { Appearance , StatusBar } from 'react-native' ;
5055import { CartProvider , useCart } from './context/Cart' ;
5156import CartScreen from './screens/CartScreen' ;
5257import ProductDetailsScreen from './screens/ProductDetailsScreen' ;
@@ -78,7 +83,7 @@ export type RootStackParamList = {
7883 Catalog : undefined ;
7984 CatalogScreen : undefined ;
8085 ProductDetails : { product : ShopifyProduct ; variant ?: ProductVariant } ;
81- Cart : { userId : string } ;
86+ Cart : undefined ;
8287 CartModal : undefined ;
8388 Settings : undefined ;
8489} ;
@@ -115,6 +120,49 @@ const createNavigationIcon =
115120 return < Icon name = { name } color = { color } size = { size } /> ;
116121 } ;
117122
123+ // See https://reactnative.dev/docs/linking#get-the-deep-link for more information
124+ const useInitialURL = ( ) : { url : string | null } => {
125+ const [ url , setUrl ] = useState < string | null > ( null ) ;
126+
127+ useEffect ( ( ) => {
128+ const getUrlAsync = async ( ) => {
129+ // Get the deep link used to open the app
130+ const initialUrl = await Linking . getInitialURL ( ) ;
131+
132+ if ( initialUrl !== url ) {
133+ setUrl ( initialUrl ) ;
134+ }
135+ } ;
136+
137+ getUrlAsync ( ) ;
138+ } , [ url ] ) ;
139+
140+ return {
141+ url,
142+ } ;
143+ } ;
144+
145+ // This code is meant as example only.
146+ class StorefrontURL {
147+ readonly url : string ;
148+
149+ constructor ( url : string ) {
150+ this . url = url ;
151+ }
152+
153+ isThankYouPage ( ) : boolean {
154+ return / t h a n k [ - _ ] y o u / i. test ( this . url ) ;
155+ }
156+
157+ isCheckout ( ) : boolean {
158+ return this . url . includes ( '/checkout' ) ;
159+ }
160+
161+ isCart ( ) {
162+ return this . url . includes ( '/cart' ) ;
163+ }
164+ }
165+
118166function AppWithContext ( { children} : PropsWithChildren ) {
119167 const shopify = useShopifyCheckoutSheet ( ) ;
120168
@@ -210,48 +258,96 @@ function CartIcon() {
210258 ) ;
211259}
212260
213- function AppWithNavigation ( ) {
261+ function AppWithNavigation ( { children } : PropsWithChildren ) {
214262 const { colorScheme, preference} = useTheme ( ) ;
215- const { totalQuantity} = useCart ( ) ;
216-
217263 return (
218264 < NavigationContainer theme = { getNavigationTheme ( colorScheme , preference ) } >
219- < Tab . Navigator >
220- < Tab . Screen
221- name = "Catalog"
222- component = { CatalogStack }
223- options = { {
224- headerShown : false ,
225- tabBarIcon : createNavigationIcon ( 'shop' ) ,
226- } }
227- />
228- < Tab . Screen
229- name = "Cart"
230- component = { CartScreen }
231- options = { {
232- tabBarIcon : createNavigationIcon ( 'shopping-bag' ) ,
233- tabBarBadge : totalQuantity > 0 ? totalQuantity : undefined ,
234- } }
235- />
236- < Tab . Screen
237- name = "Settings"
238- component = { SettingsScreen }
239- options = { {
240- tabBarIcon : createNavigationIcon ( 'cog' ) ,
241- } }
242- />
243- </ Tab . Navigator >
265+ { children }
244266 </ NavigationContainer >
245267 ) ;
246268}
247269
270+ function Routes ( ) {
271+ const { totalQuantity} = useCart ( ) ;
272+ const navigation = useNavigation < NavigationProp < RootStackParamList > > ( ) ;
273+ const { url : initialUrl } = useInitialURL ( ) ;
274+ const shopify = useShopifyCheckoutSheet ( ) ;
275+
276+ useEffect ( ( ) => {
277+ async function handleUniversalLink ( url : string ) {
278+ const storefrontUrl = new StorefrontURL ( url ) ;
279+
280+ switch ( true ) {
281+ // Checkout URLs
282+ case storefrontUrl . isCheckout ( ) && ! storefrontUrl . isThankYouPage ( ) :
283+ shopify . present ( url ) ;
284+ return ;
285+ // Cart URLs
286+ case storefrontUrl . isCart ( ) :
287+ navigation . navigate ( 'Cart' ) ;
288+ return ;
289+ }
290+
291+ // Open everything else in a mobile browser
292+ const canOpenUrl = await Linking . canOpenURL ( url ) ;
293+
294+ if ( canOpenUrl ) {
295+ await Linking . openURL ( url ) ;
296+ }
297+ }
298+
299+ if ( initialUrl ) {
300+ handleUniversalLink ( initialUrl ) ;
301+ }
302+
303+ // Subscribe to universal links
304+ const subscription = Linking . addEventListener ( 'url' , ( { url} ) => {
305+ handleUniversalLink ( url ) ;
306+ } ) ;
307+
308+ return ( ) => {
309+ subscription . remove ( ) ;
310+ } ;
311+ } , [ initialUrl , shopify , navigation ] ) ;
312+
313+ return (
314+ < Tab . Navigator >
315+ < Tab . Screen
316+ name = "Catalog"
317+ component = { CatalogStack }
318+ options = { {
319+ headerShown : false ,
320+ tabBarIcon : createNavigationIcon ( 'shop' ) ,
321+ } }
322+ />
323+ < Tab . Screen
324+ name = "Cart"
325+ component = { CartScreen }
326+ options = { {
327+ tabBarIcon : createNavigationIcon ( 'shopping-bag' ) ,
328+ tabBarBadge : totalQuantity > 0 ? totalQuantity : undefined ,
329+ } }
330+ />
331+ < Tab . Screen
332+ name = "Settings"
333+ component = { SettingsScreen }
334+ options = { {
335+ tabBarIcon : createNavigationIcon ( 'cog' ) ,
336+ } }
337+ />
338+ </ Tab . Navigator >
339+ ) ;
340+ }
341+
248342function App ( ) {
249343 return (
250344 < ErrorBoundary >
251345 < ShopifyCheckoutSheetProvider configuration = { config } >
252346 < AppWithTheme >
253347 < AppWithContext >
254- < AppWithNavigation />
348+ < AppWithNavigation >
349+ < Routes />
350+ </ AppWithNavigation >
255351 </ AppWithContext >
256352 </ AppWithTheme >
257353 </ ShopifyCheckoutSheetProvider >
0 commit comments