Forráskód Böngészése

deep links for profile

Viktoriia 5 hónapja
szülő
commit
740494548e
5 módosított fájl, 141 hozzáadás és 47 törlés
  1. 11 1
      App.tsx
  2. 54 45
      Route.tsx
  3. 14 0
      app.config.ts
  4. 3 1
      package.json
  5. 59 0
      src/contexts/NavigationContext.tsx

+ 11 - 1
App.tsx

@@ -15,7 +15,7 @@ import { setupInterceptors } from 'src/utils/request';
 import { ErrorModal, WarningModal } from 'src/components';
 import React from 'react';
 import { Linking, Platform } from 'react-native';
-import { API_URL, APP_VERSION } from 'src/constants';
+import { API_HOST, API_URL, APP_VERSION } from 'src/constants';
 import axios from 'axios';
 import { API } from 'src/types';
 
@@ -34,6 +34,15 @@ Sentry.init({
   ignoreErrors: ['Network Error', 'ECONNABORTED', 'timeout of 10000ms exceeded']
 });
 
+const linking = {
+  prefixes: [API_HOST, 'nomadmania://'],
+  config: {
+    screens: {
+      publicProfileView: '/profile/:userId'
+    }
+  }
+};
+
 const App = () => {
   return (
     <QueryClientProvider client={queryClient}>
@@ -95,6 +104,7 @@ const InnerApp = () => {
           onReady={() => {
             routingInstrumentation.registerNavigationContainer(navigation);
           }}
+          linking={linking}
         >
           <Route />
           <ConnectionBanner />

+ 54 - 45
Route.tsx

@@ -94,6 +94,7 @@ import { Splash } from 'src/components/SplashSpinner';
 import { useMessagesStore } from 'src/stores/unreadMessagesStore';
 import LocationSharingScreen from 'src/screens/LocationSharingScreen';
 import { useFriendsNotificationsStore } from 'src/stores/friendsNotificationsStore';
+import { NavigationProvider } from 'src/contexts/NavigationContext';
 
 enableScreens();
 
@@ -463,51 +464,59 @@ const Route = () => {
 
   return (
     <PushNotificationProvider>
-      <ScreenStack.Navigator
-        screenOptions={{ headerShown: false, cardStyle: { backgroundColor: 'white' } }}
-        initialRouteName={token ? NAVIGATION_PAGES.IN_APP : NAVIGATION_PAGES.WELCOME}
-      >
-        <ScreenStack.Screen name={NAVIGATION_PAGES.WELCOME} component={WelcomeScreen} />
-        <ScreenStack.Screen name={NAVIGATION_PAGES.LOGIN} component={LoginScreen} />
-        <ScreenStack.Screen name={NAVIGATION_PAGES.REGISTER} component={JoinUsScreen} />
-        <ScreenStack.Screen name={NAVIGATION_PAGES.REGISTER_ACCOUNT_DATA} component={EditAccount} />
-        <ScreenStack.Screen
-          name={NAVIGATION_PAGES.RESET_PASSWORD}
-          component={ResetPasswordScreen}
-        />
-        <ScreenStack.Screen
-          name={NAVIGATION_PAGES.RESET_PASSWORD_DEEP}
-          component={ResetPasswordDeepScreen}
-        />
-        <ScreenStack.Screen name={NAVIGATION_PAGES.INFO} component={InfoScreen} />
-        <ScreenStack.Screen name={NAVIGATION_PAGES.JOIN_INFO} component={JoinInfoScreen} />
-        <ScreenStack.Screen name={NAVIGATION_PAGES.DISCOVER_INFO} component={DiscoverInfoScreen} />
-        <ScreenStack.Screen name={NAVIGATION_PAGES.PLAN_INFO} component={PlanInfoScreen} />
-        <ScreenStack.Screen
-          name={NAVIGATION_PAGES.FIRST_STEPS_INFO}
-          component={FirstStepsInfoScreen}
-        />
-        <ScreenStack.Screen
-          name={NAVIGATION_PAGES.COUNTRIES_INFO}
-          component={CountriesInfoScreen}
-        />
-        <ScreenStack.Screen name={NAVIGATION_PAGES.DARE_INFO} component={DareInfoScreen} />
-        <ScreenStack.Screen name={NAVIGATION_PAGES.REGIONS_INFO} component={RegionsInfoScreen} />
-        <ScreenStack.Screen name={NAVIGATION_PAGES.TRIPS_INFO} component={TripsInfoScreen} />
-        <ScreenStack.Screen name={NAVIGATION_PAGES.FIXERS_INFO} component={FixersInfoScreen} />
-        <ScreenStack.Screen name={NAVIGATION_PAGES.EARTH_INFO} component={EarthInfoScreen} />
-        <ScreenStack.Screen name={NAVIGATION_PAGES.IN_APP}>
-          {() => (
-            <MapDrawer.Navigator drawerContent={(props) => <MenuDrawer {...props} />}>
-              <MapDrawer.Screen
-                name="DrawerApp"
-                component={BottomTabNavigator}
-                options={{ headerShown: false }}
-              />
-            </MapDrawer.Navigator>
-          )}
-        </ScreenStack.Screen>
-      </ScreenStack.Navigator>
+      <NavigationProvider>
+        <ScreenStack.Navigator
+          screenOptions={{ headerShown: false, cardStyle: { backgroundColor: 'white' } }}
+          initialRouteName={token ? NAVIGATION_PAGES.IN_APP : NAVIGATION_PAGES.WELCOME}
+        >
+          <ScreenStack.Screen name={NAVIGATION_PAGES.WELCOME} component={WelcomeScreen} />
+          <ScreenStack.Screen name={NAVIGATION_PAGES.LOGIN} component={LoginScreen} />
+          <ScreenStack.Screen name={NAVIGATION_PAGES.REGISTER} component={JoinUsScreen} />
+          <ScreenStack.Screen
+            name={NAVIGATION_PAGES.REGISTER_ACCOUNT_DATA}
+            component={EditAccount}
+          />
+          <ScreenStack.Screen
+            name={NAVIGATION_PAGES.RESET_PASSWORD}
+            component={ResetPasswordScreen}
+          />
+          <ScreenStack.Screen
+            name={NAVIGATION_PAGES.RESET_PASSWORD_DEEP}
+            component={ResetPasswordDeepScreen}
+          />
+          <ScreenStack.Screen name={NAVIGATION_PAGES.INFO} component={InfoScreen} />
+          <ScreenStack.Screen name={NAVIGATION_PAGES.JOIN_INFO} component={JoinInfoScreen} />
+          <ScreenStack.Screen
+            name={NAVIGATION_PAGES.DISCOVER_INFO}
+            component={DiscoverInfoScreen}
+          />
+          <ScreenStack.Screen name={NAVIGATION_PAGES.PLAN_INFO} component={PlanInfoScreen} />
+          <ScreenStack.Screen
+            name={NAVIGATION_PAGES.FIRST_STEPS_INFO}
+            component={FirstStepsInfoScreen}
+          />
+          <ScreenStack.Screen
+            name={NAVIGATION_PAGES.COUNTRIES_INFO}
+            component={CountriesInfoScreen}
+          />
+          <ScreenStack.Screen name={NAVIGATION_PAGES.DARE_INFO} component={DareInfoScreen} />
+          <ScreenStack.Screen name={NAVIGATION_PAGES.REGIONS_INFO} component={RegionsInfoScreen} />
+          <ScreenStack.Screen name={NAVIGATION_PAGES.TRIPS_INFO} component={TripsInfoScreen} />
+          <ScreenStack.Screen name={NAVIGATION_PAGES.FIXERS_INFO} component={FixersInfoScreen} />
+          <ScreenStack.Screen name={NAVIGATION_PAGES.EARTH_INFO} component={EarthInfoScreen} />
+          <ScreenStack.Screen name={NAVIGATION_PAGES.IN_APP}>
+            {() => (
+              <MapDrawer.Navigator drawerContent={(props) => <MenuDrawer {...props} />}>
+                <MapDrawer.Screen
+                  name="DrawerApp"
+                  component={BottomTabNavigator}
+                  options={{ headerShown: false }}
+                />
+              </MapDrawer.Navigator>
+            )}
+          </ScreenStack.Screen>
+        </ScreenStack.Navigator>
+      </NavigationProvider>
     </PushNotificationProvider>
   );
 };

+ 14 - 0
app.config.ts

@@ -69,6 +69,7 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
     config: {
       googleMapsApiKey: env.IOS_GOOGLE_MAP_APIKEY
     },
+    associatedDomains: ['applinks:nomadmania.com'],
     infoPlist: {
       UIBackgroundModes: ['fetch'],
       NSLocationAlwaysUsageDescription:
@@ -101,6 +102,19 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
         apiKey: env.ANDROID_GOOGLE_MAP_APIKEY
       }
     },
+    intentFilters: [
+      {
+        action: 'VIEW',
+        data: [
+          {
+            scheme: 'https',
+            host: 'nomadmania.com',
+            pathPrefix: '/profile/'
+          }
+        ],
+        category: ['BROWSABLE', 'DEFAULT']
+      }
+    ],
     googleServicesFile: './google-services.json',
     permissions: [
       // 'ACCESS_BACKGROUND_LOCATION',

+ 3 - 1
package.json

@@ -62,9 +62,10 @@
     "react-native-device-detection": "^0.2.1",
     "react-native-document-picker": "^9.3.1",
     "react-native-emoji-selector": "^0.2.0",
+    "react-native-file-viewer": "^2.1.5",
     "react-native-gesture-handler": "~2.16.1",
     "react-native-get-random-values": "^1.11.0",
-    "react-native-gifted-chat": "^2.6.3",
+    "react-native-gifted-chat": "^2.6.5",
     "react-native-google-places-autocomplete": "^2.5.7",
     "react-native-haptic-feedback": "^2.3.2",
     "react-native-image-viewing": "^0.2.2",
@@ -86,6 +87,7 @@
     "react-native-share": "^10.2.1",
     "react-native-svg": "15.2.0",
     "react-native-tab-view": "^3.5.2",
+    "react-native-url-polyfill": "^2.0.0",
     "react-native-video": "^6.5.0",
     "react-native-view-shot": "^3.7.0",
     "react-native-walkthrough-tooltip": "^1.6.0",

+ 59 - 0
src/contexts/NavigationContext.tsx

@@ -0,0 +1,59 @@
+import React, { createContext, useContext, useEffect, useState } from 'react';
+import { Linking } from 'react-native';
+import { useNavigation } from '@react-navigation/native';
+import { NAVIGATION_PAGES } from 'src/types';
+import { storage, StoreType } from 'src/storage';
+
+interface NavigationContextType {
+  handleDeepLink: () => Promise<void>;
+}
+
+const NavigationContext = createContext<NavigationContextType | null>(null);
+
+const parseURL = (url: string) => {
+  const parsedUrl = new URL(url);
+  const path = parsedUrl.pathname;
+  const queryParams = Object.fromEntries(parsedUrl.searchParams.entries());
+  return { path, queryParams };
+};
+
+export const useNavigationContext = () => useContext(NavigationContext);
+
+export const NavigationProvider = ({ children }: { children: React.ReactNode }) => {
+  const navigation = useNavigation();
+  const token = storage.get('token', StoreType.STRING);
+  const [initialUrlProcessed, setInitialUrlProcessed] = useState(false);
+
+  const handleDeepLink = async (url?: string) => {
+    const link = url || (await Linking.getInitialURL());
+    if (link) {
+      const { path } = parseURL(link);
+      if (path.startsWith('/profile') && token) {
+        const segments = path.split('/');
+        const userId = segments[2];
+        navigation.navigate(...([NAVIGATION_PAGES.PUBLIC_PROFILE_VIEW, { userId }] as never));
+      }
+    }
+    if (!initialUrlProcessed) {
+      setInitialUrlProcessed(true);
+    }
+  };
+
+  useEffect(() => {
+    if (!initialUrlProcessed) {
+      handleDeepLink();
+    }
+
+    const subscription = Linking.addEventListener('url', (event) => {
+      handleDeepLink(event.url);
+    });
+
+    return () => {
+      subscription.remove();
+    };
+  }, [initialUrlProcessed]);
+
+  return (
+    <NavigationContext.Provider value={{ handleDeepLink }}>{children}</NavigationContext.Provider>
+  );
+};