From 6b63ede63f2d10b5a480b94c69236a74c333ee77 Mon Sep 17 00:00:00 2001 From: Dahyeon Date: Mon, 22 Dec 2025 23:31:59 +0900 Subject: [PATCH 01/14] =?UTF-8?q?refactor:=20=ED=94=BC=EB=93=9C=20?= =?UTF-8?q?=EA=B8=80=EC=9E=91=EC=84=B1=20=ED=82=A4=EB=B3=B4=EB=93=9C=20?= =?UTF-8?q?=ED=99=9C=EC=84=B1=ED=99=94=20=EC=8B=9C=20=EB=92=B7=EB=B0=B0?= =?UTF-8?q?=EA=B2=BD=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(post)/feed/write.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/(post)/feed/write.tsx b/app/(post)/feed/write.tsx index 8a02d93f..775203fe 100644 --- a/app/(post)/feed/write.tsx +++ b/app/(post)/feed/write.tsx @@ -219,6 +219,17 @@ export default function FeedWritePage() { gradient={{ top: 120 * height, bottom: 420 * height }} /> + {kbVisible && ( + + )} + {/* 헤더 */}
Date: Tue, 23 Dec 2025 23:53:04 +0900 Subject: [PATCH 02/14] =?UTF-8?q?refactor:=20s3=20=EC=97=85=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=20=EC=8B=A4=ED=8C=A8=20=EC=8B=9C=20=EC=97=90=EB=9F=AC?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(post)/feed.tsx | 30 +++++++++++++++++++++++------ app/(post)/feed/write.tsx | 14 +++++++++----- components/feed/FeedDetailItem.tsx | 2 ++ components/feed/GradientOverlay.tsx | 10 +++++++++- 4 files changed, 44 insertions(+), 12 deletions(-) diff --git a/app/(post)/feed.tsx b/app/(post)/feed.tsx index 17ab6a66..b7d1f7c5 100644 --- a/app/(post)/feed.tsx +++ b/app/(post)/feed.tsx @@ -14,6 +14,7 @@ import { getPresignedUrl, uploadImageToS3 } from '@/api/file/s3Upload'; import { Media } from '@/types/feed'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { prepareUploadImages } from '@/utils'; +import { useToastStore } from '@/stores/toastStore'; export default function PostFeedPage() { const insets = useSafeAreaInsets(); @@ -26,6 +27,7 @@ export default function PostFeedPage() { const setMediaUrls = useFeedWriteStore.getState().setMediaUrls; const router = useRouter(); + const { showToast } = useToastStore(); const [isUploading, setIsUploading] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false); @@ -51,6 +53,7 @@ export default function PostFeedPage() { const presignedUrls = await getPresignedUrl(fileNames, 'FEED'); const mediaArray: Media[] = []; + const failedUploads: string[] = []; for (let i = 0; i < presignedUrls.length; i++) { const { fileName, mediaUrl } = presignedUrls[i]; @@ -62,22 +65,37 @@ export default function PostFeedPage() { try { await uploadImageToS3(mediaUrl, foundImage.uri); + mediaArray.push({ + position: i + 1, + mediaUrl: mediaUrl.split('?')[0], + mediaType: 'IMAGE', + }); } catch (uploadError) { console.error(`S3 업로드 실패 (${fileName}):`, uploadError); - continue; + failedUploads.push(fileName); } + } + + // 모든 이미지 업로드 실패 시 + if (mediaArray.length === 0) { + showToast('이미지 업로드에 실패했어. 다시 시도해줘!', 'error'); + return; + } - mediaArray.push({ - position: i + 1, - mediaUrl: mediaUrl.split('?')[0], - mediaType: 'IMAGE', - }); + // 일부 이미지만 실패한 경우 + if (failedUploads.length > 0) { + showToast( + `${failedUploads.length}개의 이미지 업로드에 실패했어. 다시 시도해줘!`, + 'error', + ); + return; } setMediaUrls(mediaArray); router.push('/(post)/feed/write'); } catch (error) { console.error('이미지 업로드 중 에러 발생:', error); + showToast('이미지 업로드에 실패했어. 다시 시도해줘!', 'error'); } finally { setIsUploading(false); } diff --git a/app/(post)/feed/write.tsx b/app/(post)/feed/write.tsx index 775203fe..0a221bbd 100644 --- a/app/(post)/feed/write.tsx +++ b/app/(post)/feed/write.tsx @@ -92,11 +92,13 @@ export default function FeedWritePage() { const hasAnyImage = mediaUrls.length > 0; - const previewMedia: Media[] = mediaUrls.map((m, idx) => ({ - position: idx + 1, - mediaUrl: m.mediaUrl, - mediaType: m.mediaType, - })); + const previewMedia: Media[] = mediaUrls + .filter((m) => m.mediaUrl && m.mediaUrl.trim() !== '') + .map((m, idx) => ({ + position: idx + 1, + mediaUrl: m.mediaUrl, + mediaType: m.mediaType, + })); const requestBodyCreate = { description, @@ -214,10 +216,12 @@ export default function FeedWritePage() { onScrollBeginDrag={Keyboard.dismiss} > + {/* {previewMedia.length > 0 && ( */} + {/* )} */} {kbVisible && ( Date: Wed, 24 Dec 2025 00:01:33 +0900 Subject: [PATCH 03/14] =?UTF-8?q?fix:=20=ED=94=BC=EB=93=9C=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=ED=95=98=EA=B8=B0=20=EB=B2=84=EA=B7=B8=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/common/Header/KebabButton.tsx | 5 ++++- components/feed/FeedDetailItem.tsx | 10 +++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/components/common/Header/KebabButton.tsx b/components/common/Header/KebabButton.tsx index 972748c6..c66233c4 100644 --- a/components/common/Header/KebabButton.tsx +++ b/components/common/Header/KebabButton.tsx @@ -14,7 +14,10 @@ export default function KebabButton({ const iconColor = color === 'white' ? colors.white : colors.black; return ( - {})}> + {})} + hitSlop={{ top: 16, bottom: 16, left: 16, right: 16 }} + > ); diff --git a/components/feed/FeedDetailItem.tsx b/components/feed/FeedDetailItem.tsx index 2a3ffa43..3141698b 100644 --- a/components/feed/FeedDetailItem.tsx +++ b/components/feed/FeedDetailItem.tsx @@ -38,11 +38,11 @@ interface Props { } export default function FeedDetailItem({ feed }: Props) { - console.log('[FeedDetailItem] 피드 ID:', feed.feedId); - const { modalType, openModal, closeModal, payload } = useModalStore(); const isOpen = modalType === 'feedLinked' && payload?.feedId === feed.feedId; const isMenuOpen = modalType === 'menu' && payload?.feedId === feed.feedId; + const isDeleteConfirmOpen = + modalType === 'deleteConfirm' && payload?.feedId === feed.feedId; const { showToast } = useToastStore(); const { userInfo } = useUserStore(); @@ -109,8 +109,8 @@ export default function FeedDetailItem({ feed }: Props) { // 피드 삭제/신고 로직 const handleDelete = useCallback(() => { closeModal(); - openModal('deleteConfirm'); - }, [closeModal, openModal]); + openModal('deleteConfirm', null, { feedId: feed.feedId }); + }, [closeModal, openModal, feed.feedId]); const handleReport = useCallback(() => { closeModal(); @@ -239,7 +239,7 @@ export default function FeedDetailItem({ feed }: Props) { onPressSecond={isAuthor ? handleDelete : undefined} /> - {modalType === 'deleteConfirm' && ( + {isDeleteConfirmOpen && ( Date: Wed, 24 Dec 2025 00:51:31 +0900 Subject: [PATCH 04/14] =?UTF-8?q?refactor:=20=EC=9C=A0=EC=A0=80=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EB=AA=A8=EB=8B=AC=20=EC=95=A0=EB=8B=88?= =?UTF-8?q?=EB=A9=94=EC=9D=B4=EC=85=98=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/Modal/UserListModal.tsx | 80 ++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 11 deletions(-) diff --git a/components/Modal/UserListModal.tsx b/components/Modal/UserListModal.tsx index dff35690..8d4e9e5f 100644 --- a/components/Modal/UserListModal.tsx +++ b/components/Modal/UserListModal.tsx @@ -1,10 +1,17 @@ -import { Modal, Pressable, Platform, KeyboardAvoidingView } from 'react-native'; +import { + Modal, + Pressable, + Platform, + KeyboardAvoidingView, + Animated, +} from 'react-native'; import styled from 'styled-components/native'; import { BlurView } from 'expo-blur'; import colors from '@/theme/color'; import { fonts, fontSize, radius, height, width } from '@/theme/globalStyles'; import { FeedReactedUser, FeedConnectedUser } from '@/types/feed'; import UserListModalContent from '../feed/UserListModalContent'; +import { useEffect, useRef } from 'react'; interface Props { visible: boolean; @@ -19,14 +26,61 @@ export default function UserListModal({ list, onClose, }: Props) { + const fadeAnim = useRef(new Animated.Value(0)).current; + const slideAnim = useRef(new Animated.Value(500)).current; + + useEffect(() => { + if (visible) { + Animated.parallel([ + Animated.timing(fadeAnim, { + toValue: 1, + duration: 120, + useNativeDriver: true, + }), + Animated.spring(slideAnim, { + toValue: 0, + damping: 24, + stiffness: 180, + mass: 0.8, + useNativeDriver: true, + }), + ]).start(); + } else { + Animated.parallel([ + Animated.timing(fadeAnim, { + toValue: 0, + duration: 300, + useNativeDriver: true, + }), + Animated.timing(slideAnim, { + toValue: 500, + duration: 350, + useNativeDriver: true, + }), + ]).start(); + } + }, [visible, fadeAnim, slideAnim]); + return ( - - + + + - + @@ -42,15 +96,19 @@ export default function UserListModal({ - - + + ); } -const Backdrop = styled.Pressable` - flex: 1; - justify-content: flex-end; +const AnimatedBackdrop = styled(Animated.View)` + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); `; const SheetContainer = styled.View` From 2b2c430acefbb3af8f97297fdb8856cbfd25af36 Mon Sep 17 00:00:00 2001 From: Dahyeon Date: Thu, 25 Dec 2025 00:08:59 +0900 Subject: [PATCH 05/14] =?UTF-8?q?refactor:=20=EA=B0=A4=EB=9F=AC=EB=A6=AC?= =?UTF-8?q?=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=A1=9C=EB=93=9C=20=EC=8B=9C?= =?UTF-8?q?=20=EB=A1=9C=EB=94=A9=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EC=9C=84=EC=B9=98=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/Modal/UserListModal.tsx | 6 +++--- components/common/ImagePicker.tsx | 5 +++++ ios/Leenk.xcodeproj/project.pbxproj | 10 ++++------ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/components/Modal/UserListModal.tsx b/components/Modal/UserListModal.tsx index 8d4e9e5f..885df383 100644 --- a/components/Modal/UserListModal.tsx +++ b/components/Modal/UserListModal.tsx @@ -39,9 +39,9 @@ export default function UserListModal({ }), Animated.spring(slideAnim, { toValue: 0, - damping: 24, - stiffness: 180, - mass: 0.8, + damping: 20, + stiffness: 220, + mass: 0.6, useNativeDriver: true, }), ]).start(); diff --git a/components/common/ImagePicker.tsx b/components/common/ImagePicker.tsx index 7fef9d75..de0182b9 100644 --- a/components/common/ImagePicker.tsx +++ b/components/common/ImagePicker.tsx @@ -101,6 +101,11 @@ export default function ImagePicker({ windowSize={5} removeClippedSubviews ListFooterComponent={pagingLoading ? : null} + ListFooterComponentStyle={{ + marginTop: 44 * height, + marginBottom: 40 * height, + alignItems: 'center', + }} renderItem={({ item }) => ( Date: Thu, 25 Dec 2025 00:42:17 +0900 Subject: [PATCH 06/14] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=ED=86=A0=EA=B8=80=20=ED=96=85=ED=8B=B1,=20=EC=95=A0=EB=8B=88?= =?UTF-8?q?=EB=A9=94=EC=9D=B4=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/account/setting/notifications.tsx | 6 ++++ components/common/Toggle.tsx | 49 ++++++++++++++++++++++----- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/app/account/setting/notifications.tsx b/app/account/setting/notifications.tsx index 87f93b00..5bf7b317 100644 --- a/app/account/setting/notifications.tsx +++ b/app/account/setting/notifications.tsx @@ -8,6 +8,7 @@ import { getNotificationsSetting, patchNotificationsSetting, } from '@/api/users/notification.api'; +import * as Haptics from 'expo-haptics'; export default function SettingNotificationsPage() { const [toggles, setToggles] = useState({ @@ -57,6 +58,9 @@ export default function SettingNotificationsPage() { const handleToggle = async (key: keyof typeof toggles, apiKey: string) => { const newValue = !toggles[key]; + //햅틱 + Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Soft); + setToggles((prev) => ({ ...prev, [key]: newValue, @@ -66,6 +70,8 @@ export default function SettingNotificationsPage() { await patchNotificationsSetting({ [apiKey]: newValue }); } catch (error) { console.error('알림 설정 업데이트 실패:', error); + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error); + setToggles((prev) => ({ ...prev, [key]: !newValue, diff --git a/components/common/Toggle.tsx b/components/common/Toggle.tsx index 1b8d08eb..127c653e 100644 --- a/components/common/Toggle.tsx +++ b/components/common/Toggle.tsx @@ -1,7 +1,8 @@ import styled from 'styled-components/native'; import { width, height } from '@/theme/globalStyles'; import colors from '@/theme/color'; -import { Pressable } from 'react-native'; +import { Pressable, Animated } from 'react-native'; +import { useRef, useEffect } from 'react'; interface ToggleProps { isOn: boolean; @@ -9,11 +10,43 @@ interface ToggleProps { } export default function Toggle({ isOn, onToggle }: ToggleProps) { + const anim = useRef(new Animated.Value(isOn ? 1 : 0)).current; + const isFirstRender = useRef(true); + + useEffect(() => { + if (isFirstRender.current) { + // 최초 진입 시 애니메이션 없이 바로 반영 + anim.setValue(isOn ? 1 : 0); + isFirstRender.current = false; + return; + } + + Animated.timing(anim, { + toValue: isOn ? 1 : 0, + duration: 180, + useNativeDriver: false, + }).start(); + }, [isOn, anim]); + + const translateX = anim.interpolate({ + inputRange: [0, 1], + outputRange: [0, 15 * width], // Thumb 이동 거리 + }); + + const trackColor = anim.interpolate({ + inputRange: [0, 1], + outputRange: [colors.gray[400], colors.primary], + }); + return ( - - - + + + ); } @@ -24,19 +57,17 @@ const ToggleWrapper = styled(Pressable)` justify-content: center; `; -const Track = styled.View<{ isOn: boolean }>` +const AnimatedTrack = styled(Animated.View)` width: 100%; height: 100%; border-radius: ${100 * height}px; - background-color: ${({ isOn }) => (isOn ? colors.primary : colors.gray[400])}; - padding-horizontal: ${4 * width}px; + padding: 0 ${2.5 * width}px; justify-content: center; `; -const Thumb = styled.View<{ isOn: boolean }>` +const AnimatedThumb = styled(Animated.View)` width: ${16 * height}px; height: ${16 * height}px; border-radius: ${100 * height}px; background-color: ${colors.white}; - align-self: ${({ isOn }) => (isOn ? 'flex-end' : 'flex-start')}; `; From 504c64bff77cfc78a6d5987c62963ee4e3b77525 Mon Sep 17 00:00:00 2001 From: Dahyeon Date: Fri, 26 Dec 2025 23:54:50 +0900 Subject: [PATCH 07/14] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=ED=94=BC=EB=93=9C=20=EA=B2=BD=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/account/notification-list.tsx | 8 +++++++- ios/Leenk.xcodeproj/project.pbxproj | 10 ++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/account/notification-list.tsx b/app/account/notification-list.tsx index 882effb1..074ff62e 100644 --- a/app/account/notification-list.tsx +++ b/app/account/notification-list.tsx @@ -87,7 +87,13 @@ export default function NotificationListPage() { if (path === 'leenks') { router.push(`/leenk/${content.leenkId}`); } else { - router.push(`/feed/${content.feedId}`); + router.push({ + pathname: '/feed', + params: { + feedId: content.feedId, + mode: 'single', + }, + }); } } } catch (error) { diff --git a/ios/Leenk.xcodeproj/project.pbxproj b/ios/Leenk.xcodeproj/project.pbxproj index 1bfc3536..1fb5e950 100644 --- a/ios/Leenk.xcodeproj/project.pbxproj +++ b/ios/Leenk.xcodeproj/project.pbxproj @@ -174,6 +174,8 @@ TargetAttributes = { 13B07F861A680F5B00A75B9A = { LastSwiftMigration = 1250; + DevelopmentTeam = "2VCK7Z2C2W"; + ProvisioningStyle = Automatic; }; }; }; @@ -425,9 +427,9 @@ CODE_SIGN_ENTITLEMENTS = Leenk/Leenk.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = "2VCK7Z2C2W"; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 5DPG335P5J; ENABLE_BITCODE = NO; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -468,9 +470,9 @@ CODE_SIGN_ENTITLEMENTS = Leenk/Leenk.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = "2VCK7Z2C2W"; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 5DPG335P5J; INFOPLIST_FILE = Leenk/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.1; From 1ffdd80e12a3c2f006c4d78cb9fba27b9bdb393c Mon Sep 17 00:00:00 2001 From: Dahyeon Date: Sat, 27 Dec 2025 23:29:17 +0900 Subject: [PATCH 08/14] =?UTF-8?q?refactor:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EB=AA=A8=EB=8B=AC=20=EC=8A=A4=ED=83=80=EC=9D=BC=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 --- app/account/notification-list.tsx | 1 + components/Modal/NotificationModal.tsx | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/app/account/notification-list.tsx b/app/account/notification-list.tsx index 074ff62e..991e9abc 100644 --- a/app/account/notification-list.tsx +++ b/app/account/notification-list.tsx @@ -87,6 +87,7 @@ export default function NotificationListPage() { if (path === 'leenks') { router.push(`/leenk/${content.leenkId}`); } else { + console.log('feedId : ', content.feedId); router.push({ pathname: '/feed', params: { diff --git a/components/Modal/NotificationModal.tsx b/components/Modal/NotificationModal.tsx index f508324e..2ac246d9 100644 --- a/components/Modal/NotificationModal.tsx +++ b/components/Modal/NotificationModal.tsx @@ -1,7 +1,6 @@ -import React from 'react'; import { Modal, Platform, TouchableWithoutFeedback } from 'react-native'; import styled from 'styled-components/native'; -import { width, height, radius } from '@/theme/globalStyles'; +import { width, height, radius, fonts } from '@/theme/globalStyles'; import colors from '@/theme/color'; import { FlatList } from 'react-native-gesture-handler'; import { @@ -38,7 +37,7 @@ export default function NotificationModal({ ): item is NewLeenkParticipantDetails => (item as NewLeenkParticipantDetails).participantName !== undefined; - const isScrollable = data.length > 5; + const isScrollable = data.length >= 4; return ( - {item.body} - {item.name} + {item.body} + {item.name} ); @@ -105,7 +104,7 @@ export default function NotificationModal({ - {item.participantName} + {item.participantName} ); @@ -171,6 +170,9 @@ const Item = styled.View` const ContentContainer = styled.View` margin-left: ${28 * width}px; + display: flex; + gap: ${6 * height}px; + align-items: flex-start; `; const GradientOverlay = styled(LinearGradient).attrs({ @@ -183,3 +185,7 @@ const GradientOverlay = styled(LinearGradient).attrs({ height: ${60 * height}px; z-index: 1; `; + +const StyledTitle = styled(SubText)` + color: ${colors.text[1]}; +`; From a34ca9885a9b177035c2b0136412a8cf39a00572 Mon Sep 17 00:00:00 2001 From: Dahyeon Date: Sun, 28 Dec 2025 23:21:45 +0900 Subject: [PATCH 09/14] =?UTF-8?q?fix:=20=EC=82=AD=EC=A0=9C=EB=90=9C=20?= =?UTF-8?q?=EB=A7=81=ED=81=AC/=ED=94=BC=EB=93=9C=20=ED=86=A0=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(post)/feed.tsx | 30 ++++++----------------------- app/(post)/leenk/[id]/index.tsx | 16 +++++++++++---- app/account/notification-list.tsx | 8 +------- hooks/useFeedDetailNavigation.ts | 14 +++++++++++++- ios/Leenk.xcodeproj/project.pbxproj | 10 ++++------ 5 files changed, 36 insertions(+), 42 deletions(-) diff --git a/app/(post)/feed.tsx b/app/(post)/feed.tsx index b7d1f7c5..17ab6a66 100644 --- a/app/(post)/feed.tsx +++ b/app/(post)/feed.tsx @@ -14,7 +14,6 @@ import { getPresignedUrl, uploadImageToS3 } from '@/api/file/s3Upload'; import { Media } from '@/types/feed'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { prepareUploadImages } from '@/utils'; -import { useToastStore } from '@/stores/toastStore'; export default function PostFeedPage() { const insets = useSafeAreaInsets(); @@ -27,7 +26,6 @@ export default function PostFeedPage() { const setMediaUrls = useFeedWriteStore.getState().setMediaUrls; const router = useRouter(); - const { showToast } = useToastStore(); const [isUploading, setIsUploading] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false); @@ -53,7 +51,6 @@ export default function PostFeedPage() { const presignedUrls = await getPresignedUrl(fileNames, 'FEED'); const mediaArray: Media[] = []; - const failedUploads: string[] = []; for (let i = 0; i < presignedUrls.length; i++) { const { fileName, mediaUrl } = presignedUrls[i]; @@ -65,37 +62,22 @@ export default function PostFeedPage() { try { await uploadImageToS3(mediaUrl, foundImage.uri); - mediaArray.push({ - position: i + 1, - mediaUrl: mediaUrl.split('?')[0], - mediaType: 'IMAGE', - }); } catch (uploadError) { console.error(`S3 업로드 실패 (${fileName}):`, uploadError); - failedUploads.push(fileName); + continue; } - } - - // 모든 이미지 업로드 실패 시 - if (mediaArray.length === 0) { - showToast('이미지 업로드에 실패했어. 다시 시도해줘!', 'error'); - return; - } - // 일부 이미지만 실패한 경우 - if (failedUploads.length > 0) { - showToast( - `${failedUploads.length}개의 이미지 업로드에 실패했어. 다시 시도해줘!`, - 'error', - ); - return; + mediaArray.push({ + position: i + 1, + mediaUrl: mediaUrl.split('?')[0], + mediaType: 'IMAGE', + }); } setMediaUrls(mediaArray); router.push('/(post)/feed/write'); } catch (error) { console.error('이미지 업로드 중 에러 발생:', error); - showToast('이미지 업로드에 실패했어. 다시 시도해줘!', 'error'); } finally { setIsUploading(false); } diff --git a/app/(post)/leenk/[id]/index.tsx b/app/(post)/leenk/[id]/index.tsx index c4ed393a..2a7ffd09 100644 --- a/app/(post)/leenk/[id]/index.tsx +++ b/app/(post)/leenk/[id]/index.tsx @@ -87,10 +87,18 @@ export default function LeenkDetailPage() { if (!signal?.canceled) { setLeenkDetail(data); } - } catch (e) { - if (!signal?.canceled) { - showToast('상세 정보를 불러오지 못했어.', 'error'); - router.back(); + } catch (e: any) { + if (signal?.canceled) return; + + // 삭제된 링크 (404) + if (e?.response?.status === 404) { + showToast('삭제된 링크야!', 'error'); + + setTimeout(() => { + router.back(); + }, 900); + + return; } } finally { if (!signal?.canceled) setLoading(false); diff --git a/app/account/notification-list.tsx b/app/account/notification-list.tsx index 991e9abc..080bf76d 100644 --- a/app/account/notification-list.tsx +++ b/app/account/notification-list.tsx @@ -88,13 +88,7 @@ export default function NotificationListPage() { router.push(`/leenk/${content.leenkId}`); } else { console.log('feedId : ', content.feedId); - router.push({ - pathname: '/feed', - params: { - feedId: content.feedId, - mode: 'single', - }, - }); + router.push(`/feed/${content.feedId}`); } } } catch (error) { diff --git a/hooks/useFeedDetailNavigation.ts b/hooks/useFeedDetailNavigation.ts index 71f9be3f..da3d075e 100644 --- a/hooks/useFeedDetailNavigation.ts +++ b/hooks/useFeedDetailNavigation.ts @@ -3,6 +3,7 @@ import { FlatList, ViewToken } from 'react-native'; import { router } from 'expo-router'; import { getFeedNavigation } from '@/api/feed/feed.api'; import { FeedNavigationItem } from '@/types/feed'; +import { useToastStore } from '@/stores/toastStore'; const transformFeedItem = (item: FeedNavigationItem, uniqueKey?: string) => ({ feedId: item.feedId, @@ -24,6 +25,7 @@ export function useFeedDetailNavigation(initialFeedId: number) { const [hasMorePrev, setHasMorePrev] = useState(false); const [hasMoreNext, setHasMoreNext] = useState(false); const [isLoadingMore, setIsLoadingMore] = useState(false); + const { showToast } = useToastStore(); const flatListRef = useRef(null); const currentIndexRef = useRef(0); @@ -65,7 +67,17 @@ export function useFeedDetailNavigation(initialFeedId: number) { currentIndexRef.current = navigation.prevFeeds.length; initialLoadDoneRef.current = true; - } catch (e) { + } catch (e: any) { + const status = e?.response?.status; + + if (status === 404) { + showToast('삭제된 피드야!', 'error'); + + setTimeout(() => { + router.back(); + }, 900); + return; + } console.error('피드 네비게이션 초기 로드 실패', e); } finally { setIsLoading(false); diff --git a/ios/Leenk.xcodeproj/project.pbxproj b/ios/Leenk.xcodeproj/project.pbxproj index 1fb5e950..1bfc3536 100644 --- a/ios/Leenk.xcodeproj/project.pbxproj +++ b/ios/Leenk.xcodeproj/project.pbxproj @@ -174,8 +174,6 @@ TargetAttributes = { 13B07F861A680F5B00A75B9A = { LastSwiftMigration = 1250; - DevelopmentTeam = "2VCK7Z2C2W"; - ProvisioningStyle = Automatic; }; }; }; @@ -427,9 +425,9 @@ CODE_SIGN_ENTITLEMENTS = Leenk/Leenk.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = "2VCK7Z2C2W"; + DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 5DPG335P5J; ENABLE_BITCODE = NO; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -470,9 +468,9 @@ CODE_SIGN_ENTITLEMENTS = Leenk/Leenk.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = "2VCK7Z2C2W"; + DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 5DPG335P5J; INFOPLIST_FILE = Leenk/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.1; From 08a474d594a3ce3fc586d2ec7168afa4fc1cc237 Mon Sep 17 00:00:00 2001 From: Dahyeon Date: Sun, 28 Dec 2025 23:25:11 +0900 Subject: [PATCH 10/14] =?UTF-8?q?fix:=20=EB=A7=81=ED=81=AC=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=ED=9B=84=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=20=EA=B2=BD=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(post)/leenk/[id]/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(post)/leenk/[id]/index.tsx b/app/(post)/leenk/[id]/index.tsx index 2a7ffd09..2bff8f7f 100644 --- a/app/(post)/leenk/[id]/index.tsx +++ b/app/(post)/leenk/[id]/index.tsx @@ -200,7 +200,7 @@ export default function LeenkDetailPage() { await deleteLeenk(leenkDetail.id); showToast('삭제 완료!', 'success'); closeModal(); - router.replace('/leenk'); + router.replace('/(page)/leenk'); } catch (err) { console.error('링크 삭제 오류:', err); showToast('삭제에 실패했어. 잠시 후 다시 시도해 줘.', 'error'); From a867ba9e6d02dde4e5284a2e4de6835769b94a8a Mon Sep 17 00:00:00 2001 From: Dahyeon Date: Sun, 28 Dec 2025 23:34:48 +0900 Subject: [PATCH 11/14] =?UTF-8?q?chore:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EB=A1=9C=EA=B7=B8=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/account/notification-list.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/account/notification-list.tsx b/app/account/notification-list.tsx index 080bf76d..882effb1 100644 --- a/app/account/notification-list.tsx +++ b/app/account/notification-list.tsx @@ -87,7 +87,6 @@ export default function NotificationListPage() { if (path === 'leenks') { router.push(`/leenk/${content.leenkId}`); } else { - console.log('feedId : ', content.feedId); router.push(`/feed/${content.feedId}`); } } From 4cb5363bb0707bc59d7ec210aa94c4f9cb359f61 Mon Sep 17 00:00:00 2001 From: Dahyeon Date: Tue, 30 Dec 2025 23:49:28 +0900 Subject: [PATCH 12/14] =?UTF-8?q?chore:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(post)/feed/write.tsx | 14 ++++++-------- ios/Leenk.xcodeproj/project.pbxproj | 10 ++++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/(post)/feed/write.tsx b/app/(post)/feed/write.tsx index 0a221bbd..1973da59 100644 --- a/app/(post)/feed/write.tsx +++ b/app/(post)/feed/write.tsx @@ -162,8 +162,6 @@ export default function FeedWritePage() { setIsModalOpen(false); setIsUploading(true); try { - // if (__DEV__) console.log('수정 내용 : ', requestBodyEdit); - const idNum = typeof feedId === 'string' ? Number(feedId) : NaN; if (Number.isNaN(idNum) || idNum <= 0) { showToast('잘못된 피드 아이디야!', 'error'); @@ -216,12 +214,12 @@ export default function FeedWritePage() { onScrollBeginDrag={Keyboard.dismiss} > - {/* {previewMedia.length > 0 && ( */} - - {/* )} */} + {previewMedia.length > 0 && ( + + )} {kbVisible && ( Date: Wed, 31 Dec 2025 00:53:45 +0900 Subject: [PATCH 13/14] =?UTF-8?q?style:=20=EB=A7=81=ED=81=AC=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=8A=A4=ED=83=80?= =?UTF-8?q?=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(post)/leenk/[id]/index.tsx | 22 ++++++++++++------- app/signup/terms.tsx | 4 ++-- components/leenk/LeenkDetailContent.tsx | 7 ++++--- components/mypage/MypageButton.tsx | 1 - ios/Leenk.xcodeproj/project.pbxproj | 20 ++++++++++-------- ios/Podfile.lock | 28 +++++++++++++++++++++++++ package-lock.json | 10 +++++++++ package.json | 1 + 8 files changed, 70 insertions(+), 23 deletions(-) diff --git a/app/(post)/leenk/[id]/index.tsx b/app/(post)/leenk/[id]/index.tsx index 2bff8f7f..3656cfdd 100644 --- a/app/(post)/leenk/[id]/index.tsx +++ b/app/(post)/leenk/[id]/index.tsx @@ -12,7 +12,7 @@ import styled from 'styled-components/native'; import { Platform, StyleSheet } from 'react-native'; import { Image } from 'expo-image'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { Share } from 'react-native'; +import Share from 'react-native-share'; import { useCallback, useEffect, useMemo, useState } from 'react'; import LeenkContentSection from '@/components/leenk/LeenkDetailContent'; @@ -281,14 +281,20 @@ export default function LeenkDetailPage() { }); // 예: "leenk://leenk/123" - // iOS는 url 필드를 더 잘 인식 - if (Platform.OS === 'ios') { - await Share.share({ url: deepLink, message: leenkDetail?.title }); - } else { - await Share.share({ message: `${leenkDetail?.title}\n${deepLink}` }); + const shareOptions = { + title: leenkDetail?.title, + message: `${leenkDetail?.title}\n${deepLink}`, + url: deepLink, + }; + + await Share.open(shareOptions); + } catch (error: any) { + // 사용자가 공유를 취소한 경우 (error.message === 'User did not share') + if (error?.message && error.message.includes('User did not share')) { + return; // 에러 표시 없이 종료 } - } catch { - // 실패 시 클립보드 복사 폴백 + + // 실제 에러인 경우 클립보드 복사 폴백 const fallback = Linking.createURL(`/leenk/${leenkId}`, { scheme: 'leenk', }); diff --git a/app/signup/terms.tsx b/app/signup/terms.tsx index a786cecd..11ea0e47 100644 --- a/app/signup/terms.tsx +++ b/app/signup/terms.tsx @@ -84,7 +84,7 @@ export default function TermsPage() { 서비스 이용약관 (필수) - + setVisibleModal('info')}> @@ -92,7 +92,7 @@ export default function TermsPage() { 개인정보 수집/이용 동의 (필수) - + diff --git a/components/leenk/LeenkDetailContent.tsx b/components/leenk/LeenkDetailContent.tsx index 275ba435..cb20553e 100644 --- a/components/leenk/LeenkDetailContent.tsx +++ b/components/leenk/LeenkDetailContent.tsx @@ -55,6 +55,9 @@ export default function LeenkContentSection({ {data.title} + {/* + + */} @@ -67,9 +70,7 @@ export default function LeenkContentSection({ - {data.author.name} - {''}・{''} - {formatRelativeTime(data.createdAt)} + {data.author.name} ・ {formatRelativeTime(data.createdAt)} diff --git a/components/mypage/MypageButton.tsx b/components/mypage/MypageButton.tsx index 0d169858..bea44ddc 100644 --- a/components/mypage/MypageButton.tsx +++ b/components/mypage/MypageButton.tsx @@ -1,7 +1,6 @@ import { RightArrowIcon } from '@/assets'; import colors from '@/theme/color'; import { fonts, fontSize, width } from '@/theme/globalStyles'; -import React from 'react'; import styled from 'styled-components/native'; import Toggle from '@/components/common/Toggle'; diff --git a/ios/Leenk.xcodeproj/project.pbxproj b/ios/Leenk.xcodeproj/project.pbxproj index 1fb5e950..5ceb606b 100644 --- a/ios/Leenk.xcodeproj/project.pbxproj +++ b/ios/Leenk.xcodeproj/project.pbxproj @@ -173,8 +173,8 @@ LastUpgradeCheck = 1130; TargetAttributes = { 13B07F861A680F5B00A75B9A = { + DevelopmentTeam = 2VCK7Z2C2W; LastSwiftMigration = 1250; - DevelopmentTeam = "2VCK7Z2C2W"; ProvisioningStyle = Automatic; }; }; @@ -257,8 +257,6 @@ "$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)", ); name = "[CP-User] [RNFB] Crashlytics Configuration"; - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nset -e\n\nif [[ ${PODS_ROOT} ]]; then\n echo \"info: Exec FirebaseCrashlytics Run from Pods\"\n \"${PODS_ROOT}/FirebaseCrashlytics/run\"\nelse\n echo \"info: Exec FirebaseCrashlytics Run from framework\"\n \"${PROJECT_DIR}/FirebaseCrashlytics.framework/run\"\nfi\n"; @@ -272,8 +270,6 @@ "$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)", ); name = "[CP-User] [RNFB] Core Configuration"; - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##########################################################################\n##########################################################################\n#\n# NOTE THAT IF YOU CHANGE THIS FILE YOU MUST RUN pod install AFTERWARDS\n#\n# This file is installed as an Xcode build script in the project file\n# by cocoapods, and you will not see your changes until you pod install\n#\n##########################################################################\n##########################################################################\n\nset -e\n\n_MAX_LOOKUPS=2;\n_SEARCH_RESULT=''\n_RN_ROOT_EXISTS=''\n_CURRENT_LOOKUPS=1\n_JSON_ROOT=\"'react-native'\"\n_JSON_FILE_NAME='firebase.json'\n_JSON_OUTPUT_BASE64='e30=' # { }\n_CURRENT_SEARCH_DIR=${PROJECT_DIR}\n_PLIST_BUDDY=/usr/libexec/PlistBuddy\n_TARGET_PLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n_DSYM_PLIST=\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist\"\n\n# plist arrays\n_PLIST_ENTRY_KEYS=()\n_PLIST_ENTRY_TYPES=()\n_PLIST_ENTRY_VALUES=()\n\nfunction setPlistValue {\n echo \"note: setting plist entry '$1' of type '$2' in file '$4'\"\n ${_PLIST_BUDDY} -c \"Add :$1 $2 '$3'\" $4 || echo \"note: '$1' already exists\"\n}\n\nfunction getFirebaseJsonKeyValue () {\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n ruby -Ku -e \"require 'rubygems';require 'json'; output=JSON.parse('$1'); puts output[$_JSON_ROOT]['$2']\"\n else\n echo \"\"\n fi;\n}\n\nfunction jsonBoolToYesNo () {\n if [[ $1 == \"false\" ]]; then\n echo \"NO\"\n elif [[ $1 == \"true\" ]]; then\n echo \"YES\"\n else echo \"NO\"\n fi\n}\n\necho \"note: -> RNFB build script started\"\necho \"note: 1) Locating ${_JSON_FILE_NAME} file:\"\n\nif [[ -z ${_CURRENT_SEARCH_DIR} ]]; then\n _CURRENT_SEARCH_DIR=$(pwd)\nfi;\n\nwhile true; do\n _CURRENT_SEARCH_DIR=$(dirname \"$_CURRENT_SEARCH_DIR\")\n if [[ \"$_CURRENT_SEARCH_DIR\" == \"/\" ]] || [[ ${_CURRENT_LOOKUPS} -gt ${_MAX_LOOKUPS} ]]; then break; fi;\n echo \"note: ($_CURRENT_LOOKUPS of $_MAX_LOOKUPS) Searching in '$_CURRENT_SEARCH_DIR' for a ${_JSON_FILE_NAME} file.\"\n _SEARCH_RESULT=$(find \"$_CURRENT_SEARCH_DIR\" -maxdepth 2 -name ${_JSON_FILE_NAME} -print | /usr/bin/head -n 1)\n if [[ ${_SEARCH_RESULT} ]]; then\n echo \"note: ${_JSON_FILE_NAME} found at $_SEARCH_RESULT\"\n break;\n fi;\n _CURRENT_LOOKUPS=$((_CURRENT_LOOKUPS+1))\ndone\n\nif [[ ${_SEARCH_RESULT} ]]; then\n _JSON_OUTPUT_RAW=$(cat \"${_SEARCH_RESULT}\")\n if ! _RN_ROOT_EXISTS=$(ruby -Ku -e \"require 'json'; output=JSON.parse('$_JSON_OUTPUT_RAW'); puts output[$_JSON_ROOT]\"); then\n echo \"error: Failed to parse firebase.json, check for syntax errors.\"\n exit 1\n fi\n\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n if ! python3 --version >/dev/null 2>&1; then echo \"error: python3 not found, firebase.json file processing error.\" && exit 1; fi\n _JSON_OUTPUT_BASE64=$(python3 -c 'import json,sys,base64;print(base64.b64encode(bytes(json.dumps(json.loads(open('\"'${_SEARCH_RESULT}'\"', '\"'rb'\"').read())['${_JSON_ROOT}']), '\"'utf-8'\"')).decode())' || echo \"e30=\")\n fi\n\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n\n # config.app_data_collection_default_enabled\n _APP_DATA_COLLECTION_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_data_collection_default_enabled\")\n if [[ $_APP_DATA_COLLECTION_ENABLED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseDataCollectionDefaultEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_DATA_COLLECTION_ENABLED\")\")\n fi\n\n # config.analytics_auto_collection_enabled\n _ANALYTICS_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_auto_collection_enabled\")\n if [[ $_ANALYTICS_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_COLLECTION\")\")\n fi\n\n # config.analytics_collection_deactivated\n _ANALYTICS_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_collection_deactivated\")\n if [[ $_ANALYTICS_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_DEACTIVATED\")\")\n fi\n\n # config.analytics_idfv_collection_enabled\n _ANALYTICS_IDFV_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_idfv_collection_enabled\")\n if [[ $_ANALYTICS_IDFV_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_IDFV_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_IDFV_COLLECTION\")\")\n fi\n\n # config.analytics_default_allow_analytics_storage\n _ANALYTICS_STORAGE=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_analytics_storage\")\n if [[ $_ANALYTICS_STORAGE ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_ANALYTICS_STORAGE\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_STORAGE\")\")\n fi\n\n # config.analytics_default_allow_ad_storage\n _ANALYTICS_AD_STORAGE=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_storage\")\n if [[ $_ANALYTICS_AD_STORAGE ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_STORAGE\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AD_STORAGE\")\")\n fi\n\n # config.analytics_default_allow_ad_user_data\n _ANALYTICS_AD_USER_DATA=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_user_data\")\n if [[ $_ANALYTICS_AD_USER_DATA ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_USER_DATA\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AD_USER_DATA\")\")\n fi\n\n # config.analytics_default_allow_ad_personalization_signals\n _ANALYTICS_PERSONALIZATION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_personalization_signals\")\n if [[ $_ANALYTICS_PERSONALIZATION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_PERSONALIZATION_SIGNALS\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_PERSONALIZATION\")\")\n fi\n\n # config.analytics_registration_with_ad_network_enabled\n _ANALYTICS_REGISTRATION_WITH_AD_NETWORK=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"google_analytics_registration_with_ad_network_enabled\")\n if [[ $_ANALYTICS_REGISTRATION_WITH_AD_NETWORK ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_REGISTRATION_WITH_AD_NETWORK_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_REGISTRATION_WITH_AD_NETWORK\")\")\n fi\n\n # config.google_analytics_automatic_screen_reporting_enabled\n _ANALYTICS_AUTO_SCREEN_REPORTING=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"google_analytics_automatic_screen_reporting_enabled\")\n if [[ $_ANALYTICS_AUTO_SCREEN_REPORTING ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAutomaticScreenReportingEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_SCREEN_REPORTING\")\")\n fi\n\n # config.perf_auto_collection_enabled\n _PERF_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_auto_collection_enabled\")\n if [[ $_PERF_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_enabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_AUTO_COLLECTION\")\")\n fi\n\n # config.perf_collection_deactivated\n _PERF_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_collection_deactivated\")\n if [[ $_PERF_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_deactivated\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_DEACTIVATED\")\")\n fi\n\n # config.messaging_auto_init_enabled\n _MESSAGING_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"messaging_auto_init_enabled\")\n if [[ $_MESSAGING_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseMessagingAutoInitEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_MESSAGING_AUTO_INIT\")\")\n fi\n\n # config.in_app_messaging_auto_colllection_enabled\n _FIAM_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"in_app_messaging_auto_collection_enabled\")\n if [[ $_FIAM_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseInAppMessagingAutomaticDataCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_FIAM_AUTO_INIT\")\")\n fi\n\n # config.app_check_token_auto_refresh\n _APP_CHECK_TOKEN_AUTO_REFRESH=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_check_token_auto_refresh\")\n if [[ $_APP_CHECK_TOKEN_AUTO_REFRESH ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAppCheckTokenAutoRefreshEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_CHECK_TOKEN_AUTO_REFRESH\")\")\n fi\n\n # config.crashlytics_disable_auto_disabler - undocumented for now - mainly for debugging, document if becomes useful\n _CRASHLYTICS_AUTO_DISABLE_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"crashlytics_disable_auto_disabler\")\n if [[ $_CRASHLYTICS_AUTO_DISABLE_ENABLED == \"true\" ]]; then\n echo \"Disabled Crashlytics auto disabler.\" # do nothing\n else\n _PLIST_ENTRY_KEYS+=(\"FirebaseCrashlyticsCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"NO\")\n fi\nelse\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n echo \"warning: A firebase.json file was not found, whilst this file is optional it is recommended to include it to configure firebase services in React Native Firebase.\"\nfi;\n\necho \"note: 2) Injecting Info.plist entries: \"\n\n# Log out the keys we're adding\nfor i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n echo \" -> $i) ${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\"\ndone\n\nfor plist in \"${_TARGET_PLIST}\" \"${_DSYM_PLIST}\" ; do\n if [[ -f \"${plist}\" ]]; then\n\n # paths with spaces break the call to setPlistValue. temporarily modify\n # the shell internal field separator variable (IFS), which normally\n # includes spaces, to consist only of line breaks\n oldifs=$IFS\n IFS=\"\n\"\n\n for i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n setPlistValue \"${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\" \"${plist}\"\n done\n\n # restore the original internal field separator value\n IFS=$oldifs\n else\n echo \"warning: A Info.plist build output file was not found (${plist})\"\n fi\ndone\n\necho \"note: <- RNFB build script finished\"\n"; @@ -429,7 +425,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = "2VCK7Z2C2W"; + DEVELOPMENT_TEAM = 2VCK7Z2C2W; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 5DPG335P5J; ENABLE_BITCODE = NO; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -472,7 +468,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = "2VCK7Z2C2W"; + DEVELOPMENT_TEAM = 2VCK7Z2C2W; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 5DPG335P5J; INFOPLIST_FILE = Leenk/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.1; @@ -564,7 +560,10 @@ LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\""; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; @@ -630,7 +629,10 @@ ); LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\""; MTL_ENABLE_DEBUG_INFO = NO; - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 05f4c7b9..7bebc7c9 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -2630,6 +2630,30 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga + - RNShare (12.2.1): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.11.18.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga - RNSVG (15.12.0): - DoubleConversion - glog @@ -2815,6 +2839,7 @@ DEPENDENCIES: - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - RNReanimated (from `../node_modules/react-native-reanimated`) - RNScreens (from `../node_modules/react-native-screens`) + - RNShare (from `../node_modules/react-native-share`) - RNSVG (from `../node_modules/react-native-svg`) - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) @@ -3092,6 +3117,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-reanimated" RNScreens: :path: "../node_modules/react-native-screens" + RNShare: + :path: "../node_modules/react-native-share" RNSVG: :path: "../node_modules/react-native-svg" Yoga: @@ -3243,6 +3270,7 @@ SPEC CHECKSUMS: RNGestureHandler: 8ff2b1434b0ff8bab28c8242a656fb842990bbc8 RNReanimated: a3f55346df73f35c38bf0b446294d3d0b1ac8cf9 RNScreens: 90b905d545a5ebbe976985702b8a39e3475727b2 + RNShare: 1012edc984c2c4123041012fcb94e9e0a1df46d5 RNSVG: 45e3c3210465e75ab6374c9f746179e75d76ce48 SDWebImage: d0184764be51240d49c761c37f53dd017e1ccaaf SDWebImageAVIFCoder: afe194a084e851f70228e4be35ef651df0fc5c57 diff --git a/package-lock.json b/package-lock.json index 8a820ed4..be439d2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -69,6 +69,7 @@ "react-native-reanimated-carousel": "^4.0.2", "react-native-safe-area-context": "5.4.0", "react-native-screens": "~4.11.1", + "react-native-share": "^12.2.1", "react-native-svg": "^15.12.0", "react-native-svg-transformer": "^1.5.1", "react-native-web": "~0.20.0", @@ -13901,6 +13902,15 @@ "react-native": "*" } }, + "node_modules/react-native-share": { + "version": "12.2.1", + "resolved": "https://registry.npmjs.org/react-native-share/-/react-native-share-12.2.1.tgz", + "integrity": "sha512-DPfvqQMbbLK4ykPkqYarby5AXdgFsbefOhsQHkOrDeAIixWzeI4oe/WvI7AoYmlQxM4Ys2DcBPBWDYQ6gn5xYA==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, "node_modules/react-native-svg": { "version": "15.12.0", "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.12.0.tgz", diff --git a/package.json b/package.json index e4f1b047..ff3bbf90 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "react-native-reanimated-carousel": "^4.0.2", "react-native-safe-area-context": "5.4.0", "react-native-screens": "~4.11.1", + "react-native-share": "^12.2.1", "react-native-svg": "^15.12.0", "react-native-svg-transformer": "^1.5.1", "react-native-web": "~0.20.0", From 2a843061662e9526ab05248369d52e03ce125819 Mon Sep 17 00:00:00 2001 From: Dahyeon Date: Wed, 31 Dec 2025 01:00:31 +0900 Subject: [PATCH 14/14] =?UTF-8?q?chore:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/signup/terms.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/signup/terms.tsx b/app/signup/terms.tsx index 11ea0e47..a786cecd 100644 --- a/app/signup/terms.tsx +++ b/app/signup/terms.tsx @@ -84,7 +84,7 @@ export default function TermsPage() { 서비스 이용약관 (필수) - + setVisibleModal('info')}> @@ -92,7 +92,7 @@ export default function TermsPage() { 개인정보 수집/이용 동의 (필수) - +