From f6e849515b0fc6634ae378a3aa44ba5ee97b60db Mon Sep 17 00:00:00 2001 From: seongwon seo Date: Tue, 2 Jun 2026 13:37:06 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20=EB=84=A4=EC=9D=B4=ED=8B=B0?= =?UTF-8?q?=EB=B8=8C=20=EB=B0=94=ED=85=80=ED=83=AD=20=EC=A0=9C=EA=B1=B0=20?= =?UTF-8?q?=E2=86=92=20=EB=8B=A8=EC=9D=BC=20=EC=9B=B9=EB=B7=B0=20=EC=85=B8?= =?UTF-8?q?=20+=20=EC=95=B1=20=EB=B2=84=EC=A0=84=20=EB=B8=8C=EB=A6=BF?= =?UTF-8?q?=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - (tabs) 탭 레이아웃 및 구독/메뉴 네이티브 화면 제거, app/index.tsx로 홈 웹뷰 단일 호스트 - _layout Stack/anchor를 index로, 상세/웹뷰 back 폴백을 /로 변경 - home-webview-screen에 REQUEST_APP_VERSION→APP_VERSION 핸들러 추가 - ui/subscribe 제거(웹이 대체) --- app/(tabs)/_layout.tsx | 112 ---------- app/(tabs)/explore.tsx | 3 - app/(tabs)/index.tsx.backup | 3 - app/(tabs)/more.tsx | 165 -------------- app/_layout.tsx | 4 +- app/{(tabs) => }/index.tsx | 2 +- app/webview/[slug].tsx | 2 +- ui/club-detail/club-detail-screen.tsx | 2 +- ui/home/home-webview-screen.tsx | 8 + ui/subscribe/components/empty-state.tsx | 90 -------- ui/subscribe/components/index.ts | 7 - .../components/subscribed-club-list.tsx | 97 --------- ui/subscribe/hook/index.ts | 6 - ui/subscribe/hook/use-subscribe-screen.ts | 80 ------- ui/subscribe/index.ts | 7 - ui/subscribe/subscribe-screen.tsx | 206 ------------------ 16 files changed, 13 insertions(+), 781 deletions(-) delete mode 100644 app/(tabs)/_layout.tsx delete mode 100644 app/(tabs)/explore.tsx delete mode 100644 app/(tabs)/index.tsx.backup delete mode 100644 app/(tabs)/more.tsx rename app/{(tabs) => }/index.tsx (90%) delete mode 100644 ui/subscribe/components/empty-state.tsx delete mode 100644 ui/subscribe/components/index.ts delete mode 100644 ui/subscribe/components/subscribed-club-list.tsx delete mode 100644 ui/subscribe/hook/index.ts delete mode 100644 ui/subscribe/hook/use-subscribe-screen.ts delete mode 100644 ui/subscribe/index.ts delete mode 100644 ui/subscribe/subscribe-screen.tsx diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx deleted file mode 100644 index 4f3b026..0000000 --- a/app/(tabs)/_layout.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import MenuIcon from "@/assets/icons/ic-menu.svg"; -import HomeIcon from "@/assets/icons/ic_home.svg"; -import { HapticTab } from "@/components/haptic-tab"; -import { USER_EVENT } from "@/constants/eventname"; -import { Colors } from "@/constants/theme"; -import { useMixpanelTrack } from "@/hooks"; -import { useColorScheme } from "@/hooks/use-color-scheme"; -import { Tabs } from "expo-router"; -import React from "react"; -import { Image } from "react-native"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; - -export default function TabLayout() { - const colorScheme = useColorScheme(); - const trackEvent = useMixpanelTrack(); - const insets = useSafeAreaInsets(); - const TAB_BAR_BASE_HEIGHT = 56; - const TAB_BAR_BASE_PADDING_VERTICAL = 6; - - const handleTabPress = (tabName: string) => { - trackEvent(USER_EVENT.BOTTOM_TAB_CLICKED, { - tab: tabName, - url: `app://moadong/(tabs)/${tabName}`, - }); - }; - - return ( - - ( - - ), - }} - listeners={{ - tabPress: () => handleTabPress("home"), - }} - /> - ( - - ), - }} - listeners={{ - tabPress: () => handleTabPress("explore"), - }} - /> - ( - - ), - }} - listeners={{ - tabPress: () => handleTabPress("more"), - }} - /> - - ); -} diff --git a/app/(tabs)/explore.tsx b/app/(tabs)/explore.tsx deleted file mode 100644 index 9417ca4..0000000 --- a/app/(tabs)/explore.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import SubscribeScreen from '@/ui/subscribe'; - -export default SubscribeScreen; diff --git a/app/(tabs)/index.tsx.backup b/app/(tabs)/index.tsx.backup deleted file mode 100644 index d4269c3..0000000 --- a/app/(tabs)/index.tsx.backup +++ /dev/null @@ -1,3 +0,0 @@ -import HomeScreen from '@/ui/home'; - -export default HomeScreen; diff --git a/app/(tabs)/more.tsx b/app/(tabs)/more.tsx deleted file mode 100644 index 42954c4..0000000 --- a/app/(tabs)/more.tsx +++ /dev/null @@ -1,165 +0,0 @@ -import MoadongIcon from '@/assets/icons/ic-moadong.svg'; -import { MoaText } from '@/components/moa-text'; -import { PAGE_VIEW_EVENT, USER_EVENT } from '@/constants/eventname'; -import { useMixpanelTrack, useTrackScreenView } from '@/hooks'; -import { Ionicons } from '@expo/vector-icons'; -import Constants from 'expo-constants'; -import { useRouter } from 'expo-router'; -import React from 'react'; -import { TouchableOpacity, View } from 'react-native'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import styled from 'styled-components/native'; - -interface MenuItem { - id: string; - title: string; - icon: keyof typeof Ionicons.glyphMap; - route: string; -} - -const menuItems: MenuItem[] = [ - { - id: 'introduce', - title: '서비스 소개', - icon: 'information-circle-outline', - route: '/webview/introduce', - }, - { - id: 'club-union', - title: '총 동아리 연합회', - icon: 'people-outline', - route: '/webview/club-union', - }, - { - id: 'privacy-policy', - title: '개인정보 처리방침', - icon: 'document-text-outline', - route: '/webview/privacy-policy', - }, -]; - -export default function MoreScreen() { - const insets = useSafeAreaInsets(); - const router = useRouter(); - const trackEvent = useMixpanelTrack(); - - useTrackScreenView(PAGE_VIEW_EVENT.MORE_PAGE); - - const appVersion = - Constants.expoConfig?.version ?? - Constants.nativeAppVersion ?? - Constants.manifest?.version ?? - '알 수 없음'; - - const handleMenuPress = (item: MenuItem) => { - trackEvent(USER_EVENT.MORE_MENU_CLICKED, { - menu: item.title, - url: `app://moadong${item.route}`, - }); - - router.push(item.route as any); - }; - - return ( - -
- 더보기 -
- - - {menuItems.map((item) => ( - handleMenuPress(item)} - activeOpacity={0.7} - > - - - - - {item.title} - - - - ))} - - - - - - 앱 버전 - - {appVersion} - - -
- ); -} - -// Styled Components -const Container = styled(View)` - flex: 1; - background-color: #fff; -`; - -const Header = styled.View` - padding-horizontal: 16px; - padding-vertical: 16px; - border-bottom-width: 1px; - border-bottom-color: #F0F0F0; -`; - -const HeaderTitle = styled(MoaText)` - color: #111111; -`; - -const MenuList = styled.View` - padding-top: 8px; - flex: 1; -`; - -const MenuItem = styled(TouchableOpacity)` - flex-direction: row; - align-items: center; - justify-content: space-between; - padding-horizontal: 16px; - padding-vertical: 16px; - border-bottom-width: 1px; - border-bottom-color: #F5F5F5; -`; - -const MenuItemContent = styled.View` - flex-direction: row; - align-items: center; - flex: 1; -`; - -const IconContainer = styled.View` - width: 40px; - height: 40px; - border-radius: 20px; - background-color: #FFECE5; - justify-content: center; - align-items: center; - margin-right: 12px; -`; - -const MenuItemText = styled(MoaText)` - color: #111111; - font-size: 16px; -`; - -const VersionItem = styled.View` - flex-direction: row; - align-items: center; - justify-content: space-between; - padding-horizontal: 16px; - padding-vertical: 16px; - border-bottom-width: 1px; - border-bottom-color: #ffffff; -`; - -const VersionValue = styled(MoaText)` - color: #888888; -`; - diff --git a/app/_layout.tsx b/app/_layout.tsx index 978d29f..21836cf 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -25,7 +25,7 @@ SplashScreen.preventAutoHideAsync().catch(() => { }); export const unstable_settings = { - anchor: '(tabs)', + anchor: 'index', }; type BootstrapStatus = 'idle' | 'running' | 'success' | 'failed'; @@ -144,7 +144,7 @@ export default function RootLayout() { - + diff --git a/app/(tabs)/index.tsx b/app/index.tsx similarity index 90% rename from app/(tabs)/index.tsx rename to app/index.tsx index a32e779..a0e6847 100644 --- a/app/(tabs)/index.tsx +++ b/app/index.tsx @@ -2,7 +2,7 @@ import { HomeScreen } from '@/ui/home/home-screen'; import { HomeWebViewScreen } from '@/ui/home/home-webview-screen'; import React, { useState } from 'react'; -export default function HomeTab() { +export default function Home() { const [webViewFailed, setWebViewFailed] = useState(false); if (webViewFailed) { diff --git a/app/webview/[slug].tsx b/app/webview/[slug].tsx index cd406ae..b683662 100644 --- a/app/webview/[slug].tsx +++ b/app/webview/[slug].tsx @@ -83,7 +83,7 @@ export default function WebViewScreen() { if (router.canGoBack()) { router.back(); } else { - router.push("/(tabs)/more"); + router.push("/"); } }; diff --git a/ui/club-detail/club-detail-screen.tsx b/ui/club-detail/club-detail-screen.tsx index df92b89..a1b45cb 100644 --- a/ui/club-detail/club-detail-screen.tsx +++ b/ui/club-detail/club-detail-screen.tsx @@ -71,7 +71,7 @@ export default function ClubWebViewScreen() { if (router.canGoBack()) { router.back(); } else { - router.push("/(tabs)"); + router.push("/"); } }; diff --git a/ui/home/home-webview-screen.tsx b/ui/home/home-webview-screen.tsx index c6da484..5b51dd3 100644 --- a/ui/home/home-webview-screen.tsx +++ b/ui/home/home-webview-screen.tsx @@ -1,6 +1,7 @@ import { useMixpanelContext } from '@/contexts'; import { useSubscribedClubsContext } from '@/contexts/subscribed-clubs-context'; import { appendSessionId, getWebViewUserAgent } from '@/utils/webview'; +import Constants from 'expo-constants'; import { useRouter } from 'expo-router'; import * as WebBrowser from 'expo-web-browser'; import React, { useCallback, useEffect, useRef, useState } from 'react'; @@ -81,6 +82,13 @@ export function HomeWebViewScreen({ onError }: HomeWebViewScreenProps) { case 'OPEN_EXTERNAL_URL': await WebBrowser.openBrowserAsync(payload.url); break; + + case 'REQUEST_APP_VERSION': + sendMessage({ + type: 'APP_VERSION', + payload: { version: Constants.expoConfig?.version ?? 'unknown' }, + }); + break; } } catch { // 파싱 실패 무시 diff --git a/ui/subscribe/components/empty-state.tsx b/ui/subscribe/components/empty-state.tsx deleted file mode 100644 index 74d73ca..0000000 --- a/ui/subscribe/components/empty-state.tsx +++ /dev/null @@ -1,90 +0,0 @@ -/** - * 구독 빈 상태 컴포넌트 - */ - -import { MoaImage } from '@/components/moa-image'; -import { MoaText } from '@/components/moa-text'; -import { USER_EVENT } from '@/constants/eventname'; -import { MainColors } from '@/constants/theme'; -import { useMixpanelTrack } from '@/hooks'; -import { useRouter } from 'expo-router'; -import React from 'react'; -import { TouchableOpacity } from 'react-native'; -import styled from 'styled-components/native'; - -/** - * 구독한 동아리가 없을 때 표시되는 컴포넌트 - */ -export function EmptyState() { - const router = useRouter(); - const trackEvent = useMixpanelTrack(); - - const handleGoHome = () => { - trackEvent(USER_EVENT.GO_HOME_BUTTON_CLICKED, { - from: 'subscribe_empty', - url: 'app://moadong/(tabs)/home', - }); - - router.push('/(tabs)'); - }; - - return ( - - - - - - 구독한 동아리가 없어요 - - - 관심있는 동아리를 구독하고{'\n'}새로운 모집 및 활동 소식을 받아보세요 - - - - 홈으로 가기 - - - ); -} - -// Styled Components -const Container = styled.View` - flex: 1; - justify-content: center; - align-items: center; - padding: 40px; - background-color: #fff; -`; - -const IconContainer = styled.View` - margin-bottom: 24px; -`; - -const Title = styled(MoaText)` - color: #111111; - margin-bottom: 12px; - text-align: center; -`; - -const Description = styled(MoaText)` - color: #989898; - text-align: center; - margin-bottom: 32px; - line-height: 24px; -`; - -const HomeButton = styled(TouchableOpacity)` - background-color: ${MainColors.main}; - padding-horizontal: 32px; - padding-vertical: 14px; - border-radius: 12px; -`; - -const ButtonText = styled(MoaText)` - color: #FFFFFF; -`; - diff --git a/ui/subscribe/components/index.ts b/ui/subscribe/components/index.ts deleted file mode 100644 index 0984669..0000000 --- a/ui/subscribe/components/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * 구독 화면 컴포넌트 Export - */ - -export * from './empty-state'; -export * from './subscribed-club-list'; - diff --git a/ui/subscribe/components/subscribed-club-list.tsx b/ui/subscribe/components/subscribed-club-list.tsx deleted file mode 100644 index a2eb835..0000000 --- a/ui/subscribe/components/subscribed-club-list.tsx +++ /dev/null @@ -1,97 +0,0 @@ -/** - * 구독한 동아리 목록 컴포넌트 - */ - -import { Club } from '@/types/club.types'; -import { ClubCard } from '@/ui/home/components/club-card'; -import React, { RefObject } from 'react'; -import { ActivityIndicator, FlatList, RefreshControl } from 'react-native'; -import styled from 'styled-components/native'; - -/** - * 구독한 동아리 목록 Props - */ -interface SubscribedClubListProps { - clubs: Club[]; - loading: boolean; - onRefresh: () => void; - onClubPress: (club: Club) => void; - isSubscribed: (clubId: string) => boolean; - onSubscribeToggle: (club: Club) => Promise; - listRef?: RefObject>; -} - -/** - * 구독한 동아리 목록을 표시하는 컴포넌트 - */ -export function SubscribedClubList({ - clubs, - loading, - onRefresh, - onClubPress, - isSubscribed, - onSubscribeToggle, - listRef, -}: SubscribedClubListProps) { - const renderItem = ({ item }: { item: Club }) => ( - onSubscribeToggle(item)} - /> - ); - - const keyExtractor = (item: Club) => item.id; - - const renderListHeader = () => ( - - 총 {clubs.length}개의 동아리를 구독 중입니다 - - ); - - return ( - - } - showsVerticalScrollIndicator={false} - ListFooterComponent={ - loading ? ( - - - - ) : null - } - /> - ); -} - -// Styled Components -const HeaderContainer = styled.View` - padding-horizontal: 16px; - padding-vertical: 16px; -`; - -const CountText = styled.Text` - font-size: 14px; - color: #666666; - font-weight: 500; -`; - -const LoadingContainer = styled.View` - padding: 20px; - align-items: center; -`; - diff --git a/ui/subscribe/hook/index.ts b/ui/subscribe/hook/index.ts deleted file mode 100644 index 2e2fcb7..0000000 --- a/ui/subscribe/hook/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * 구독 화면 훅 Export - */ - -export * from './use-subscribe-screen'; - diff --git a/ui/subscribe/hook/use-subscribe-screen.ts b/ui/subscribe/hook/use-subscribe-screen.ts deleted file mode 100644 index 9c9cd45..0000000 --- a/ui/subscribe/hook/use-subscribe-screen.ts +++ /dev/null @@ -1,80 +0,0 @@ -/** - * 구독 화면 로직 커스텀 훅 - */ - -import { Club } from '@/types/club.types'; -import { useClubs } from '@/ui/home/hook/use-clubs'; -import { useSubscribedClubs } from '@/ui/home/hook/use-subscribed-clubs'; -import { useCallback, useMemo } from 'react'; - -/** - * 구독 화면 훅 반환 값 - */ -interface UseSubscribeScreenReturn { - subscribedClubs: Club[]; - loading: boolean; - error: string | null; - refetch: () => void; - isSubscribed: (clubId: string) => boolean; - toggleSubscribe: (clubId: string) => Promise<{ needsPermission: boolean }>; -} - -/** - * 구독 화면 데이터를 관리하는 훅 - * - * @example - * ```typescript - * const { - * subscribedClubs, - * loading, - * error, - * refetch, - * isSubscribed, - * toggleSubscribe, - * } = useSubscribeScreen(); - * ``` - */ -export function useSubscribeScreen(): UseSubscribeScreenReturn { - // 구독 동아리 ID 목록 가져오기 - const { - subscribedClubIds, - isSubscribed, - toggleSubscribe, - } = useSubscribedClubs(); - - // 모든 동아리 데이터 가져오기 - const { - clubs, - loading, - error, - refetch, - } = useClubs({ - initialCategory: undefined, // 전체 카테고리 - initialType: 'central', // 중앙동아리 - autoFetch: true, - }); - - /** - * 구독한 동아리만 필터링 - */ - const subscribedClubs = useMemo(() => { - return clubs.filter(club => subscribedClubIds.includes(club.id)); - }, [clubs, subscribedClubIds]); - - /** - * 새로고침 핸들러 - */ - const handleRefetch = useCallback(() => { - refetch(); - }, [refetch]); - - return { - subscribedClubs, - loading, - error, - refetch: handleRefetch, - isSubscribed, - toggleSubscribe, - }; -} - diff --git a/ui/subscribe/index.ts b/ui/subscribe/index.ts deleted file mode 100644 index 6a55f08..0000000 --- a/ui/subscribe/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * 구독 화면 Export - */ - -export * from './subscribe-screen'; -export { default } from './subscribe-screen'; - diff --git a/ui/subscribe/subscribe-screen.tsx b/ui/subscribe/subscribe-screen.tsx deleted file mode 100644 index 7cb9e07..0000000 --- a/ui/subscribe/subscribe-screen.tsx +++ /dev/null @@ -1,206 +0,0 @@ -/** - * 구독 화면 컴포넌트 - */ - -import { MoaText } from '@/components/moa-text'; -import { PermissionDialog } from '@/components/permission-dialog'; -import { PAGE_VIEW_EVENT, USER_EVENT } from '@/constants/eventname'; -import { useMixpanelTrack, useTrackScreenView } from '@/hooks'; -import { Club } from '@/types/club.types'; -import { useRouter } from 'expo-router'; -import React, { RefObject, useCallback, useRef, useState } from 'react'; -import { ActivityIndicator, FlatList, TouchableOpacity, View } from 'react-native'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import styled from 'styled-components/native'; -import { EmptyState, SubscribedClubList } from './components'; -import { useSubscribeScreen } from './hook'; - -/** - * 구독 화면 메인 컴포넌트 - */ -export function SubscribeScreen() { - const insets = useSafeAreaInsets(); - const router = useRouter(); - const listRef = useRef | null>(null); - const [showPermissionDialog, setShowPermissionDialog] = useState(false); - - useTrackScreenView(PAGE_VIEW_EVENT.SUBSCRIBE_PAGE); - const trackEvent = useMixpanelTrack(); - - // 구독 화면 데이터 및 로직 - const { - subscribedClubs, - loading, - error, - refetch, - isSubscribed, - toggleSubscribe, - } = useSubscribeScreen(); - - /** - * 동아리 카드 클릭 핸들러 - */ - const handleClubPress = useCallback((club: Club) => { - if (!club?.id) { - return; - } - - trackEvent(USER_EVENT.CLUB_CARD_CLICKED, { - clubName: club.name, - category: club.category, - from: 'subscribe', - url: 'app://moadong/(tabs)/subscribe', - }); - - router.push({ - pathname: '/club/[id]', - params: { id: club.id, name: club.name } - }); - }, [router, trackEvent]); - - /** - * 구독 토글 핸들러 - */ - const handleSubscribeToggle = useCallback(async (club: Club) => { - const wasSubscribed = isSubscribed(club.id); - - trackEvent(USER_EVENT.SUBSCRIBE_BUTTON_CLICKED, { - clubName: club.name, - subscribed: !wasSubscribed, - from: 'subscribe', - url: 'app://moadong/(tabs)/subscribe', - }); - - const result = await toggleSubscribe(club.id); - if (result.needsPermission) { - setShowPermissionDialog(true); - } - }, [toggleSubscribe, isSubscribed, trackEvent]); - - /** - * 로딩 중 표시 - */ - if (loading && subscribedClubs.length === 0) { - return ( - -
- 구독 -
- - - -
- ); - } - - /** - * 에러 발생 시 표시 - */ - if (error && subscribedClubs.length === 0) { - return ( - -
- 구독 -
- - 구독한 동아리 목록을 불러오지 못했어요. - - 재시도 - - -
- ); - } - - /** - * 구독한 동아리가 없을 때 - */ - if (subscribedClubs.length === 0) { - return ( - -
- 구독 -
- -
- ); - } - - /** - * 구독한 동아리 목록 표시 - */ - return ( - -
- 구독 -
- >} - /> - - {/* 알림 권한 다이얼로그 */} - setShowPermissionDialog(false)} - /> -
- ); -} - -// Styled Components -const Container = styled(View)` - flex: 1; - background-color: #fff; -`; - -const Header = styled.View` - padding-horizontal: 16px; - padding-vertical: 16px; - border-bottom-width: 1px; - border-bottom-color: #F0F0F0; -`; - -const HeaderTitle = styled(MoaText)` - color: #111111; -`; - -const LoadingContainer = styled.View` - flex: 1; - justify-content: center; - align-items: center; -`; - -const ErrorContainer = styled.View` - flex: 1; - justify-content: center; - align-items: center; - padding: 24px; -`; - -const ErrorTitle = styled(MoaText)` - color: #3A3A3A; - text-align: center; - margin-bottom: 16px; - font-size: 18px; -`; - -const RetryButton = styled(TouchableOpacity)` - background-color: #FF5414; - padding-horizontal: 28px; - padding-vertical: 14px; - border-radius: 8px; -`; - -const RetryButtonText = styled(MoaText)` - color: #fff; - font-size: 16px; -`; - -export default SubscribeScreen; - From b17fdb5c40323fbd464f43219a1b138cb557f4ef Mon Sep 17 00:00:00 2001 From: seongwon seo Date: Sun, 7 Jun 2026 14:58:34 +0900 Subject: [PATCH 2/6] =?UTF-8?q?fix:=20=EB=8B=A8=EC=9D=BC=20=EC=9B=B9?= =?UTF-8?q?=EB=B7=B0=20=EC=85=B8=20=EC=83=81=EC=84=B8=20=EB=92=A4=EB=A1=9C?= =?UTF-8?q?=EA=B0=80=EA=B8=B0=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - NAVIGATE_BACK 메시지 수신 시 webView.goBack() (상세 '<' 버튼) - Android 하드웨어 백: 웹뷰 히스토리 있으면 goBack, 없으면 종료 - iOS 엣지 스와이프 백 제스처 활성화 (allowsBackForwardNavigationGestures) --- ui/home/home-webview-screen.tsx | 37 +++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/ui/home/home-webview-screen.tsx b/ui/home/home-webview-screen.tsx index 5b51dd3..213a06c 100644 --- a/ui/home/home-webview-screen.tsx +++ b/ui/home/home-webview-screen.tsx @@ -5,9 +5,13 @@ import Constants from 'expo-constants'; import { useRouter } from 'expo-router'; import * as WebBrowser from 'expo-web-browser'; import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { ActivityIndicator, View } from 'react-native'; +import { ActivityIndicator, BackHandler, View } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { WebView, WebViewMessageEvent } from 'react-native-webview'; +import { + WebView, + WebViewMessageEvent, + WebViewNavigation, +} from 'react-native-webview'; import styled from 'styled-components/native'; const BASE_URL = `${(process.env.EXPO_PUBLIC_WEBVIEW_URL || 'https://moadong.com').replace(/\/$/, '')}/webview/main`; @@ -21,6 +25,7 @@ export function HomeWebViewScreen({ onError }: HomeWebViewScreenProps) { const insets = useSafeAreaInsets(); const router = useRouter(); const webViewRef = useRef(null); + const canGoBackRef = useRef(false); const [loaded, setLoaded] = useState(false); const { sessionId, isLoading: sessionLoading } = useMixpanelContext(); @@ -67,6 +72,10 @@ export function HomeWebViewScreen({ onError }: HomeWebViewScreenProps) { break; } + case 'NAVIGATE_BACK': + webViewRef.current?.goBack(); + break; + case 'NAVIGATE_WEBVIEW': if (payload.slug?.startsWith('club/')) { const clubId = payload.slug.slice('club/'.length); @@ -101,6 +110,28 @@ export function HomeWebViewScreen({ onError }: HomeWebViewScreenProps) { setLoaded(true); }, []); + const handleNavigationStateChange = useCallback( + (navState: WebViewNavigation) => { + canGoBackRef.current = navState.canGoBack; + }, + [], + ); + + // Android 하드웨어 뒤로가기: 웹뷰 히스토리가 있으면 웹뷰 back, 없으면 기본 동작(종료) + useEffect(() => { + const subscription = BackHandler.addEventListener( + 'hardwareBackPress', + () => { + if (canGoBackRef.current) { + webViewRef.current?.goBack(); + return true; + } + return false; + }, + ); + return () => subscription.remove(); + }, []); + return ( {!loaded && ( @@ -116,11 +147,13 @@ export function HomeWebViewScreen({ onError }: HomeWebViewScreenProps) { userAgent={USER_AGENT} onMessage={handleMessage} onLoadEnd={handleLoadEnd} + onNavigationStateChange={handleNavigationStateChange} onError={onError} onHttpError={onError} javaScriptEnabled domStorageEnabled pullToRefreshEnabled + allowsBackForwardNavigationGestures /> )} From 8d3eefde322c362798b99109694064f56ad5e729 Mon Sep 17 00:00:00 2001 From: seongwon seo Date: Sun, 7 Jun 2026 18:47:28 +0900 Subject: [PATCH 3/6] =?UTF-8?q?fix:=20=ED=99=88=20=EC=9B=B9=EB=B7=B0?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=99=B8=EB=B6=80=20=EB=A7=81=ED=81=AC=20?= =?UTF-8?q?=ED=81=B4=EB=A6=AD=20=EC=8B=9C=20=ED=97=A4=EB=8D=94=20=EC=9E=88?= =?UTF-8?q?=EB=8A=94=20=ED=99=94=EB=A9=B4=EC=9C=BC=EB=A1=9C=20=EC=97=B4?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit onShouldStartLoadWithRequest로 moadong.com 외부 URL 탐색을 가로채 /webview/[slug] 화면으로 push — 개인정보처리방침(노션) 등 외부 링크에 네이티브 헤더와 뒤로가기 버튼이 표시됨 Co-Authored-By: Claude Sonnet 4.6 --- ui/home/home-webview-screen.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ui/home/home-webview-screen.tsx b/ui/home/home-webview-screen.tsx index 095edbe..054ea36 100644 --- a/ui/home/home-webview-screen.tsx +++ b/ui/home/home-webview-screen.tsx @@ -12,6 +12,7 @@ import { WebView, WebViewMessageEvent, WebViewNavigation, + ShouldStartLoadRequest, } from 'react-native-webview'; import styled from 'styled-components/native'; @@ -140,6 +141,18 @@ export function HomeWebViewScreen({ onError }: HomeWebViewScreenProps) { [], ); + const handleShouldStartLoadWithRequest = useCallback( + (request: ShouldStartLoadRequest) => { + const baseOrigin = (process.env.EXPO_PUBLIC_WEBVIEW_URL ?? 'https://moadong.com').replace(/\/$/, ''); + if (request.url.startsWith('http') && !request.url.startsWith(baseOrigin)) { + router.push({ pathname: '/webview/[slug]', params: { slug: 'external', url: request.url } }); + return false; + } + return true; + }, + [router], + ); + // Android 하드웨어 뒤로가기: 웹뷰 히스토리가 있으면 웹뷰 back, 없으면 기본 동작(종료) useEffect(() => { const subscription = BackHandler.addEventListener( @@ -171,6 +184,7 @@ export function HomeWebViewScreen({ onError }: HomeWebViewScreenProps) { onMessage={handleMessage} onLoadEnd={handleLoadEnd} onNavigationStateChange={handleNavigationStateChange} + onShouldStartLoadWithRequest={handleShouldStartLoadWithRequest} onError={handleError} onHttpError={handleError} javaScriptEnabled From 92005c9688863d376c592870f15206c655168222 Mon Sep 17 00:00:00 2001 From: seongwon seo Date: Sun, 7 Jun 2026 22:50:38 +0900 Subject: [PATCH 4/6] =?UTF-8?q?fix:=20=EC=95=B1=20=EC=8B=9C=EC=9E=91=20?= =?UTF-8?q?=EC=8B=9C=20=EC=9B=B9=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=EC=9D=B4=20=EB=A8=BC=EC=A0=80=20=EB=9C=A8=EB=8A=94=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - handleShouldStartLoadWithRequest: iOS는 navigationType === 'click'인 경우(사용자 탭)만 외부 URL로 인터셉트. 초기 로드·서버 리다이렉트('other')는 통과시켜 스플래시 중 잘못된 화면 전환 방지 - handleMessage NAVIGATE_WEBVIEW: loaded 상태 guard 추가, 첫 로드 완료 전 웹 페이지의 navigation 메시지 무시 - _layout.tsx: initialPathnameRef로 초기 경로 캡처, preload 중 pathname 변경에 무관하게 homeWebViewPreloadSettled 조건 유지 Co-Authored-By: Claude Sonnet 4.6 --- app/_layout.tsx | 12 +++++------ package-lock.json | 36 --------------------------------- ui/home/home-webview-screen.tsx | 20 +++++++++++++----- 3 files changed, 21 insertions(+), 47 deletions(-) diff --git a/app/_layout.tsx b/app/_layout.tsx index 8653d3b..c367db6 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -57,15 +57,15 @@ function RootLayoutContent() { const [bootstrapResult, setBootstrapResult] = useState(null); const bootstrapStatusRef = useRef('idle'); const nativeSplashHiddenRef = useRef(false); - const { isSettled: homeWebViewPreloadSettled, status: homeWebViewPreloadStatus } = + const { isSettled: homeWebViewPreloadSettled } = useHomeWebViewPreloadContext(); + const initialPathnameRef = useRef(pathname); const bootstrapSucceeded = bootstrapStatus === 'success'; - const shouldWaitForHomeWebView = pathname === '/'; const shouldBlockSplash = forceUpdateRequired || !bootstrapSucceeded || - (shouldWaitForHomeWebView && !homeWebViewPreloadSettled); + (initialPathnameRef.current === '/' && !homeWebViewPreloadSettled); // 강제 업데이트가 필요한 경우(또는 체크 전)에는 FCM 권한 프롬프트/핸들러 설정이 뜨지 않도록 비활성화 useFcm(forceUpdateChecked && !forceUpdateRequired && bootstrapSucceeded); @@ -204,7 +204,7 @@ function RootLayoutContent() { forceUpdateRequired, bootstrapStatus, pathname, - homeWebViewPreloadStatus, + homeWebViewPreloadSettled, }); return; } @@ -213,7 +213,7 @@ function RootLayoutContent() { forceUpdateRequired, bootstrapStatus, pathname, - homeWebViewPreloadStatus, + homeWebViewPreloadSettled, shouldBlockSplash, ]); @@ -236,7 +236,7 @@ function RootLayoutContent() { bootstrapStatus, forceUpdateRequired, pathname, - homeWebViewPreloadStatus, + homeWebViewPreloadSettled, }); } diff --git a/package-lock.json b/package-lock.json index ac21e6a..168f572 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4887,9 +4887,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4904,9 +4901,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -4921,9 +4915,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4938,9 +4929,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4955,9 +4943,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -4972,9 +4957,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4989,9 +4971,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5006,9 +4985,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -10314,9 +10290,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -10337,9 +10310,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -10360,9 +10330,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -10383,9 +10350,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ diff --git a/ui/home/home-webview-screen.tsx b/ui/home/home-webview-screen.tsx index 054ea36..d4cbbe9 100644 --- a/ui/home/home-webview-screen.tsx +++ b/ui/home/home-webview-screen.tsx @@ -6,14 +6,14 @@ import Constants from 'expo-constants'; import { useRouter } from 'expo-router'; import * as WebBrowser from 'expo-web-browser'; import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { ActivityIndicator, BackHandler, View } from 'react-native'; +import { ActivityIndicator, BackHandler, Platform, View } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { WebView, WebViewMessageEvent, WebViewNavigation, - ShouldStartLoadRequest, } from 'react-native-webview'; +import type { ShouldStartLoadRequest } from 'react-native-webview/lib/WebViewTypes'; import styled from 'styled-components/native'; const BASE_URL = `${(process.env.EXPO_PUBLIC_WEBVIEW_URL || 'https://moadong.com').replace(/\/$/, '')}/webview/main`; @@ -89,6 +89,7 @@ export function HomeWebViewScreen({ onError }: HomeWebViewScreenProps) { break; case 'NAVIGATE_WEBVIEW': + if (!loaded) break; if (payload.slug?.startsWith('club/')) { const clubId = payload.slug.slice('club/'.length); if (!clubId) break; @@ -145,12 +146,21 @@ export function HomeWebViewScreen({ onError }: HomeWebViewScreenProps) { (request: ShouldStartLoadRequest) => { const baseOrigin = (process.env.EXPO_PUBLIC_WEBVIEW_URL ?? 'https://moadong.com').replace(/\/$/, ''); if (request.url.startsWith('http') && !request.url.startsWith(baseOrigin)) { - router.push({ pathname: '/webview/[slug]', params: { slug: 'external', url: request.url } }); - return false; + // iOS: navigationType === 'click' 은 사용자가 직접 링크를 탭한 경우만 해당 + // 초기 로드·서버 리다이렉트는 'other' 이므로 인터셉트하지 않음 + // Android: navigationType이 항상 'other'이므로 loaded 상태로 구분 + const isUserInitiated = Platform.OS === 'ios' + ? request.navigationType === 'click' + : loaded; + if (isUserInitiated) { + router.push({ pathname: '/webview/[slug]', params: { slug: 'external', url: request.url } }); + return false; + } + return true; } return true; }, - [router], + [router, loaded], ); // Android 하드웨어 뒤로가기: 웹뷰 히스토리가 있으면 웹뷰 back, 없으면 기본 동작(종료) From effb31233d9b88586f40ea705bb68fc5c901856a Mon Sep 17 00:00:00 2001 From: seongwon seo Date: Sat, 13 Jun 2026 17:18:48 +0900 Subject: [PATCH 5/6] =?UTF-8?q?feat:=20NAVIGATE=5FWEBVIEW=20payload?= =?UTF-8?q?=EC=97=90=20clubId=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EB=8F=99?= =?UTF-8?q?=EC=95=84=EB=A6=AC=20=EC=83=81=EC=84=B8=20=EA=B5=AC=EB=8F=85=20?= =?UTF-8?q?=EC=B4=88=EA=B8=B0=EA=B0=92=20=EB=B2=84=EA=B7=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - NAVIGATE_WEBVIEW 브릿지 payload에 clubId(ObjectId) 필드 추가 - ClubDetailScreen에서 slug 대신 ObjectId로 isSubscribed 조회하여 is_subscribed 초기값 항상 false 버그 수정 - HomeWebViewScreen SHARE 브릿지 메시지 처리 추가 Co-Authored-By: Claude Sonnet 4.6 (1M context) --- app/webview/[slug].tsx | 9 +++++---- hooks/use-webview-message-handler.ts | 4 ++-- types/webview-message.types.ts | 2 +- ui/club-detail/club-detail-screen.tsx | 12 +++++++----- ui/home/home-webview-screen.tsx | 12 ++++++++---- 5 files changed, 23 insertions(+), 16 deletions(-) diff --git a/app/webview/[slug].tsx b/app/webview/[slug].tsx index 3bcf681..6cf9104 100644 --- a/app/webview/[slug].tsx +++ b/app/webview/[slug].tsx @@ -87,11 +87,11 @@ export default function WebViewScreen() { } }; - const handleNavigateWebview = (slug: string) => { + const handleNavigateWebview = (slug: string, clubId?: string) => { if (slug.startsWith('club/')) { - const clubId = slug.slice('club/'.length); - if (!clubId) return; - router.push({ pathname: '/club/[id]', params: { id: clubId } }); + const slugId = slug.slice('club/'.length); + if (!slugId) return; + router.push({ pathname: '/club/[id]', params: { id: slugId, clubId } }); } else { router.push({ pathname: '/webview/[slug]', params: { slug } }); } @@ -174,6 +174,7 @@ export default function WebViewScreen() { domStorageEnabled={true} /> + ); } diff --git a/hooks/use-webview-message-handler.ts b/hooks/use-webview-message-handler.ts index 8ebc0ce..845feb8 100644 --- a/hooks/use-webview-message-handler.ts +++ b/hooks/use-webview-message-handler.ts @@ -6,7 +6,7 @@ interface UseWebViewMessageHandlerOptions { // 뒤로가기 요청 시 호출 onNavigateBack?: () => void; // 웹뷰 내 화면 이동 요청 시 호출 - onNavigateWebview?: (slug: string) => void; + onNavigateWebview?: (slug: string, clubId?: string) => void; // 알림 구독 요청 시 호출 onSubscribe?: (clubId: string, clubName?: string) => Promise | void; // 알림 구독 해제 요청 시 호출 @@ -36,7 +36,7 @@ export const useWebViewMessageHandler = ({ break; case WebViewMessageTypes.NAVIGATE_WEBVIEW: if (message.payload?.slug) { - onNavigateWebview?.(message.payload.slug); + onNavigateWebview?.(message.payload.slug, message.payload.clubId); } break; case WebViewMessageTypes.NOTIFICATION_SUBSCRIBE: diff --git a/types/webview-message.types.ts b/types/webview-message.types.ts index 0d917d4..1bf740e 100644 --- a/types/webview-message.types.ts +++ b/types/webview-message.types.ts @@ -13,7 +13,7 @@ export const WebViewMessageTypes = { export type WebViewMessage = | { type: 'NAVIGATE_BACK' } - | { type: 'NAVIGATE_WEBVIEW'; payload: { slug: string } } + | { type: 'NAVIGATE_WEBVIEW'; payload: { slug: string; clubId?: string } } | { type: 'NOTIFICATION_SUBSCRIBE'; payload: { clubId: string; clubName?: string } } | { type: 'NOTIFICATION_UNSUBSCRIBE'; payload: { clubId: string } } | { type: 'SHARE'; payload: { title: string; text: string; url: string } } diff --git a/ui/club-detail/club-detail-screen.tsx b/ui/club-detail/club-detail-screen.tsx index 511bb3a..7f66ef1 100644 --- a/ui/club-detail/club-detail-screen.tsx +++ b/ui/club-detail/club-detail-screen.tsx @@ -18,7 +18,7 @@ import styled from "styled-components/native"; export default function ClubWebViewScreen() { const router = useRouter(); - const { id, name } = useLocalSearchParams<{ id?: string; name?: string }>(); + const { id, name, clubId: objectId } = useLocalSearchParams<{ id?: string; name?: string; clubId?: string }>(); const [isLoading, setIsLoading] = useState(true); const [hasError, setHasError] = useState(false); const [showPermissionDialog, setShowPermissionDialog] = useState(false); @@ -38,15 +38,17 @@ export default function ClubWebViewScreen() { const baseUrl = `${cleanUrl}/webview/club/${id}`; let url = appendSessionId(baseUrl, sessionId); - if (id && isSubscribed(id)) { + const lookupId = typeof objectId === 'string' ? objectId : id; + if (lookupId && isSubscribed(lookupId)) { url += `&is_subscribed=true`; } return url; - }, [id, webviewUrl, sessionId, isSubscribed]); + }, [id, objectId, webviewUrl, sessionId, isSubscribed]); const subscribed = useMemo(() => { - return id ? isSubscribed(id) : false; - }, [id, isSubscribed]); + const lookupId = typeof objectId === 'string' ? objectId : id; + return lookupId ? isSubscribed(lookupId) : false; + }, [id, objectId, isSubscribed]); const userAgent = getWebViewUserAgent(); diff --git a/ui/home/home-webview-screen.tsx b/ui/home/home-webview-screen.tsx index d4cbbe9..fd26ad0 100644 --- a/ui/home/home-webview-screen.tsx +++ b/ui/home/home-webview-screen.tsx @@ -6,7 +6,7 @@ import Constants from 'expo-constants'; import { useRouter } from 'expo-router'; import * as WebBrowser from 'expo-web-browser'; import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { ActivityIndicator, BackHandler, Platform, View } from 'react-native'; +import { ActivityIndicator, BackHandler, Platform, Share, View } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { WebView, @@ -91,9 +91,9 @@ export function HomeWebViewScreen({ onError }: HomeWebViewScreenProps) { case 'NAVIGATE_WEBVIEW': if (!loaded) break; if (payload.slug?.startsWith('club/')) { - const clubId = payload.slug.slice('club/'.length); - if (!clubId) break; - router.push({ pathname: '/club/[id]', params: { id: clubId } }); + const slugId = payload.slug.slice('club/'.length); + if (!slugId) break; + router.push({ pathname: '/club/[id]', params: { id: slugId, clubId: payload.clubId } }); } else if (payload.slug?.startsWith('promotions/')) { router.push({ pathname: '/webview/[slug]', params: { slug: 'promotions', path: `/${payload.slug}`, hideHeader: 'true' } }); } else { @@ -105,6 +105,10 @@ export function HomeWebViewScreen({ onError }: HomeWebViewScreenProps) { await WebBrowser.openBrowserAsync(payload.url); break; + case 'SHARE': + await Share.share({ title: payload.title, message: payload.text, url: payload.url }); + break; + case 'REQUEST_APP_VERSION': sendMessage({ type: 'APP_VERSION', From 55d1774e76120c85f784d997272fc6b6f0442ebc Mon Sep 17 00:00:00 2001 From: SeongHoonC Date: Sat, 13 Jun 2026 16:29:51 +0900 Subject: [PATCH 6/6] chore: bump app version to 1.6.0 --- app.json | 6 +++--- ios/app.xcodeproj/project.pbxproj | 8 ++++---- ios/app/Info.plist | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app.json b/app.json index 84a864b..a4116c6 100644 --- a/app.json +++ b/app.json @@ -2,7 +2,7 @@ "expo": { "name": "모아동", "slug": "moadong-app", - "version": "1.5.2", + "version": "1.6.0", "orientation": "portrait", "icon": "./assets/images/icon.png", "scheme": "moadongapp", @@ -10,7 +10,7 @@ "newArchEnabled": true, "ios": { "supportsTablet": false, - "buildNumber": "15", + "buildNumber": "16", "googleServicesFile": "./GoogleService-Info.plist", "bundleIdentifier": "com.moadong.moadong", "associatedDomains": [ @@ -26,7 +26,7 @@ }, "android": { "jsEngine": "hermes", - "versionCode": 15, + "versionCode": 16, "adaptiveIcon": { "backgroundColor": "#E6F4FE", "foregroundImage": "./assets/images/android-icon-foreground.png", diff --git a/ios/app.xcodeproj/project.pbxproj b/ios/app.xcodeproj/project.pbxproj index d5e513c..98d23bc 100644 --- a/ios/app.xcodeproj/project.pbxproj +++ b/ios/app.xcodeproj/project.pbxproj @@ -417,7 +417,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = app/app.entitlements; - CURRENT_PROJECT_VERSION = 15; + CURRENT_PROJECT_VERSION = 16; DEVELOPMENT_TEAM = 2QMK9GBWN6; ENABLE_BITCODE = NO; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -430,7 +430,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 1.6.0; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -454,7 +454,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = app/app.entitlements; - CURRENT_PROJECT_VERSION = 15; + CURRENT_PROJECT_VERSION = 16; DEVELOPMENT_TEAM = 2QMK9GBWN6; INFOPLIST_FILE = app/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.1; @@ -462,7 +462,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 1.6.0; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", diff --git a/ios/app/Info.plist b/ios/app/Info.plist index aed8a97..71a5bfa 100644 --- a/ios/app/Info.plist +++ b/ios/app/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 1.5.2 + 1.6.0 CFBundleSignature ???? CFBundleURLTypes @@ -39,7 +39,7 @@ CFBundleVersion - 15 + 16 LSMinimumSystemVersion 12.0 LSRequiresIPhoneOS