diff --git a/App.js b/App.js index 9263190..febe8e7 100644 --- a/App.js +++ b/App.js @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useState } from "react"; -import { Image } from "react-native"; +import { Image, View } from "react-native"; import * as SplashScreen from "expo-splash-screen"; import * as Font from "expo-font"; import { Asset } from "expo-asset"; @@ -46,7 +46,7 @@ export default function App() { await _loadResourcesAsync(); // Pre-load fonts, make any API calls you need to do here await Font.loadAsync({ - ArgonExtra: require("./assets/font/argon.ttf"), + ArgonExtra: require("./assets/font/Orbit-Regular.ttf"), }); } catch (e) { console.warn(e); @@ -73,12 +73,13 @@ export default function App() { } return ( - - - - - - - + + + + + + + + ); } diff --git a/app.json b/app.json index b1702aa..14032d9 100644 --- a/app.json +++ b/app.json @@ -3,10 +3,7 @@ "name": "Argon FREE React Native", "slug": "argon-free-react-native", "privacy": "public", - "platforms": [ - "ios", - "android" - ], + "platforms": ["ios", "android", "web"], "version": "1.7.1", "orientation": "portrait", "icon": "./assets/icon.png", @@ -18,9 +15,7 @@ "updates": { "fallbackToCacheTimeout": 0 }, - "assetBundlePatterns": [ - "**/*" - ], + "assetBundlePatterns": ["**/*"], "ios": { "supportsTablet": true }, diff --git a/assets/.DS_Store b/assets/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/assets/.DS_Store differ diff --git a/assets/font/Orbit-Regular.ttf b/assets/font/Orbit-Regular.ttf new file mode 100644 index 0000000..db9562d Binary files /dev/null and b/assets/font/Orbit-Regular.ttf differ diff --git a/assets/font/argon.ttf b/assets/font/argon.ttf deleted file mode 100755 index 4240bc9..0000000 Binary files a/assets/font/argon.ttf and /dev/null differ diff --git a/assets/imgs/argon-logo-onboarding.png b/assets/imgs/argon-logo-onboarding.png index 4350b4e..53ca2e9 100644 Binary files a/assets/imgs/argon-logo-onboarding.png and b/assets/imgs/argon-logo-onboarding.png differ diff --git a/assets/imgs/argon-logo-onboarding@2x.png b/assets/imgs/argon-logo-onboarding@2x.png index a59cc29..53ca2e9 100644 Binary files a/assets/imgs/argon-logo-onboarding@2x.png and b/assets/imgs/argon-logo-onboarding@2x.png differ diff --git a/assets/imgs/argon-logo.png b/assets/imgs/argon-logo.png index 2ca2750..0bd08a9 100644 Binary files a/assets/imgs/argon-logo.png and b/assets/imgs/argon-logo.png differ diff --git a/assets/imgs/argon-logo@2x.png b/assets/imgs/argon-logo@2x.png deleted file mode 100644 index 82e3fa8..0000000 Binary files a/assets/imgs/argon-logo@2x.png and /dev/null differ diff --git a/assets/imgs/backg1.png b/assets/imgs/backg1.png new file mode 100644 index 0000000..65ffb22 Binary files /dev/null and b/assets/imgs/backg1.png differ diff --git a/assets/imgs/backg2.png b/assets/imgs/backg2.png new file mode 100644 index 0000000..277858e Binary files /dev/null and b/assets/imgs/backg2.png differ diff --git a/assets/imgs/kakao.png b/assets/imgs/kakao.png new file mode 100644 index 0000000..c882acc Binary files /dev/null and b/assets/imgs/kakao.png differ diff --git a/assets/imgs/logo_sub.png b/assets/imgs/logo_sub.png new file mode 100644 index 0000000..66847de Binary files /dev/null and b/assets/imgs/logo_sub.png differ diff --git a/assets/splash.png b/assets/splash.png index 89a8eaf..62230fd 100644 Binary files a/assets/splash.png and b/assets/splash.png differ diff --git a/babel.config.js b/babel.config.js index c0d6e65..d69299a 100644 --- a/babel.config.js +++ b/babel.config.js @@ -9,7 +9,7 @@ module.exports = function (api) { extensions: [".tsx", ".ts", ".js", ".json"], }, ], - "react-native-reanimated/plugin", + ["react-native-reanimated/plugin"], ], }; }; diff --git a/components/DrawerItem.js b/components/DrawerItem.js index 36ba698..aedf237 100644 --- a/components/DrawerItem.js +++ b/components/DrawerItem.js @@ -10,7 +10,7 @@ class DrawerItem extends React.Component { const { title, focused } = this.props; switch (title) { - case "Home": + case "홈": return ( ); - case "Elements": + case "복용기록확인": return ( ); - case "Articles": + case "복용알람": return ( ); - case "Profile": + case "중독위험도": return ( ); - case "Account": + case "프로필": return ( ", + "&": "&", + """: '"', + "'": "'", + " ": " ", + "©": "©", + "®": "®", + // 필요한 추가 HTML 엔티티를 여기에 추가하세요. + }; + + let decodedString = Object.keys(entities).reduce((acc, entity) => { + const regex = new RegExp(entity, "g"); + return acc.replace(regex, entities[entity]); + }, str); + + // 문자열의 시작과 끝에 있는 따옴표 제거 + decodedString = decodedString.replace(/^"|"$/g, ""); + // 이스케이프된 줄바꿈 문자 처리 + decodedString = decodedString.replace(/\\n/g, "\n"); + + return decodedString; +} + +const DrugCard = ({ item }) => { + const { itemName, efficiency, warn, sideEffect, image, typeName } = item; + + // decodeHtmlEntity 함수를 적용하여 출력 + const renderTextWithNewLines = (text) => { + const formattedText = decodeHtmlEntity(text); + return formattedText.split("\n").map((line, index) => ( + + {line} + + )); + }; + + return ( + + {itemName} + {image && } + {efficiency && ( + <> + 효능 + {renderTextWithNewLines(efficiency)} + + )} + {warn && ( + <> + 주의 + {renderTextWithNewLines(warn)} + + )} + {sideEffect && ( + <> + 부작용 + {renderTextWithNewLines(sideEffect)} + + )} + {typeName && ( + <> + 위험분류 + {renderTextWithNewLines(typeName)} + + )} + + ); +}; + +const styles = StyleSheet.create({ + card: { + backgroundColor: "#fff", + borderRadius: 8, + padding: 20, + marginBottom: 20, + shadowColor: "#000", + shadowOffset: { + width: 0, + height: 2, + }, + shadowOpacity: 0.25, + shadowRadius: 3.84, + elevation: 5, + }, + title: { + fontSize: 18, + fontWeight: "bold", + marginBottom: 10, + }, + subtitle: { + fontSize: 16, + fontWeight: "bold", + marginTop: 10, + }, + text: { + fontSize: 14, + marginTop: 5, + }, + image: { + width: 100, + height: 100, + resizeMode: "cover", + marginBottom: 10, + }, +}); + +export default DrugCard; diff --git a/components/DrugRecordCard.js b/components/DrugRecordCard.js new file mode 100644 index 0000000..f12f135 --- /dev/null +++ b/components/DrugRecordCard.js @@ -0,0 +1,104 @@ +import React from "react"; +import { View, Text, Image, StyleSheet } from "react-native"; +import { FontAwesome } from '@expo/vector-icons'; + +const DrugRecordCard = ({ item }) => { + const { medicineName, dailyFrequency, duration, startDate, endDate, isActive, image, typeName } = item; + + const calculateDrugIntakeDates = (startDate, duration) => { + const intakeDates = []; + const start = new Date(startDate); + + for (let day = 0; day < duration; day++) { + const date = new Date(start); + date.setDate(start.getDate() + day); + + intakeDates.push(date.toLocaleDateString("ko-KR")); + } + + return intakeDates; + }; + + const drugIntakeDates = calculateDrugIntakeDates(startDate, duration); + + return ( + + + + {image && } + + + 이름 {medicineName} + + + {startDate} ~ {endDate} | 총{duration}일 + + 위험 분류 {typeName} + + + + ); +}; + +const styles = StyleSheet.create({ + card: { + padding: 20, + elevation: 5, + flexDirection: 'row', + alignItems: 'center', + }, + contentContainer: { + flexDirection: 'row', + alignItems: 'flex-start' + }, + imageContainer: { + marginRight: 10, + alignItems: 'center', + justifyContent: 'center', + }, + image: { + width: 80, + height: 60, + resizeMode: "cover", + borderRadius: 5 + }, + icon: { + width: 68, + height: 19, + marginTop: 5 + }, + statusText: { + marginTop: 8, + }, + textContainer: { + flex: 1, + alignItems: 'flex-start' + }, + title: { + fontSize: 18, + fontWeight: "bold", + marginBottom: 5, + }, + periodContainer: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: 5, + }, + calendarIcon: { + marginRight: 5, + }, + period: { + fontSize: 12, + marginBottom: 5, + }, + lightText: { + color: '#6a6c6c', + fontSize: 12, + }, + boldText: { + fontWeight: 'bold', + color: '#171717', + } +}); + +export default DrugRecordCard; \ No newline at end of file diff --git a/components/Header.js b/components/Header.js index 207c7e8..abc3885 100644 --- a/components/Header.js +++ b/components/Header.js @@ -3,6 +3,10 @@ import { withNavigation } from '@react-navigation/compat'; import { TouchableOpacity, StyleSheet, Platform, Dimensions } from 'react-native'; import { Button, Block, NavBar, Text, theme } from 'galio-framework'; +import { createBottomTabNavigator } from "@react-navigation/bottom-tabs"; + +const Tab = createBottomTabNavigator(); + import Icon from './Icon'; import Input from './Input'; import Tabs from './Tabs'; @@ -11,134 +15,14 @@ import argonTheme from '../constants/Theme'; const { height, width } = Dimensions.get('window'); const iPhoneX = () => Platform.OS === 'ios' && (height === 812 || width === 812 || height === 896 || width === 896); -const BellButton = ({isWhite, style, navigation}) => ( - navigation.navigate('Pro')}> - - - -); - -const BasketButton = ({isWhite, style, navigation}) => ( - navigation.navigate('Pro')}> - - -); - -const SearchButton = ({isWhite, style, navigation}) => ( - navigation.navigate('Pro')}> - - -); class Header extends React.Component { handleLeftPress = () => { const { back, navigation } = this.props; return (back ? navigation.goBack() : navigation.openDrawer()); } - renderRight = () => { - const { white, title, navigation } = this.props; - - if (title === 'Title') { - return [ - , - - ] - } - - switch (title) { - case 'Home': - return ([ - , - - ]); - case 'Deals': - return ([ - , - - ]); - case 'Categories': - return ([ - , - - ]); - case 'Category': - return ([ - , - - ]); - case 'Profile': - return ([ - , - - ]); - case 'Product': - return ([ - , - - ]); - case 'Search': - return ([ - , - - ]); - case 'Settings': - return ([ - , - - ]); - default: - break; - } - } - renderSearch = () => { - const { navigation } = this.props; - return ( - navigation.navigate('Pro')} - iconContent={} - /> - ); - } - renderOptions = () => { - const { navigation, optionLeft, optionRight } = this.props; - return ( - - - - - ); - } + renderTabs = () => { const { tabs, tabIndex, navigation } = this.props; const defaultTab = tabs && tabs[0] && tabs[0].id; @@ -157,8 +41,6 @@ class Header extends React.Component { if (search || tabs || options) { return ( - {search ? this.renderSearch() : null} - {options ? this.renderOptions() : null} {tabs ? this.renderTabs() : null} ); @@ -185,7 +67,6 @@ class Header extends React.Component { title={title} style={navbarStyles} transparent={transparent} - right={this.renderRight()} rightStyle={{ alignItems: 'center' }} left={ ; } return ; diff --git a/components/ImagePickerComponent.js b/components/ImagePickerComponent.js new file mode 100644 index 0000000..8fb2a4a --- /dev/null +++ b/components/ImagePickerComponent.js @@ -0,0 +1,121 @@ +import React, { useState, useEffect } from "react"; +import { TouchableOpacity, Text, View } from "react-native"; +import * as ImagePicker from "expo-image-picker"; +import Constants from "expo-constants"; +import axios from "axios"; +import { withNavigation } from "@react-navigation/compat"; + +const ImagePickerComponent = ({ navigation }) => { + const [selectedImage, setSelectedImage] = useState(null); + const [selectedImageData, setSelectedImageData] = useState(null); + + useEffect(() => { + (async () => { + if (Constants.platform.ios) { + const { status } = + await ImagePicker.requestMediaLibraryPermissionsAsync(); + if (status !== "granted") { + alert( + "죄송합니다. 이 기능을 사용하려면 카메라 롤 권한이 필요합니다!" + ); + } + } + })(); + }, []); + + // ImagePickerComponent + useEffect(() => { + if (selectedImageData) { + navigation.push("NewStackScreen", { + selectedImageData: selectedImageData, + }); + } + }, [selectedImageData]); + + const pickImageAndSend = async () => { + let result = await ImagePicker.launchImageLibraryAsync({ + mediaTypes: ImagePicker.MediaTypeOptions.Images, + allowsEditing: true, + aspect: [4, 3], + quality: 1, + }); + + if (!result.cancelled) { + setSelectedImage({ uri: result.uri }); + sendImageToBackend(result.uri); + } + }; + + const sendImageToBackend = async (imageUri) => { + const formData = new FormData(); + formData.append("imageFile", { + uri: imageUri, + type: "image/jpeg", + name: "photo.jpg", + }); + + try { + const response = await axios.post( + "http://34.64.161.34:8080/medicine", + formData, + { + headers: { + "Content-Type": "multipart/form-data", + }, + timeout: 1000000000, // 10초 시간 제한 설정 + } + ); + + + // Handle response from backend + console.log(response.data); + setSelectedImageData(response.data); + + // Open a new stack screen after image selection + navigation.push("NewStackScreen", { + selectedImageData: response.data, + }); + } catch (error) { + console.error(error); + } + }; + + return ( + + 이미지로 + 분석하기 + + ); +}; + +const styles = { + buttonContainer: { + flex: 1, + marginHorizontal: 5, // Adjust margin as needed + borderRadius: 15, // Add border radius for rounded corners + overflow: "hidden", // Ensure content stays within rounded borders + elevation: 5, // Add shadow elevation + }, + cameraButton: { + justifyContent: "center", + alignItems: "center", + paddingVertical: 15, // Adjust this value as needed to control the button height + borderRadius: 15, // Make it round + backgroundColor: "purple", + marginVertical: 20, + height: 100, // Fixed height for the button + }, + greenButton: { + backgroundColor: "#67B779" + }, + buttonText: { + fontSize: 25, + color: "white", + fontFamily: "ArgonExtra", + }, +}; + +export default withNavigation(ImagePickerComponent); diff --git a/components/KakaoLogin.js b/components/KakaoLogin.js new file mode 100644 index 0000000..b1c9b1c --- /dev/null +++ b/components/KakaoLogin.js @@ -0,0 +1,102 @@ +import React, { useState } from "react"; +import { + View, + Button, + Modal, + TouchableOpacity, + Text, + Image, +} from "react-native"; +import { WebView } from "react-native-webview"; +import axios from "axios"; + +const runFirst = `window.ReactNativeWebView.postMessage("this is message from web");`; + +const KakaoLogin = ({ navigation, onLoginSuccess }) => { + const [modalVisible, setModalVisible] = useState(false); + + const handleLoginButton = () => { + setModalVisible(true); + }; + + function LogInProgress(data) { + const exp = "code="; + var condition = data.indexOf(exp); + + if (condition !== -1) { + var request_code = data.substring(condition + exp.length); + console.log("Authorization Code:", request_code); + sendAuthCodeToBackend(request_code); + } + } + + const sendAuthCodeToBackend = async (authorizationCode) => { + var backend_url = "http://34.64.161.34:8080/login/kakao"; + + axios({ + method: "post", + url: backend_url, + data: { + authorizationCode: authorizationCode, + }, + }) + .then(function (response) { + console.log("Backend Response:", response); + // 카카오 로그인이 성공했을 때 콜백 함수 호출 + onLoginSuccess(); + }) + .catch(function (error) { + console.log("Backend Error:", error); + }); + }; + + return ( + + + + + { + setModalVisible(false); + }} + > + { + if ( + event.nativeEvent["url"].startsWith( + "http://34.64.161.34:8080/login/kakao" + ) + ) { + LogInProgress(event.nativeEvent["url"]); + setModalVisible(false); + } + }} + /> + + + ); +}; + +export default KakaoLogin; diff --git a/components/ModalComponent.js b/components/ModalComponent.js new file mode 100644 index 0000000..1b97cc7 --- /dev/null +++ b/components/ModalComponent.js @@ -0,0 +1,151 @@ +import React from "react"; +import { + View, + Text, + StyleSheet, + Modal, + Button, + TouchableOpacity, +} from "react-native"; +import RadioButton from "react-native-radio-button"; +import { Picker } from "@react-native-picker/picker"; +import DateTimePicker from "@react-native-community/datetimepicker"; + +const ModalComponent = (props) => { + return ( + + + + 복용 정보 입력 + {props.drugName} + + + 복용시작일: + + + + + 복용마감일: + + + + + 현재 복용중 여부: + + + Yes + + No + + + + + 하루 복용 횟수: + + + + + + + + + + + + @@ -67,7 +57,7 @@ class Onboarding extends React.Component { const styles = StyleSheet.create({ container: { - backgroundColor: theme.COLORS.BLACK + fontFamily: "ArgonExtra", }, padded: { paddingHorizontal: theme.SIZES.BASE * 2, @@ -76,24 +66,26 @@ const styles = StyleSheet.create({ zIndex: 2, }, button: { - width: width - theme.SIZES.BASE * 4, - height: theme.SIZES.BASE * 3, + width: 300, shadowRadius: 0, - shadowOpacity: 0 + shadowOpacity: 0, + backgroundColor: "#67B779", + alignItems: "center", + justifyContent: "center", }, logo: { - width: 200, - height: 60, + width: 194, + height: 210, zIndex: 2, - position: 'relative', - marginTop: '-50%' + position: "relative", + marginTop: "-50%", }, title: { - marginTop:'-5%' + marginTop: "-5%", }, subTitle: { - marginTop: 20 - } + marginTop: 20, + }, }); export default Onboarding; diff --git a/screens/Profile.js b/screens/Profile.js index e70150f..8b52bea 100644 --- a/screens/Profile.js +++ b/screens/Profile.js @@ -5,7 +5,7 @@ import { ScrollView, Image, ImageBackground, - Platform + Platform, } from "react-native"; import { Block, Text, theme } from "galio-framework"; @@ -29,7 +29,7 @@ class Profile extends React.Component { > @@ -68,7 +68,9 @@ class Profile extends React.Component { > 2K - Orders + + Orders + 10 - Photos + + Photos + 89 - Comments + + Comments + @@ -106,174 +112,12 @@ class Profile extends React.Component { - - - An artist of considerable range, Jessica name taken by - Melbourne … - - - - - - Album - - - - - - {Images.Viewed.map((img, imgIndex) => ( - - ))} - - + - {/* - - - - - - - - - - - - - - 2K - - Orders - - - - 10 - - Photos - - - - 89 - - Comments - - - - - - - Jessica Jones, 27 - - - San Francisco, USA - - - - - - - - An artist of considerable range, Jessica name taken by - Melbourne … - - - - - - Album - - - - - - - - {Images.Viewed.map((img, imgIndex) => ( - - ))} - - - - - */} ); } @@ -283,17 +127,17 @@ const styles = StyleSheet.create({ profile: { marginTop: Platform.OS === "android" ? -HeaderHeight : 0, // marginBottom: -HeaderHeight * 2, - flex: 1 + flex: 1, }, profileContainer: { width: width, height: height, padding: 0, - zIndex: 1 + zIndex: 1, }, profileBackground: { width: width, - height: height / 2 + height: height / 2, }, profileCard: { // position: "relative", @@ -307,36 +151,36 @@ const styles = StyleSheet.create({ shadowOffset: { width: 0, height: 0 }, shadowRadius: 8, shadowOpacity: 0.2, - zIndex: 2 + zIndex: 2, }, info: { - paddingHorizontal: 40 + paddingHorizontal: 40, }, avatarContainer: { position: "relative", - marginTop: -80 + marginTop: -80, }, avatar: { width: 124, height: 124, borderRadius: 62, - borderWidth: 0 + borderWidth: 0, }, nameInfo: { - marginTop: 35 + marginTop: 35, }, divider: { width: "90%", borderWidth: 1, - borderColor: "#E9ECEF" + borderColor: "#E9ECEF", }, thumb: { borderRadius: 4, marginVertical: 4, alignSelf: "center", width: thumbMeasure, - height: thumbMeasure - } + height: thumbMeasure, + }, }); export default Profile;