From 90a655b496811ba2635de5b77032a994b4822dcf Mon Sep 17 00:00:00 2001 From: SJ-Kwak Date: Wed, 14 Feb 2024 09:12:04 +0100 Subject: [PATCH] sdp-tech#25 feat: add photo options component --- ios/Podfile.lock | 28 +++- ios/upcy.xcodeproj/project.pbxproj | 12 +- package.json | 1 + src/common/PhotoOptions.tsx | 154 ++++++++++++++++++ .../Home/Quotation/QuotationForm.tsx | 97 +++++++++-- yarn.lock | 5 + 6 files changed, 279 insertions(+), 18 deletions(-) create mode 100644 src/common/PhotoOptions.tsx diff --git a/ios/Podfile.lock b/ios/Podfile.lock index ed76ac7..edc91c0 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -944,12 +944,24 @@ PODS: - React-Mapbuffer (0.73.2): - glog - React-debug + - react-native-image-picker (7.1.0): + - glog + - RCT-Folly (= 2022.05.16.00) + - React-Core - react-native-pager-view (6.2.3): - glog - RCT-Folly (= 2022.05.16.00) - React-Core - react-native-safe-area-context (4.8.2): - React-Core + - react-native-slider (4.5.0): + - glog + - RCT-Folly (= 2022.05.16.00) + - React-Core + - react-native-webview (13.7.1): + - glog + - RCT-Folly (= 2022.05.16.00) + - React-Core - React-nativeconfig (0.73.2) - React-NativeModulesApple (0.73.2): - glog @@ -1190,8 +1202,11 @@ DEPENDENCIES: - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector-modern`) - React-logger (from `../node_modules/react-native/ReactCommon/logger`) - React-Mapbuffer (from `../node_modules/react-native/ReactCommon`) + - react-native-image-picker (from `../node_modules/react-native-image-picker`) - react-native-pager-view (from `../node_modules/react-native-pager-view`) - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) + - "react-native-slider (from `../node_modules/@react-native-community/slider`)" + - react-native-webview (from `../node_modules/react-native-webview`) - React-nativeconfig (from `../node_modules/react-native/ReactCommon`) - React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`) - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`) @@ -1291,10 +1306,16 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/logger" React-Mapbuffer: :path: "../node_modules/react-native/ReactCommon" + react-native-image-picker: + :path: "../node_modules/react-native-image-picker" react-native-pager-view: :path: "../node_modules/react-native-pager-view" react-native-safe-area-context: :path: "../node_modules/react-native-safe-area-context" + react-native-slider: + :path: "../node_modules/@react-native-community/slider" + react-native-webview: + :path: "../node_modules/react-native-webview" React-nativeconfig: :path: "../node_modules/react-native/ReactCommon" React-NativeModulesApple: @@ -1363,7 +1384,7 @@ SPEC CHECKSUMS: Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9 FlipperKit: 37525a5d056ef9b93d1578e04bc3ea1de940094f fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 - glog: 035f1e36e53b355cf70f6434d161b36e7d21fecd + glog: c5d68082e772fa1c511173d6b30a9de2c05a69a2 hermes-engine: 34df9d5034e90bd9bf1505e1ca198760373935af libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c @@ -1388,8 +1409,11 @@ SPEC CHECKSUMS: React-jsinspector: 03644c063fc3621c9a4e8bf263a8150909129618 React-logger: 66b168e2b2bee57bd8ce9e69f739d805732a5570 React-Mapbuffer: 9ee041e1d7be96da6d76a251f92e72b711c651d6 + react-native-image-picker: 6c51359eca7a7df9f07e297218c25696eb9da976 react-native-pager-view: d81ab2060b9caf57ca8c3a0d57467ff407cdb825 react-native-safe-area-context: 0ee144a6170530ccc37a0fd9388e28d06f516a89 + react-native-slider: 7d387c7e8dd0b4c12bf49c975c8666435f082a33 + react-native-webview: d35380fcc2e1385cebc5b90fb96eebcdd1f2548e React-nativeconfig: d753fbbc8cecc8ae413d615599ac378bbf6999bb React-NativeModulesApple: 964f4eeab1b4325e8b6a799cf4444c3fd4eb0a9c React-perflogger: 29efe63b7ef5fbaaa50ef6eaa92482f98a24b97e @@ -1420,4 +1444,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: eb490ed861c0caf2f2649ac9503532c4cce2d3d8 -COCOAPODS: 1.12.1 +COCOAPODS: 1.14.3 diff --git a/ios/upcy.xcodeproj/project.pbxproj b/ios/upcy.xcodeproj/project.pbxproj index 54f03a5..cda84ee 100644 --- a/ios/upcy.xcodeproj/project.pbxproj +++ b/ios/upcy.xcodeproj/project.pbxproj @@ -690,7 +690,11 @@ "-DFOLLY_USE_LIBCPP=1", "-DFOLLY_CFG_NO_COROUTINES=1", ); - OTHER_LDFLAGS = "$(inherited)"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-Wl", + "-ld_classic", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; @@ -759,7 +763,11 @@ "-DFOLLY_USE_LIBCPP=1", "-DFOLLY_CFG_NO_COROUTINES=1", ); - OTHER_LDFLAGS = "$(inherited)"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-Wl", + "-ld_classic", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; diff --git a/package.json b/package.json index 4ec9718..c93a272 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "react-native-collapsible-tab-view": "^6.2.1", "react-native-dotenv": "^3.4.9", "react-native-gesture-handler": "^2.14.0", + "react-native-image-picker": "^7.1.0", "react-native-keychain": "^8.1.2", "react-native-pager-view": "^6.2.3", "react-native-pell-rich-editor": "^1.9.0", diff --git a/src/common/PhotoOptions.tsx b/src/common/PhotoOptions.tsx new file mode 100644 index 0000000..c71fcdf --- /dev/null +++ b/src/common/PhotoOptions.tsx @@ -0,0 +1,154 @@ +import React, { + Dispatch, + ReactElement, + ReactNode, + SetStateAction, + useCallback, +} from 'react'; +import { Alert, TouchableOpacity, View, Text } from 'react-native'; +import * as ImagePicker from 'react-native-image-picker'; +import styled from 'styled-components/native'; +import PhotoIcon from '../assets/common/Photo.svg'; +import { Body14M } from '../styles/GlobalText'; +import { LIGHTGRAY } from '../styles/GlobalColor'; + +const PhotosInput = styled.View` + display: flex; + flex-flow: row wrap; +`; + +interface Action { + title: string; + type: 'capture' | 'library'; + options: ImagePicker.CameraOptions | ImagePicker.ImageLibraryOptions; +} + +interface PhotoProps { + buttonLabel: string; + photo: any; + setPhoto: Dispatch>; + max: number; +} + +export interface PhotoResultProps { + fileName: string; + width: number; + height: number; + uri: string; +} + +const PhotoOptions = ({ buttonLabel, photo, setPhoto, max }: PhotoProps) => { + const CameraActions: Action[] = [ + //카메라 & 갤러리 세팅 + { + title: "카메라", + type: "capture", + options: { + selectionLimit: max, + mediaType: "photo", + includeBase64: false, + maxHeight: 300, + maxWidth: 300, + }, + }, + { + title: "앨범", + type: "library", + options: { + selectionLimit: max, + mediaType: "photo", + includeBase64: false, + maxHeight: 300, + maxWidth: 300, + }, + }, + ]; + // const onButtonPress = useCallback( + // ( + // type: string, + // options: ImagePicker.CameraOptions | ImagePicker.ImageLibraryOptions + // ) => { + // //카메라 & 갤러리 열기 + // if (type === "capture") { + // ImagePicker.launchCamera(options, (response) => + // setPhoto([...photo, response.assets]) + // ); + // } else { + // ImagePicker.launchImageLibrary(options, (response) => + // setPhoto([...photo, response.assets]) + // ); + // } + // }, + // [] + // ); + + const onButtonPress = useCallback( + ( + type: string, + options: ImagePicker.CameraOptions | ImagePicker.ImageLibraryOptions + ) => { + if (type === "capture") { + ImagePicker.launchCamera(options, (response) => { + if (!response.didCancel) { + const selectedPhotos = response.assets!.map((asset) => ({ + fileName: asset.fileName, + width: asset.width, + height: asset.height, + uri: asset.uri, + })); + setPhoto((prevPhotos: any[]) => prevPhotos.concat(selectedPhotos)); + } + }); + } else { + ImagePicker.launchImageLibrary(options, (response) => { + if (!response.didCancel) { + const selectedPhotos = response.assets!.map((asset) => ({ + fileName: asset.fileName, + width: asset.width, + height: asset.height, + uri: asset.uri, + })); + setPhoto((prevPhotos: any[]) => prevPhotos.concat(selectedPhotos)); + } + }); + } + }, + [] + ); + + return ( + + { + Alert.alert("사진 선택", "", [ + { + text: "카메라", + onPress: () => onButtonPress(CameraActions[0].type, CameraActions[0].options) + }, + { + text: "앨범", + onPress: () => onButtonPress(CameraActions[1].type, CameraActions[1].options) + }, + { text: "취소", style: "destructive" }, + ]); + }} + > + + {buttonLabel} + + + ); +} + +const PhotoButton = styled.TouchableOpacity` + display: flex; + flex-direction: row; + align-items: center; + padding-horizontal: 16px; + background: ${LIGHTGRAY}; + border-radius: 6px; + margin-bottom: 20px; +` + +export default PhotoOptions; \ No newline at end of file diff --git a/src/components/Home/Quotation/QuotationForm.tsx b/src/components/Home/Quotation/QuotationForm.tsx index 76ed82d..c988c54 100644 --- a/src/components/Home/Quotation/QuotationForm.tsx +++ b/src/components/Home/Quotation/QuotationForm.tsx @@ -1,4 +1,4 @@ -import { SetStateAction, useState, Dispatch } from 'react'; +import { SetStateAction, useState, Dispatch, useEffect } from 'react'; import { ScrollView, View, Text, TouchableOpacity, ImageBackground } from 'react-native'; import styled from 'styled-components/native'; import { BLACK, LIGHTGRAY, PURPLE } from '../../../styles/GlobalColor'; @@ -16,6 +16,8 @@ import { HomeStackParams } from '../../../pages/Home'; import Arrow from '../../../assets/common/Arrow.svg'; import Search from '../../../assets/common/Search.svg'; import Photo from '../../../assets/common/Photo.svg'; +import PhotoOptions, { PhotoResultProps } from '../../../common/PhotoOptions'; +import Carousel from '../../../common/Carousel'; const statusBarHeight = getStatusBarHeight(true); @@ -45,6 +47,21 @@ const QuotationForm = ({ navigation, route }: StackScreenProps(''); + const [photos, setPhotos] = useState([]); + const [refPhotos, setRefPhotos] = useState([]); + + // 한 줄에 2개씩 아이템 배치 + const splitArrayIntoPairs = (arr: any[], pairSize: number) => { + return arr.reduce((result, item, index) => { + if (index % pairSize === 0) { + result.push([]); + } + result[result.length - 1].push(item); + return result; + }, []); + }; + const splitPhotos = splitArrayIntoPairs(photos, 2); + const splitRefPhotos = splitArrayIntoPairs(refPhotos, 2); return ( @@ -65,25 +82,77 @@ const QuotationForm = ({ navigation, route }: StackScreenProps마켓 소개글 - - 견적서 작성 - - - 작업할 사진 첨부 - + + 견적서 작성 + {photos.length > 0 && + { + return ( + + {item.map((subItem: any) => ( + + + + ))} + + ) + }} + slider + /> + } + + + - - 추가 요청사항 - - - 참고 사진 첨부 - - + + 추가 요청사항 + {refPhotos.length > 0 && + { + return ( + + {item.map((subItem: any) => ( + + + + ))} + + ) + }} + slider + /> + } + + + + 포트폴리오 사용 가능 여부 diff --git a/yarn.lock b/yarn.lock index 6ad553a..85a50d9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6319,6 +6319,11 @@ react-native-gesture-handler@^2.14.0: lodash "^4.17.21" prop-types "^15.7.2" +react-native-image-picker@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/react-native-image-picker/-/react-native-image-picker-7.1.0.tgz#1a41cc45ccdc177c2ebf7c7accee0ceadc36abd8" + integrity sha512-An0hn2mwqjGAA2mbsXdHRTyoMMklGPT9stIjE2zvkegU7CdoFhowqvVHfnELJNZnfAiSQuIaeY//z0r1R0lsgw== + react-native-keychain@^8.1.2: version "8.1.2" resolved "https://registry.npmjs.org/react-native-keychain/-/react-native-keychain-8.1.2.tgz"