Showing
9 changed files
with
136 additions
and
2 deletions
| @@ -41,6 +41,13 @@ | @@ -41,6 +41,13 @@ | ||
| 41 | <action android:name="android.intent.action.MAIN" /> | 41 | <action android:name="android.intent.action.MAIN" /> |
| 42 | <category android:name="android.intent.category.LAUNCHER" /> | 42 | <category android:name="android.intent.category.LAUNCHER" /> |
| 43 | </intent-filter> | 43 | </intent-filter> |
| 44 | + <intent-filter> | ||
| 45 | + <action android:name="android.intent.action.VIEW" /> | ||
| 46 | + <category android:name="android.intent.category.DEFAULT" /> | ||
| 47 | + <category android:name="android.intent.category.BROWSABLE" /> | ||
| 48 | + <!-- Accepts URIs that begin with "example://gizmos” --> | ||
| 49 | + <data android:scheme="ngplay" android:host="open.my.app" /> | ||
| 50 | + </intent-filter> | ||
| 44 | </activity> | 51 | </activity> |
| 45 | <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" /> | 52 | <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" /> |
| 46 | </application> | 53 | </application> |
| @@ -20,6 +20,19 @@ | @@ -20,6 +20,19 @@ | ||
| 20 | <string>1.0</string> | 20 | <string>1.0</string> |
| 21 | <key>CFBundleSignature</key> | 21 | <key>CFBundleSignature</key> |
| 22 | <string>????</string> | 22 | <string>????</string> |
| 23 | + <key>CFBundleURLTypes</key> | ||
| 24 | + <array> | ||
| 25 | + <dict> | ||
| 26 | + <key>CFBundleTypeRole</key> | ||
| 27 | + <string>Editor</string> | ||
| 28 | + <key>CFBundleURLName</key> | ||
| 29 | + <string>base</string> | ||
| 30 | + <key>CFBundleURLSchemes</key> | ||
| 31 | + <array> | ||
| 32 | + <string>ngplay</string> | ||
| 33 | + </array> | ||
| 34 | + </dict> | ||
| 35 | + </array> | ||
| 23 | <key>CFBundleVersion</key> | 36 | <key>CFBundleVersion</key> |
| 24 | <string>1</string> | 37 | <string>1</string> |
| 25 | <key>LSRequiresIPhoneOS</key> | 38 | <key>LSRequiresIPhoneOS</key> |
| @@ -23,6 +23,10 @@ import { useI18nStrings } from '../i18n'; | @@ -23,6 +23,10 @@ import { useI18nStrings } from '../i18n'; | ||
| 23 | import CameraScreen from './CameraScreen'; | 23 | import CameraScreen from './CameraScreen'; |
| 24 | import Library from './Library'; | 24 | import Library from './Library'; |
| 25 | import ReadableCode from './ReadableCode'; | 25 | import ReadableCode from './ReadableCode'; |
| 26 | +import { useDeepLinking } from '../utility/handleDeepLinking'; | ||
| 27 | +import { navigationRef, useMountedRef } from '../utility/rootNavigation'; | ||
| 28 | +import ShortcutPage from './ShortcutItem'; | ||
| 29 | +import useQuickAction from '../utility/useQuickAction'; | ||
| 26 | 30 | ||
| 27 | const MainTab = createBottomTabNavigator<MainTabParamList>(); | 31 | const MainTab = createBottomTabNavigator<MainTabParamList>(); |
| 28 | 32 | ||
| @@ -93,8 +97,14 @@ const MainStack = createStackNavigator<MainStackParamList>(); | @@ -93,8 +97,14 @@ const MainStack = createStackNavigator<MainStackParamList>(); | ||
| 93 | const Container = () => { | 97 | const Container = () => { |
| 94 | const inDarkMode = useDarkMode(); | 98 | const inDarkMode = useDarkMode(); |
| 95 | const strings = useI18nStrings(); | 99 | const strings = useI18nStrings(); |
| 100 | + | ||
| 101 | + useMountedRef(); | ||
| 102 | + useDeepLinking(); | ||
| 103 | + useQuickAction(); | ||
| 104 | + | ||
| 96 | return ( | 105 | return ( |
| 97 | <NavigationContainer | 106 | <NavigationContainer |
| 107 | + ref={navigationRef} | ||
| 98 | theme={inDarkMode ? themeForNav.dark : themeForNav.light} | 108 | theme={inDarkMode ? themeForNav.dark : themeForNav.light} |
| 99 | > | 109 | > |
| 100 | <MainStack.Navigator | 110 | <MainStack.Navigator |
| @@ -125,6 +135,7 @@ const Container = () => { | @@ -125,6 +135,7 @@ const Container = () => { | ||
| 125 | <MainStack.Screen name="RNLocalize" component={RNLocalize} /> | 135 | <MainStack.Screen name="RNLocalize" component={RNLocalize} /> |
| 126 | <MainStack.Screen name="RNCamera" component={CameraScreen} /> | 136 | <MainStack.Screen name="RNCamera" component={CameraScreen} /> |
| 127 | <MainStack.Screen name="RNCode" component={ReadableCode} /> | 137 | <MainStack.Screen name="RNCode" component={ReadableCode} /> |
| 138 | + <MainStack.Screen name="ShortcutItem" component={ShortcutPage} /> | ||
| 128 | </MainStack.Navigator> | 139 | </MainStack.Navigator> |
| 129 | </NavigationContainer> | 140 | </NavigationContainer> |
| 130 | ); | 141 | ); |
| @@ -9,12 +9,19 @@ import { NativeModules, Alert } from 'react-native'; | @@ -9,12 +9,19 @@ import { NativeModules, Alert } from 'react-native'; | ||
| 9 | 9 | ||
| 10 | const items: ShortcutItem[] = [ | 10 | const items: ShortcutItem[] = [ |
| 11 | { | 11 | { |
| 12 | - type: 'Library', | 12 | + type: 'Shortcut', |
| 13 | title: 'Shortcut', | 13 | title: 'Shortcut', |
| 14 | subtitle: 'Show shortcut page', | 14 | subtitle: 'Show shortcut page', |
| 15 | userInfo: { url: 'ngplay://open.my.app/ShortcutItem?id=trigger' }, | 15 | userInfo: { url: 'ngplay://open.my.app/ShortcutItem?id=trigger' }, |
| 16 | icon: 'Compose', | 16 | icon: 'Compose', |
| 17 | }, | 17 | }, |
| 18 | + { | ||
| 19 | + type: 'SystemInfo', | ||
| 20 | + title: 'System Info', | ||
| 21 | + subtitle: 'Show SystemInfo tab', | ||
| 22 | + userInfo: { url: 'ngplay://open.my.app/SystemInfo' }, | ||
| 23 | + icon: 'Order', | ||
| 24 | + }, | ||
| 18 | ]; | 25 | ]; |
| 19 | 26 | ||
| 20 | const ShortcutPage = () => { | 27 | const ShortcutPage = () => { |
| @@ -9,12 +9,13 @@ export type MainTabParamList = { | @@ -9,12 +9,13 @@ export type MainTabParamList = { | ||
| 9 | }; | 9 | }; |
| 10 | 10 | ||
| 11 | export type MainStackParamList = { | 11 | export type MainStackParamList = { |
| 12 | - MainTab: undefined; | 12 | + MainTab: { screen?: keyof MainTabParamList }; |
| 13 | RNDeviceInfoList: undefined; | 13 | RNDeviceInfoList: undefined; |
| 14 | WebviewScreen: { uri: string } | undefined; | 14 | WebviewScreen: { uri: string } | undefined; |
| 15 | RNLocalize: undefined; | 15 | RNLocalize: undefined; |
| 16 | RNCamera: undefined; | 16 | RNCamera: undefined; |
| 17 | RNCode: undefined; | 17 | RNCode: undefined; |
| 18 | + ShortcutItem: { id?: string }; | ||
| 18 | }; | 19 | }; |
| 19 | 20 | ||
| 20 | export type MainTabScreenProps<RouteName extends keyof MainTabParamList> = { | 21 | export type MainTabScreenProps<RouteName extends keyof MainTabParamList> = { |
| 1 | declare type NVPair<V = string> = { name: string; value: V }; | 1 | declare type NVPair<V = string> = { name: string; value: V }; |
| 2 | + | ||
| 3 | +declare type PromiseResult<T extends Promise<any>> = T extends Promise<infer U> | ||
| 4 | + ? U | ||
| 5 | + : never; | ||
| 6 | + | ||
| 7 | +declare type ResultOf<T extends () => any> = ReturnType<T> extends Promise< | ||
| 8 | + infer R | ||
| 9 | +> | ||
| 10 | + ? R | ||
| 11 | + : ReturnType<T>; |
src/utility/handleDeepLinking.ts
0 → 100644
| 1 | +import { Linking } from 'react-native'; | ||
| 2 | +import { useEffect } from 'react'; | ||
| 3 | +import { rootNavigate, AllRouteNames } from './rootNavigation'; | ||
| 4 | +import URLParse from 'url-parse'; | ||
| 5 | + | ||
| 6 | +export const appScheme = 'ngplay:'; | ||
| 7 | +export const universalLink = ''; | ||
| 8 | + | ||
| 9 | +export function handleUrl(url?: string | null) { | ||
| 10 | + if (!url) return; | ||
| 11 | + const urlParse = new URLParse(url, true); | ||
| 12 | + if (urlParse.protocol === appScheme) { | ||
| 13 | + const routeName = urlParse.pathname.substring(1) as AllRouteNames; | ||
| 14 | + const params = urlParse.query; | ||
| 15 | + if (routeName) { | ||
| 16 | + rootNavigate(routeName, params); | ||
| 17 | + } | ||
| 18 | + } | ||
| 19 | +} | ||
| 20 | + | ||
| 21 | +export function handleUrlEvent(event: { url: string }) { | ||
| 22 | + handleUrl(event.url); | ||
| 23 | +} | ||
| 24 | + | ||
| 25 | +export function useDeepLinking() { | ||
| 26 | + Linking.getInitialURL().then(handleUrl); | ||
| 27 | + useEffect(() => { | ||
| 28 | + Linking.addEventListener('url', handleUrlEvent); | ||
| 29 | + return () => Linking.removeListener('url', handleUrlEvent); | ||
| 30 | + }, []); | ||
| 31 | +} |
src/utility/rootNavigation.ts
0 → 100644
| 1 | +import * as React from 'react'; | ||
| 2 | +import { NavigationContainerRef } from '@react-navigation/native'; | ||
| 3 | +import { MainStackParamList, MainTabParamList } from '../type/Navigation'; | ||
| 4 | + | ||
| 5 | +export const navigationRef = React.createRef<NavigationContainerRef>(); | ||
| 6 | +export const isMountedRef = React.createRef< | ||
| 7 | + boolean | ||
| 8 | +>() as React.MutableRefObject<boolean>; | ||
| 9 | + | ||
| 10 | +export function useMountedRef() { | ||
| 11 | + isMountedRef.current = true; | ||
| 12 | + return () => { | ||
| 13 | + isMountedRef.current = false; | ||
| 14 | + }; | ||
| 15 | +} | ||
| 16 | + | ||
| 17 | +export type ParamList = MainStackParamList & MainTabParamList; | ||
| 18 | +export type AllRouteNames = keyof ParamList; | ||
| 19 | + | ||
| 20 | +export function rootNavigate<K extends AllRouteNames>( | ||
| 21 | + name: K, | ||
| 22 | + params: ParamList[K] | ||
| 23 | +) { | ||
| 24 | + if (isMountedRef.current && navigationRef.current) { | ||
| 25 | + navigationRef.current.navigate(name, params); | ||
| 26 | + } | ||
| 27 | +} |
src/utility/useQuickAction.ts
0 → 100644
| 1 | +import { | ||
| 2 | + QuickActionEmitter, | ||
| 3 | + QuickActionEventName, | ||
| 4 | + ShortcutItem, | ||
| 5 | + popInitialAction, | ||
| 6 | +} from 'react-native-quick-actions'; | ||
| 7 | +import { useEffect } from 'react'; | ||
| 8 | +import { handleUrl } from './handleDeepLinking'; | ||
| 9 | + | ||
| 10 | +export default function useQuickAction() { | ||
| 11 | + useEffect(() => { | ||
| 12 | + popInitialAction() | ||
| 13 | + .then(item => { | ||
| 14 | + handleUrl(item?.userInfo.url); | ||
| 15 | + }) | ||
| 16 | + .catch(() => {}); | ||
| 17 | + const subscription = QuickActionEmitter.addListener( | ||
| 18 | + QuickActionEventName, | ||
| 19 | + (item?: ShortcutItem) => { | ||
| 20 | + handleUrl(item?.userInfo.url); | ||
| 21 | + } | ||
| 22 | + ); | ||
| 23 | + return () => { | ||
| 24 | + subscription.remove(); | ||
| 25 | + }; | ||
| 26 | + }, []); | ||
| 27 | +} |
-
Please register or login to post a comment