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

Feature: 안드로이드 뒤로가기 버튼 개선, Footer 개선, splash screen 개선 (#679) #680

Merged
merged 1 commit into from
Apr 5, 2024
Merged
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
2 changes: 0 additions & 2 deletions packages/app/src/components/MainNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { TransitionPresets, createStackNavigator } from '@react-navigation/stack
import React from 'react';

import OnBoarding from './OnBoarding';
import TabBarNavigator from './TabBarNavigator';
import WebViewContainer from './WebViewContainer';

import { useDidMount } from '@/hooks/useDidMount';
Expand All @@ -29,7 +28,6 @@ export default function MainNavigator() {
}}
>
<Stack.Screen name="OnBoarding" component={OnBoarding} />
<Stack.Screen name="BottomTab" component={TabBarNavigator} />
<Stack.Screen name="WebViewContainer" component={WebViewContainer} />
</Stack.Navigator>
</NavigationContainer>
Expand Down
22 changes: 17 additions & 5 deletions packages/app/src/components/OnBoarding.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useNavigation } from '@react-navigation/native';
import { StackNavigationProp } from '@react-navigation/stack';
import { useEffect, useRef, useState } from 'react';
import { Animated, Dimensions, Image, Text, TouchableOpacity, View } from 'react-native';
import { ExpandingDot } from 'react-native-animated-pagination-dots';
Expand Down Expand Up @@ -27,28 +28,36 @@ const imageDataList = [
];

export default function OnBoarding() {
const navigation = useNavigation();
const navigation = useNavigation<StackNavigationProp<any>>();
const [isPreloadingDone, setIsPreloadingDone] = useState(false);
const scrollX = useRef(new Animated.Value(0)).current;
const [pageIndex, setpageIndex] = useState(0);
const [lang, setlang] = useState(null);
const [lang, setlang] = useState<string | null>(null);

const preloading = async () => {
const isUserOnBoardSeen = await AsyncStorage.getItem('onBoarding');
if (!isUserOnBoardSeen) return;

navigation.replace('WebViewContainer', {
url: `${SOURCE_URL}`,
});

setIsPreloadingDone(true);
};

useEffect(() => {
const locales = RNLocalize.getLocales();
const languageCode = locales && locales.length > 0 ? locales[0].languageCode : 'en';
setlang(languageCode);
preloading();
setTimeout(() => {
SplashScreen.hide();
}, 500);
}, []);

useEffect(() => {
if (isPreloadingDone) {
SplashScreen.hide();
}
}, [isPreloadingDone]);

return (
<View
style={{
Expand Down Expand Up @@ -84,6 +93,7 @@ export default function OnBoarding() {
<View style={{ alignItems: 'center' }}>
{lang === 'ko' ? <Bubble1SVG /> : <Bubble1enSVG />}
<Image
alt="onboard-image1"
style={{
width: deviceWidth * 0.8,
height: deviceWidth * 0.8,
Expand All @@ -98,6 +108,7 @@ export default function OnBoarding() {
<View style={{ alignItems: 'center' }}>
{lang === 'ko' ? <Bubble2SVG /> : <Bubble2enSVG />}
<Image
alt="onboard-image2"
style={{
width: deviceWidth * 0.8,
height: deviceWidth * 0.8,
Expand All @@ -111,6 +122,7 @@ export default function OnBoarding() {
</View>
) : (
<Image
alt="onboard-image3"
style={{
width: deviceWidth * 0.7,
height: deviceWidth * 1.6,
Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/components/TabBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const isKeyScreenIconName = (value: string) => value in ScreenIconName;
const getScreenIcon = (value: string) =>
isKeyScreenIconName(value) ? ScreenIconName[value as ScreenIconNameKey] : undefined;

function TabBar({ state, navigation, descriptors }: BottomTabBarProps) {
function TabBar({ state, navigation }: BottomTabBarProps) {
const onPress = useTabBarPress();
const renderTabBarButton = useCallback(
(name: string, index: number) => (
Expand Down
81 changes: 40 additions & 41 deletions packages/app/src/components/WebViewContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,55 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import messaging from '@react-native-firebase/messaging';
import { StackActions, useNavigation, useRoute } from '@react-navigation/native';
import { RouteProp, StackActions, useNavigation, useRoute } from '@react-navigation/native';
import React, { useEffect, useRef, useState } from 'react';
import { Alert, BackHandler, Dimensions, Linking, Platform } from 'react-native';
import RNRestart from 'react-native-restart'; // Import package from node modules
import RNRestart from 'react-native-restart';
import { SafeAreaView } from 'react-native-safe-area-context';
import WebView from 'react-native-webview';
import WebView, { WebViewMessageEvent, WebViewNavigation } from 'react-native-webview';

import Error from './Error';
import { SOURCE_URL } from '../config';
import { sendFCMTokenToWebView } from '../utils/sendFCMTokenToWebView';

import { useDidMount } from '@/hooks/useDidMount';
import useWebViewNavigationSetUp from '@/hooks/useWebViewNavigationSetUp';
import { getPermission } from '@/utils/getPermission';
import sendMessageToWebview from '@/utils/sendMessageToWebview';

const windowWidth = Dimensions.get('window').width;
const windowHeight = Dimensions.get('window').height;

type WebViewParams = {
url?: string;
edges?: any[];
title?: string;
};

export default function WebViewContainer() {
useWebViewNavigationSetUp();
const navigation = useNavigation();
const params = useRoute().params;
const webViewRef = useRef(null);
const params = useRoute<RouteProp<{ params: WebViewParams }, 'params'>>().params;
const webViewRef = useRef<WebView>(null);
const { isError, setIsError, onWebViewError } = useAppError();
const [canGoBackInWebView, setCanGoBackInWebView] = useState(false);
const url = params.url ?? SOURCE_URL;
const edges = params?.edges;

useDidMount(async () => {
/* 권한 요청 */
await sendFCMTokenToWebView(webViewRef);
await messaging().requestPermission();
});

/* 안드로이드 뒤로가기 */
useDidMount(() => {
// Android 뒤로가기 버튼 이벤트 처리
useEffect(() => {
if (Platform.OS !== 'android') return;

const handleAndroidBackPress = () => {
if (navigation.canGoBack()) navigation.goBack();
else {
console.log('back pushed ' + canGoBackInWebView);

if (canGoBackInWebView && webViewRef.current) {
webViewRef.current.goBack();
return true;
} else if (navigation.canGoBack()) {
navigation.goBack();
return true;
} else {
Alert.alert(
'Hold on!(잠시만요!)',
'Are you sure you want to quit the program?(앱을 종료하시겠습니까?)',
Expand All @@ -50,41 +61,35 @@ export default function WebViewContainer() {
{ text: 'Confirm(확인)', onPress: () => BackHandler.exitApp() },
]
);
}

if (webViewRef.current) {
webViewRef.current.goBack();
return true;
}
return false;
};

if (Platform.OS === 'android') {
BackHandler.addEventListener('hardwareBackPress', handleAndroidBackPress);
return () => {
BackHandler.removeEventListener('hardwareBackPress', handleAndroidBackPress);
};
}
});
BackHandler.addEventListener('hardwareBackPress', handleAndroidBackPress);
return () => {
BackHandler.removeEventListener('hardwareBackPress', handleAndroidBackPress);
};

/* (iOS)외부 페이지 이동 */
const onNavigationStateChange = (navState) => {
if (!navState.url.includes(SOURCE_URL)) {
Linking.openURL(navState.url).catch((err) => {});
return false;
}
/**
* addEventListener로 등록된 handleAndroidBackPress가 생성시의 클로저 환경을 유지하기 때문에
* canGoBackInWebView의 상태를 추적하기 위해 의존성 배열에 canGoBackInWebView와 navigation추가
*/
}, [canGoBackInWebView, navigation]);

const onNavigationStateChange = (navState: WebViewNavigation) => {
setCanGoBackInWebView(navState.canGoBack);
};

const onShouldStartLoadWithRequest = (navState) => {
if (!navState.url.includes(SOURCE_URL)) {
const onShouldStartLoadWithRequest = (navState: WebViewNavigation) => {
if (SOURCE_URL && !navState.url.includes(SOURCE_URL)) {
Linking.openURL(navState.url).catch((err) => {});
return false;
}
return true;
};

/* 페이지 이동 */
const requestOnMessage = async (event) => {
const requestOnMessage = async (event: WebViewMessageEvent) => {
const nativeEvent = JSON.parse(event.nativeEvent.data);
const { type, data } = nativeEvent;
switch (type) {
Expand All @@ -96,11 +101,6 @@ export default function WebViewContainer() {
url: `${SOURCE_URL}${path}`,
title,
edges: title ? ['bottom'] : [],
// right: () => { // 어디서 쓰는지 잘 모르겠는데 WARNING 떠서 주석처리
// webViewRef.current?.postMessage(
// JSON.stringify({type: 'NAVIGATION_TAPPED_RIGHT_BUTTON'}),
// );
// },
});
navigation.dispatch(pushAction);
break;
Expand Down Expand Up @@ -164,7 +164,6 @@ export default function WebViewContainer() {
pullToRefreshEnabled
thirdPartyCookiesEnabled={true}
sharedCookiesEnabled={true}
androidHardwareAccelerationDisabled
onNavigationStateChange={onNavigationStateChange}
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
onMessage={requestOnMessage} // 웹뷰 -> 앱으로 통신
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading