Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"react-native-splash-screen": "^3.3.0",
"react-native-svg": "^15.8.0",
"react-native-vector-icons": "^10.1.0",
"react-native-webview": "^13.15.0",
"tosspayments-react-native-webview": "^1.0.0",
"zustand": "^5.0.3"
},
Expand Down
6 changes: 2 additions & 4 deletions src/components/map/MyLocationMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {StackNavigationProp} from '@react-navigation/stack';
import {RootStackParamList} from '@/types/StackNavigationType';

import S from './MyLocationMap.style';
import {routeToDetail} from '@/navigation/navigator';

const MyLocationMap = ({
cords,
Expand All @@ -20,10 +21,7 @@ const MyLocationMap = ({
const navigation = useNavigation<StackNavigationProp<RootStackParamList>>();
const handleMarkerClick = (marketId: number) => {
if (marketId !== -1) {
navigation.navigate('Detail', {
screen: 'MarketDetail',
params: {marketId},
});
routeToDetail(navigation, marketId);
}
};
return (
Expand Down
5 changes: 2 additions & 3 deletions src/components/orderDetail/OrderDescription.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {to6DigitHash} from '@ummgoban/shared';
import {DetailStackParamList} from '@/types/StackNavigationType';

import S from './OrderDescription.style';
import {routeToDetail} from '@/navigation/navigator';

type Props = {
id: string;
Expand Down Expand Up @@ -48,9 +49,7 @@ const OrderCustomerInfo = ({id, navigation, orderDetail}: Props) => {
<S.Container>
<S.OrderStatusText>{orderStatusText}</S.OrderStatusText>
<S.MarketInformation
onPress={() =>
navigation.navigate('MarketDetail', {marketId: orderDetail.marketId})
}>
onPress={() => routeToDetail(navigation, orderDetail.marketId)}>
<S.MarketName>{orderDetail.marketName}</S.MarketName>
<S.MarketAddress>{orderDetail.address}</S.MarketAddress>
</S.MarketInformation>
Expand Down
17 changes: 12 additions & 5 deletions src/navigation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,18 @@ import RegisterNavigator from './RegisterNavigator';
import DetailNavigator from './DetailNavigator';
import CartNavigator from './CartNavigator';
import MyPageNavigator from './MyPageNavigator';
import withWebViewGate from './withWebViewGate';

import {RootStackParamList} from '@/types/StackNavigationType';

const Stack = createStackNavigator<RootStackParamList>();

const HomeWithGate = withWebViewGate(HomeNavigator);
const RegisterWithGate = withWebViewGate(RegisterNavigator);
const DetailWithGate = withWebViewGate(DetailNavigator);
const CartWithGate = withWebViewGate(CartNavigator);
const MyPageWithGate = withWebViewGate(MyPageNavigator);

const AppNavigator = () => {
/** omit top, because of `@react-navigation/stack` appbar containing top safe area */
// const {left, right, bottom} = useSafeAreaInsets();
Expand All @@ -19,11 +26,11 @@ const AppNavigator = () => {
<Stack.Navigator
initialRouteName="Home"
screenOptions={{headerShown: false}}>
<Stack.Screen name="Home" component={HomeNavigator} />
<Stack.Screen name="Register" component={RegisterNavigator} />
<Stack.Screen name="Detail" component={DetailNavigator} />
<Stack.Screen name="CartRoot" component={CartNavigator} />
<Stack.Screen name="MyPageRoot" component={MyPageNavigator} />
<Stack.Screen name="Home" component={HomeWithGate} />
<Stack.Screen name="Register" component={RegisterWithGate} />
<Stack.Screen name="Detail" component={DetailWithGate} />
<Stack.Screen name="CartRoot" component={CartWithGate} />
<Stack.Screen name="MyPageRoot" component={MyPageWithGate} />
</Stack.Navigator>
);
};
Expand Down
34 changes: 34 additions & 0 deletions src/navigation/navigator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {StackNavigationProp} from '@react-navigation/stack';

import {
DetailStackParamList,
FeedStackParamList,
HomeStackParamList,
MyPageStackParamList,
RegisterStackParamList,
RootStackParamList,
} from '@/types/StackNavigationType';

export function routeToDetail(
navigation: StackNavigationProp<
| RootStackParamList
| HomeStackParamList
| FeedStackParamList
| RegisterStackParamList
| DetailStackParamList
| MyPageStackParamList
>,
marketId: number,
) {
navigation.navigate('Detail', {
screen: 'MarketDetail',
params: {marketId},
webview: {
uri: __DEV__
? `http://192.168.0.12:5173/market/${marketId}`
: // `http://192.168.0.12:5173`
'https://ummgoban.com',
title: 'webview',
},
});
}
19 changes: 19 additions & 0 deletions src/navigation/withWebViewGate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';
import WebViewScreen from '@/webview/WebViewScreen';

type RouteWithWeb = {params: {webview?: {uri: string; title?: string}}};

const withWebViewGate = <P extends {route?: RouteWithWeb}>(
NavigatorComponent: React.ComponentType<P>,
) => {
return function WebViewGateComponent(props: P) {
const webview = props.route?.params?.webview;

if (webview?.uri) {
return <WebViewScreen {...webview} />;
}
return <NavigatorComponent {...props} />;
};
};

export default withWebViewGate;
8 changes: 2 additions & 6 deletions src/screens/CustomerReviewScreen/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import S from './CustomerReviewScreen.style';
import {CustomerReviewCard} from '@/components/common/customerReview';
import {ActivityIndicator} from 'react-native-paper';
import EmptyComponent from '@/components/common/EmptyComponent';
import {routeToDetail} from '@/navigation/navigator';

type CustomerReviewScreenProps = StackScreenProps<
MyPageStackParamList,
Expand Down Expand Up @@ -49,12 +50,7 @@ const CustomerReviewScreen = ({
}

const navigateMarketDetail = (marketId: number) => {
navigation.navigate('Detail', {
screen: 'MarketDetail',
params: {
marketId: marketId,
},
});
routeToDetail(navigation, marketId);
};

if (reviews.length === 0) {
Expand Down
6 changes: 2 additions & 4 deletions src/screens/FeedScreen/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import useGPSLocation from '@/hooks/useGPSLocation';
import {RootStackParamList} from '@/types/StackNavigationType';

import S from './Feed.style';
import {routeToDetail} from '@/navigation/navigator';

type Props = {
navigation: StackNavigationProp<RootStackParamList, 'Home'>;
Expand All @@ -36,10 +37,7 @@ const FeedScreen = ({navigation}: Props) => {
const marketList = data ? data.pages.flatMap(page => page.markets) : [];

const onPressStore = (marketId: number) => {
navigation.navigate('Detail', {
screen: 'MarketDetail',
params: {marketId},
});
routeToDetail(navigation, marketId);
};

const navigateMap = () => {
Expand Down
8 changes: 2 additions & 6 deletions src/screens/OrderHistoryScreen/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import usePullDownRefresh from '@/hooks/usePullDownRefresh';
import {RootStackParamList} from '@/types/StackNavigationType';

import S from './OrderHistory.style';
import {routeToDetail} from '@/navigation/navigator';

const OrderHistoryScreen = () => {
const {data: historyList, refetch} = useOrderHistoryQuery();
Expand Down Expand Up @@ -60,12 +61,7 @@ const OrderHistoryScreen = () => {
}>
<OrderHistory
historyList={historyList}
onPressMarket={marketId =>
navigation.navigate('Detail', {
screen: 'MarketDetail',
params: {marketId: marketId},
})
}
onPressMarket={marketId => routeToDetail(navigation, marketId)}
/>
</S.OrderHistoryContainer>
);
Expand Down
6 changes: 2 additions & 4 deletions src/screens/ShoppingCartScreen/ShoppingCartPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {RootStackParamList} from '@/types/StackNavigationType';
import {Menu} from '@/components/marketDetailPage';
import {BottomButton} from '@/components/common';
import {useUpdateBucket} from '@/apis/buckets';
import {routeToDetail} from '@/navigation/navigator';

type Props = {
navigation: StackNavigationProp<RootStackParamList, 'Home'>;
Expand Down Expand Up @@ -42,10 +43,7 @@ const ShoppingCartPage = ({navigation, cartData}: Props) => {
}, [market.closeAt]);

const onPressStore = () => {
navigation.navigate('Detail', {
screen: 'MarketDetail',
params: {marketId: cartData.market.id},
});
routeToDetail(navigation, cartData.market.id);
};

const onPressPayment = () => {
Expand Down
6 changes: 2 additions & 4 deletions src/screens/SubscribeScreen/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import SubscribeMarketCard from '@/components/subscribePage/SubscribeMarketCard'
import {RootStackParamList} from '@/types/StackNavigationType';

import S from './SubscribeScreen.style';
import {routeToDetail} from '@/navigation/navigator';

type Props = {
navigation: StackNavigationProp<RootStackParamList, 'Subscribe'>;
Expand Down Expand Up @@ -48,10 +49,7 @@ const SubscribeScreen = ({navigation}: Props) => {
});

const onPressStore = (marketId: number) => {
navigation.navigate('Detail', {
screen: 'MarketDetail',
params: {marketId},
});
routeToDetail(navigation, marketId);
};

const handleEndReached = () => {
Expand Down
6 changes: 5 additions & 1 deletion src/types/StackNavigationType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ import {ParamListBase} from '@react-navigation/native';
import {OrderType} from './OrderType';
import {ProductType} from '@ummgoban/shared/lib';

type StackParamType<T> = {
export type StackParamType<T> = {
screen?: keyof T;
params?: T[keyof T];
webview?: {
uri: string;
title?: string;
};
};

export interface HomeStackParamList extends ParamListBase {
Expand Down
122 changes: 122 additions & 0 deletions src/webview/WebViewScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import {RootStackParamList} from '@/types/StackNavigationType';
import {useFocusEffect, useNavigation} from '@react-navigation/native';
import {StackNavigationProp} from '@react-navigation/stack';
import React, {useCallback, useRef, useState} from 'react';
import {BackHandler, Linking, Platform} from 'react-native';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import {WebView, WebViewMessageEvent} from 'react-native-webview';
import {sendToWeb} from './utils';

import pkg from '../../package.json';

const injectedBefore = `
(function() {
// RN에서 보낸 데이터를 Web이 받을 함수
// -> 커스텀 이벤트로 앱 전역에 전달
window.__fromRN = function(data) {
try {
window.dispatchEvent(new CustomEvent('APP_MESSAGE', { detail: data }));
} catch (e) {
console.error('APP_MESSAGE error', e);
}
};
// 리스너가 없을 때 도착하는 메시지 대비 큐도 가능(선택사항)
window.__fromRNQueue = [];
const orig = window.__fromRN;
window.__fromRN = function(data) {
if (window.__hasAppMessageListener) return orig(data);
window.__fromRNQueue.push(data);
};
window.addEventListener('APP_MESSAGE_LISTENER_READY', function() {
window.__hasAppMessageListener = true;
while (window.__fromRNQueue.length) {
orig(window.__fromRNQueue.shift());
}
});
})();
true;
`;

type Props = {
uri: string;
title?: string;
};

const WebViewScreen = ({uri}: Props) => {
const webRef = useRef<WebView>(null);
const [canGoBack, setCanGoBack] = useState(false);
const {top, left, right, bottom} = useSafeAreaInsets();

const navigation = useNavigation<StackNavigationProp<RootStackParamList>>();

useFocusEffect(
useCallback(() => {
const sub = BackHandler.addEventListener('hardwareBackPress', () => {
if (canGoBack) {
webRef.current?.goBack();
return true;
}
return false;
});
return () => sub.remove();
}, [canGoBack]),
);

const onMessage = (e: WebViewMessageEvent) => {
try {
console.info('Web to RN message', e.nativeEvent.data);
const msg: {type: string; payload: any} = JSON.parse(e.nativeEvent.data);
switch (msg.type) {
case 'NATIVE_NAVIGATION':
navigation.navigate(msg.payload.screen, msg.payload.params);
break;
}
} catch (err) {
console.error(err);
}
};

const sendInit = () => {
sendToWeb(webRef, {
type: 'INIT',
payload: {
platform: Platform.OS,
version: pkg.version,
ts: Date.now(),
},
});
sendToWeb(webRef, {
type: 'SAFE_AREA_INSETS',
payload: {
top,
left,
right,
bottom,
},
});
};

return (
<WebView
ref={webRef}
source={{uri}}
onNavigationStateChange={s => setCanGoBack(s.canGoBack)}
javaScriptEnabled
domStorageEnabled
setSupportMultipleWindows={false}
onMessage={onMessage}
// 필요 시 스킴 필터/외부 링크 분리
onShouldStartLoadWithRequest={req => {
if (!/^https?:/.test(req.url)) {
Linking.openURL(req.url);
return false;
}
return true;
}}
injectedJavaScriptBeforeContentLoaded={injectedBefore}
onLoadEnd={sendInit}
/>
);
};

export default WebViewScreen;
11 changes: 11 additions & 0 deletions src/webview/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import WebView from 'react-native-webview';

export const sendToWeb = (
webRef: React.RefObject<WebView<{}>>,
payload: {type: string; payload: any},
) => {
webRef.current?.injectJavaScript(`
window.__fromRN && window.__fromRN(${JSON.stringify(payload)});
true;
`);
};
Loading