diff --git a/App.js b/App.js
index 9263190..e0210b8 100644
--- a/App.js
+++ b/App.js
@@ -1,10 +1,12 @@
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";
import { Block, GalioProvider } from "galio-framework";
import { NavigationContainer } from "@react-navigation/native";
+import { Provider } from 'react-redux';
+import store from './store/store'; // 스토어 파일의 위치에 맞게 경로를 조정하세요.
// Before rendering any navigation stack
import { enableScreens } from "react-native-screens";
@@ -46,7 +48,14 @@ 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"),
+ KoPubWorldBatang_Pro_Bold: require("./assets/font/KoPubWorld_Batang_Pro_Bold.otf"),
+ KoPubWorldBatang_Pro_Light: require("./assets/font/KoPubWorld_Batang_Pro_Light.otf"),
+ KoPubWorldBatang_Pro_Medium: require("./assets/font/KoPubWorld_Batang_Pro_Medium.otf"),
+ KoPubWorldDotum_Pro_Bold: require("./assets/font/KoPubWorld_Dotum_Pro_Bold.otf"),
+ KoPubWorldDotum_Pro_Light: require("./assets/font/KoPubWorld_Dotum_Pro_Light.otf"),
+ KoPubWorldDotum_Pro_Medium: require("./assets/font/KoPubWorld_Dotum_Pro_Medium.otf"),
+ Gugi: require("./assets/font/Gugi-Regular.ttf"),
});
} catch (e) {
console.warn(e);
@@ -73,12 +82,14 @@ 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/Gugi-Regular.ttf b/assets/font/Gugi-Regular.ttf
new file mode 100644
index 0000000..b500dbf
Binary files /dev/null and b/assets/font/Gugi-Regular.ttf differ
diff --git a/assets/font/KoPubWorld_Batang_Pro_Bold.otf b/assets/font/KoPubWorld_Batang_Pro_Bold.otf
new file mode 100644
index 0000000..9f14b15
Binary files /dev/null and b/assets/font/KoPubWorld_Batang_Pro_Bold.otf differ
diff --git a/assets/font/KoPubWorld_Batang_Pro_Light.otf b/assets/font/KoPubWorld_Batang_Pro_Light.otf
new file mode 100644
index 0000000..ed115b9
Binary files /dev/null and b/assets/font/KoPubWorld_Batang_Pro_Light.otf differ
diff --git a/assets/font/KoPubWorld_Batang_Pro_Medium.otf b/assets/font/KoPubWorld_Batang_Pro_Medium.otf
new file mode 100644
index 0000000..3e438b4
Binary files /dev/null and b/assets/font/KoPubWorld_Batang_Pro_Medium.otf differ
diff --git a/assets/font/KoPubWorld_Dotum_Pro_Bold.otf b/assets/font/KoPubWorld_Dotum_Pro_Bold.otf
new file mode 100644
index 0000000..3cd1a6b
Binary files /dev/null and b/assets/font/KoPubWorld_Dotum_Pro_Bold.otf differ
diff --git a/assets/font/KoPubWorld_Dotum_Pro_Light.otf b/assets/font/KoPubWorld_Dotum_Pro_Light.otf
new file mode 100644
index 0000000..ae977db
Binary files /dev/null and b/assets/font/KoPubWorld_Dotum_Pro_Light.otf differ
diff --git a/assets/font/KoPubWorld_Dotum_Pro_Medium.otf b/assets/font/KoPubWorld_Dotum_Pro_Medium.otf
new file mode 100644
index 0000000..0168953
Binary files /dev/null and b/assets/font/KoPubWorld_Dotum_Pro_Medium.otf 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/.DS_Store b/assets/imgs/.DS_Store
new file mode 100644
index 0000000..4cf4ba0
Binary files /dev/null and b/assets/imgs/.DS_Store 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/bg.png b/assets/imgs/bg.png
index 14840c4..d9e919b 100644
Binary files a/assets/imgs/bg.png and b/assets/imgs/bg.png differ
diff --git a/assets/imgs/bg2.png b/assets/imgs/bg2.png
new file mode 100644
index 0000000..14840c4
Binary files /dev/null and b/assets/imgs/bg2.png differ
diff --git a/assets/imgs/defaultpill.jpg b/assets/imgs/defaultpill.jpg
new file mode 100644
index 0000000..142932c
Binary files /dev/null and b/assets/imgs/defaultpill.jpg 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/imgs/profile-screen-bg.png b/assets/imgs/profile-screen-bg.png
index ee37fb0..d9e919b 100644
Binary files a/assets/imgs/profile-screen-bg.png and b/assets/imgs/profile-screen-bg.png differ
diff --git a/assets/imgs/profile-screen-bg2.png b/assets/imgs/profile-screen-bg2.png
new file mode 100644
index 0000000..ee37fb0
Binary files /dev/null and b/assets/imgs/profile-screen-bg2.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..f693ec6 100644
--- a/components/DrawerItem.js
+++ b/components/DrawerItem.js
@@ -1,58 +1,64 @@
import React from "react";
import { StyleSheet, TouchableOpacity, Linking } from "react-native";
import { Block, Text, theme } from "galio-framework";
+import { FontAwesome, MaterialIcons, Feather, MaterialCommunityIcons } from '@expo/vector-icons';
import Icon from "./Icon";
import argonTheme from "../constants/Theme";
class DrawerItem extends React.Component {
+
+ iconColor = "#67B779";
+
renderIcon = () => {
const { title, focused } = this.props;
+ const iconColor = focused ? "white" : this.iconColor;
+
switch (title) {
- case "Home":
+ case "홈":
return (
);
- case "Elements":
+ case "복용 기록 확인":
return (
);
- case "Articles":
+ case "복용 알람":
return (
);
- case "Profile":
+ case "중독 위험도":
return (
);
- case "Account":
+ case "프로필":
return (
);
case "Getting Started":
@@ -113,7 +119,7 @@ const styles = StyleSheet.create({
paddingHorizontal: 16
},
activeStyle: {
- backgroundColor: argonTheme.COLORS.ACTIVE,
+ backgroundColor: "#67B779",
borderRadius: 4
},
shadow: {
diff --git a/components/DrugCard.js b/components/DrugCard.js
new file mode 100644
index 0000000..65362cb
--- /dev/null
+++ b/components/DrugCard.js
@@ -0,0 +1,162 @@
+import React, { useState } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import {
+ ScrollView,
+ StyleSheet,
+ Text,
+ View,
+ Image,
+ Button,
+} from "react-native";
+import ModalComponent from "./ModalComponent";
+import { addRecord } from "../store/actions/recordActions";
+
+// decodeHtmlEntity 함수를 적용하여 출력
+const renderTextWithNewLines = (text) => {
+ const formattedText = decodeHtmlEntity(text);
+ return formattedText.split("\n").map((line, index) => (
+
+ {line}
+
+ ));
+};
+
+function decodeHtmlEntity(str) {
+ const entities = {
+ "<": "<",
+ ">": ">",
+ "&": "&",
+ """: '"',
+ "'": "'",
+ " ": " ",
+ "©": "©",
+ "®": "®",
+ // 필요한 추가 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;
+}
+
+
+// 컴포넌트의 props에서 item을 받아오도록 수정
+const DrugCard = ({ item }) => {
+ const { itemName, image, efficiency, warn, sideEffect, typeName } = item; // item 객체에서 필요한 데이터 추출
+ const accessToken = useSelector((state) => state.auth.accessToken); // Redux 스토어에서 accessToken 가져오기
+
+ const [modalVisible, setModalVisible] = useState(false);
+ const [startDate, setStartDate] = useState(new Date());
+ const [endDate, setEndDate] = useState(new Date());
+ const [isTaking, setIsTaking] = useState(true);
+ const [timesPerDay, setTimesPerDay] = useState(1);
+
+ const dispatch = useDispatch();
+
+ const onSubmit = (newRecord) => {
+ // newRecord 객체 구조를 확인하고 적절히 구성해야 함
+ dispatch(addRecord({ ...newRecord, accessToken })); // Redux 액션 디스패치
+ setModalVisible(false);
+ };
+
+ // 모달을 열고 닫는 함수
+ const toggleModal = () => setModalVisible(!modalVisible);
+
+ return (
+
+ {itemName}
+ {image && }
+ {efficiency && (
+ <>
+ 효능
+ {renderTextWithNewLines(efficiency)}
+ >
+ )}
+ {warn && (
+ <>
+ 주의
+ {renderTextWithNewLines(warn)}
+ >
+ )}
+ {sideEffect && (
+ <>
+ 부작용
+ {renderTextWithNewLines(sideEffect)}
+ >
+ )}
+ {typeName && (
+ <>
+ 위험분류
+ {renderTextWithNewLines(typeName)}
+ >
+ )}
+
+
+ setIsTaking(true)}
+ offTakingPress={() => setIsTaking(false)}
+ onTimesPerDayChange={setTimesPerDay}
+ // accessToken을 ModalComponent에 전달할 필요 없음
+ />
+
+ );
+};
+
+
+
+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..aaefc26
--- /dev/null
+++ b/components/DrugRecordCard.js
@@ -0,0 +1,79 @@
+import React, { useState } from "react";
+import {
+ Dimensions,
+ TouchableOpacity,
+ View,
+ Text,
+ Image,
+ StyleSheet,
+} from "react-native";
+import { useNavigation } from "@react-navigation/native";
+import DefaultImage from "../assets/imgs/defaultpill.jpg"; // Adjust the path as necessary
+
+const { width } = Dimensions.get("screen");
+const DrugRecordCard = ({ item }) => {
+ const navigation = useNavigation();
+ const [currentImageUri, setCurrentImageUri] = useState(item.image);
+ const { itemName, dailyFrequency, startDate, endDate, typeName } = item;
+
+ return (
+
+ navigation.navigate("MedicineDetail", { itemName: itemName })
+ }
+ >
+
+
+ setCurrentImageUri(null)} // 이미지 로딩에 에러가 발생하면 기본 이미지로 변경합니다.
+ />
+
+
+ 이름: {itemName}
+ 일일 투여 횟수: {dailyFrequency}
+ 시작일: {startDate}
+ 종료일: {endDate}
+ 분류: {typeName}
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ card: {
+ backgroundColor: "#FFFFFF",
+ borderRadius: 5,
+ padding: 10,
+ marginBottom: 10,
+ flexDirection: "row",
+ alignItems: "center",
+ },
+ contentContainer: {
+ flexDirection: "row",
+ alignItems: "center",
+ },
+ imageContainer: {
+ marginRight: 10,
+ },
+ image: {
+ width: 50,
+ height: 50,
+ borderRadius: 25,
+ },
+ textContainer: {
+ flex: 1,
+ },
+ title: {
+ fontSize: 16,
+ fontWeight: "bold",
+ },
+ text: {
+ fontSize: 14,
+ },
+});
+
+export default DrugRecordCard;
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..439b285
--- /dev/null
+++ b/components/ImagePickerComponent.js
@@ -0,0 +1,120 @@
+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://35.216.104.91: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: "Gugi",
+ },
+};
+
+export default withNavigation(ImagePickerComponent);
diff --git a/components/KakaoLogin.js b/components/KakaoLogin.js
new file mode 100644
index 0000000..70d84dc
--- /dev/null
+++ b/components/KakaoLogin.js
@@ -0,0 +1,109 @@
+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";
+import { useDispatch } from "react-redux";
+import { setAccessToken } from "../store/reducers/authActions";
+
+
+const runFirst = `window.ReactNativeWebView.postMessage("this is message from web");`;
+
+const KakaoLogin = ({ navigation, onLoginSuccess }) => {
+ const dispatch = useDispatch();
+ 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://35.216.104.91:8080/login/kakao";
+
+ axios({
+ method: "post",
+ url: backend_url,
+ data: {
+ authorizationCode: authorizationCode,
+ },
+ })
+ .then(function (response) {
+ console.log("Backend Response:", response);
+ // 여기서 액세스 토큰을 Redux 스토어에 저장합니다.
+ dispatch(setAccessToken(response.data.accessToken));
+ // 카카오 로그인이 성공했을 때 콜백 함수 호출
+ onLoginSuccess();
+ })
+ .catch(function (error) {
+ console.log("Backend Error:", error);
+ });
+ };
+
+ return (
+
+
+
+
+ {
+ setModalVisible(false);
+ }}
+ >
+ {
+ if (
+ event.nativeEvent["url"].startsWith(
+ "http://35.216.104.91: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..f1594dd
--- /dev/null
+++ b/components/ModalComponent.js
@@ -0,0 +1,169 @@
+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";
+import axios from "axios"; // 상단에 axios import 추가
+
+
+
+const ModalComponent = (props) => {
+ return (
+
+
+
+ 복용 정보 입력
+ {props.drugName}
+
+
+ 복용시작일:
+ {
+ const currentDate = selectedDate || props.startDate;
+ props.onStartDateChange(currentDate);
+ }}
+ />
+
+
+
+ 복용마감일:
+ {
+ const currentDate = selectedDate || props.endDate;
+ props.onEndDateChange(currentDate);
+ }}
+ />
+
+
+
+ 현재 복용중 여부:
+
+
+ Yes
+
+ No
+
+
+
+
+ 하루 복용 횟수:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ centeredView: {
+ flex: 1,
+ justifyContent: "center",
+ alignItems: "center",
+ marginTop: 22,
+ },
+ modalView: {
+ margin: 30,
+ backgroundColor: "white",
+ borderRadius: 20,
+ padding: 30,
+ alignItems: "center",
+ shadowColor: "#000",
+ shadowOffset: {
+ width: 0,
+ height: 2,
+ },
+ shadowOpacity: 0.25,
+ shadowRadius: 4,
+ elevation: 5,
+ },
+ modalTitle: {
+ marginBottom: 15,
+ textAlign: "center",
+ fontSize: 20,
+ fontWeight: "bold",
+ },
+ modalSubtitle: {
+ fontSize: 16,
+ fontWeight: "600",
+ marginRight: 10,
+ },
+ modalText: {
+ fontSize: 14,
+ marginBottom: 15,
+ },
+ datePicker: {
+ flexDirection: "row",
+ alignItems: "center",
+ marginBottom: 20,
+ },
+ rowContainer: {
+ flexDirection: "row",
+ alignItems: "center",
+ justifyContent: "space-between",
+ marginBottom: 20,
+ },
+ row: {
+ flexDirection: "row",
+ alignItems: "center",
+ },
+ radioText: {
+ fontSize: 14,
+ },
+ pickerContainer: {
+ flexDirection: "row",
+ alignItems: "center",
+ marginBottom: 10,
+ },
+ picker: {
+ height: 40,
+ width: 100,
+ fontSize: 16,
+ },
+});
+
+export default ModalComponent;
diff --git a/components/RiskRecordCard.js b/components/RiskRecordCard.js
new file mode 100644
index 0000000..48afff5
--- /dev/null
+++ b/components/RiskRecordCard.js
@@ -0,0 +1,65 @@
+// RiskRecordCard.js
+import React from "react";
+import { View, Text, StyleSheet } from "react-native";
+
+// HTML 엔티티를 디코딩하는 함수
+function decodeHtmlEntity(str) {
+ const entities = {
+ "<": "<",
+ ">": ">",
+ "&": "&",
+ """: '"',
+ "'": "'",
+ " ": " ",
+ "©": "©",
+ "®": "®",
+ // 필요한 추가 HTML 엔티티를 여기에 추가하세요.
+ };
+
+ return Object.keys(entities).reduce((acc, entity) => {
+ const regex = new RegExp(entity, "g");
+ return acc.replace(regex, entities[entity]);
+ }, str);
+}
+
+ // decodeHtmlEntity 함수를 적용하여 출력
+const renderTextWithNewLines = (text) => {
+ // 디코딩 후 앞뒤 따옴표 제거
+ const formattedText = decodeHtmlEntity(text).replace(/^"|"$/g, "");
+ return formattedText.split("\\n").map((line, index) => (
+
+ {line}
+
+ ));
+};
+
+
+
+const RiskRecordCard = ({ record }) => {
+ return (
+
+ {record.itemName}
+ {/* HTML 엔티티가 디코딩되고 \n으로 줄바꿈 처리된 경고 메시지를 렌더링 */}
+ {renderTextWithNewLines(record.warn)}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ card: {
+ backgroundColor: "#ffcccc", // 경고를 위한 연한 빨간색 배경
+ borderRadius: 5,
+ padding: 15,
+ marginBottom: 10,
+ },
+ title: {
+ fontSize: 16,
+ fontWeight: "bold",
+ },
+ warning: {
+ fontSize: 14,
+ marginTop: 5,
+ },
+});
+
+export default RiskRecordCard;
diff --git a/components/SubTitle.js b/components/SubTitle.js
new file mode 100644
index 0000000..8fdb37c
--- /dev/null
+++ b/components/SubTitle.js
@@ -0,0 +1,45 @@
+import { Block, Text, theme } from "galio-framework";
+import {
+ StyleSheet,
+ View
+} from "react-native";
+
+import React from 'react';
+import { FontAwesome } from '@expo/vector-icons';
+
+const SubTitle = ({ title, description, iconName }) => {
+ return (
+
+ {iconName && }
+
+ {title}
+ {description && {description}}
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ icon: {
+ marginRight: 10,
+ marginLeft: 10
+ },
+ textContainer: {
+ flex: 1,
+ },
+ title: {
+ marginBottom: 3,
+ fontSize: 20,
+ fontWeight: 'bold',
+ },
+ description: {
+ fontSize: 14,
+ color: 'gray',
+ },
+});
+
+export default SubTitle;
diff --git a/constants/Images.js b/constants/Images.js
index 0e9b512..b3c3e30 100644
--- a/constants/Images.js
+++ b/constants/Images.js
@@ -10,7 +10,8 @@ const iOSLogo = require("../assets/imgs/ios.png");
const androidLogo = require("../assets/imgs/android.png");
// internet imgs
-const ProfilePicture = 'https://images.unsplash.com/photo-1492633423870-43d1cd2775eb?fit=crop&w=1650&q=80';
+const ProfilePicture =
+ "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSVc-iKzJJkBNXh1z-evd3zoPYNzMtmzPcSfT6pUlCYhw&s";
const Viewed = [
'https://images.unsplash.com/photo-1501601983405-7c7cabaa1581?fit=crop&w=240&q=80',
diff --git a/log.txt b/log.txt
new file mode 100644
index 0000000..b1dbd51
Binary files /dev/null and b/log.txt differ
diff --git a/navigation/Menu.js b/navigation/Menu.js
index fda94f7..d5f0518 100644
--- a/navigation/Menu.js
+++ b/navigation/Menu.js
@@ -1,5 +1,6 @@
import { Block, Text, theme } from "galio-framework";
-import { Image, ScrollView, StyleSheet } from "react-native";
+import { View, Image, ScrollView, StyleSheet } from "react-native";
+import { Icon } from 'react-native-vector-icons/MaterialIcons';
import { DrawerItem as DrawerCustomItem } from "../components";
import Images from "../constants/Images";
@@ -13,14 +14,16 @@ function CustomDrawerContent({
state,
...rest
}) {
- const screens = ["Home", "Profile", "Account", "Elements", "Articles"];
+ const screens = ["홈", "복용 기록 확인", "프로필"];
+ const focusedColor = "#67B779";
+
return (
-
+
@@ -30,26 +33,12 @@ function CustomDrawerContent({
title={item}
key={index}
navigation={navigation}
- focused={state.index === index ? true : false}
+ focused={state.routeNames[state.index] === item}
+ focusedColor={focusedColor}
/>
);
})}
-
-
-
- DOCUMENTATION
-
-
-
+
diff --git a/navigation/Screens.js b/navigation/Screens.js
index db91d65..4aa146f 100644
--- a/navigation/Screens.js
+++ b/navigation/Screens.js
@@ -9,6 +9,8 @@ import { Block } from "galio-framework";
import CustomDrawerContent from "./Menu";
import Elements from "../screens/Elements";
// screens
+import Description from "../screens/Description";
+import MedicineDetailScreen from "../screens/MedicineDetailScreen";
import Home from "../screens/Home";
import Onboarding from "../screens/Onboarding";
import Pro from "../screens/Pro";
@@ -25,22 +27,30 @@ const Stack = createStackNavigator();
const Drawer = createDrawerNavigator();
const Tab = createBottomTabNavigator();
-function ElementsStack(props) {
+function ProfileStack(props) {
return (
(
-
+
),
- cardStyle: { backgroundColor: "#F8F9FE" },
+ cardStyle: { backgroundColor: "#FFFFFF" },
+ headerTransparent: true,
}}
/>
(
-
+
),
cardStyle: { backgroundColor: "#F8F9FE" },
}}
@@ -103,118 +120,69 @@ function ArticlesStack(props) {
);
}
-function ProfileStack(props) {
+export default function OnboardingStack(props) {
return (
(
-
- ),
- cardStyle: { backgroundColor: "#FFFFFF" },
- headerTransparent: true,
- }}
- />
- (
-
- ),
+ name="Onboarding"
+ component={Onboarding}
+ option={{
headerTransparent: true,
}}
/>
+
+
);
}
-
-function HomeStack(props) {
+// Updated DescriptionStack to include MedicineDetailScreen
+function DescriptionStack(props) {
return (
(
-
),
cardStyle: { backgroundColor: "#F8F9FE" },
}}
/>
+ {/* Add MedicineDetailScreen to the stack */}
(
),
- headerTransparent: true,
}}
/>
);
}
-export default function OnboardingStack(props) {
- return (
-
-
-
-
- );
-}
-
function AppStack(props) {
return (
-
- {
+ const dispatch = useDispatch();
+ // useSelector로 상태를 가져올 때 안전하게 접근하기 위해 기본값 설정
+ const { riskRecords = [], medicineRecords = [] } = useSelector(
+ (state) => state.records.records || {}
+ );
+
+ useEffect(() => {
+ dispatch(fetchRecords());
+ }, [dispatch]);
+
+ return (
+
+
+ {riskRecords.length > 0 && (
+ <>
+
+
+ {riskRecords.map((record, index) => (
+
+ ))}
+ >
+ )}
+
+
+
+ {medicineRecords.length === 0 ? (
+
+
+ 저장된 약물 정보가 없습니다.
+
+
+ 로그인 후 약물 분석을 진행해주세요.
+
+
+ ) : (
+ medicineRecords.map((record, index) => (
+
+ ))
+ )}
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ noRecordsContainer: {
+ flex: 1,
+ justifyContent: "center",
+ alignItems: "center",
+ marginTop: 20,
+ },
+ noRecordsText: {
+ fontSize: 16,
+ textAlign: "center",
+ },
+});
+
+export default Description;
diff --git a/screens/Elements.js b/screens/Elements.js
index 7a73d80..c91718f 100644
--- a/screens/Elements.js
+++ b/screens/Elements.js
@@ -363,10 +363,10 @@ class Elements extends React.Component {
Navigation
-
+
-
+
{
+ const { status } = await Camera.requestCameraPermissionsAsync();
+ this.setState({ hasPermission: status === "granted" });
+ };
+
+ openCamera = () => {
+ this.setState({ isCameraVisible: true });
+ };
+
+ closeCamera = () => {
+ this.setState({ isCameraVisible: false });
+ };
+
+ setType = () => {
+ this.setState((prevState) => ({
+ type:
+ prevState.type === Camera.Constants.Type.back
+ ? Camera.Constants.Type.front
+ : Camera.Constants.Type.back,
+ }));
+ };
+
+ takePicture = async () => {
+ if (this.camera) {
+ const options = { quality: 0.5, base64: true };
+ const photo = await this.camera.takePictureAsync(options);
+ this.setState({ photo, isCameraVisible: true });
+ }
+ };
+
+ savePhoto = async () => {
+ if (!this.state.photo) {
+ console.error("No photo to save.");
+ return;
+ }
+
+ console.log("Photo saved:", this.state.photo.uri); // base64 대신 uri를 사용합니다.
+
+ const formData = new FormData();
+ formData.append("imageFile", {
+ uri: this.state.photo.uri,
+ type: "image/jpeg",
+ name: "photo.jpg",
+ });
+
+ try {
+ const response = await fetch("http://35.216.104.91:8080/medicine", {
+ method: "POST",
+ body: formData,
+ headers: {
+ "Content-Type": "multipart/form-data",
+ },
+ });
+
+ if (response.ok) {
+ console.log("Photo successfully sent to backend!", response);
+ const data = await response.json(); // 서버로부터 분석 결과 데이터를 받아옵니다.
+ // 성공적으로 분석 결과를 받아왔다면, 해당 데이터를 NewStackScreen으로 넘겨줍니다.
+ this.props.navigation.push("NewStackScreen", {
+ selectedImageData: data,
+ }); // 수정된 부분
+ } else {
+ console.error("Failed to send photo to backend.", response);
+ throw new Error("No response received from the backend.");
+ }
+ } catch (error) {
+ console.error("Error while sending photo to backend:", error);
+ // 오류 발생 시, 오류 메시지와 함께 NewStackScreen으로 이동합니다.
+ this.props.navigation.push("NewStackScreen", {
+ error: "Backend submission failed",
+ });
+ }
+
+ // 사진을 서버로 전송한 후, 상태를 초기화합니다.
+ this.setState({ photo: null, isCameraVisible: false });
+ };
+
+ retakePicture = () => {
+ this.setState({ photo: null });
+ };
+
+ pickImageAndSend = async () => {
+ this.props.navigation.push("NewStackScreen", { data });
+ };
+
+ renderCameraModal = () => {
+ return (
+
+ {this.state.photo ? (
+
+
+
+
+ 다시찍기
+
+
+ 저장
+
+
+
+ ) : (
+ {
+ this.camera = ref;
+ }}
+ >
+
+
+ Flip
+
+
+
+ SNAP
+
+
+
+ )}
+
+ );
+ };
+
renderArticles = () => {
return (
-
-
-
-
-
-
-
-
+ contentContainerStyle={styles.articles}
+ >
+
+ {this.state.hasPermission && (
+
+ {this.state.hasPermission && (
+
+
+
+
+ 사진 촬영하여 분석하기
+
+
+
+
+ )}
+ {this.state.hasPermission && (
+
+
+
+ {
+ this.setState({ selectedImage });
+ this.props.navigation.push("NewStackScreen", {
+ selectedImage,
+ });
+ }}
+ />
+
+
+
+ )}
+
+ )}
- )
- }
+ );
+ };
render() {
+ const { navigation } = this.props;
+
return (
-
- {this.renderArticles()}
-
+
+
+ {() => (
+
+
+ {this.renderArticles()}
+ {this.renderCameraModal()}
+
+
+ )}
+
+
+
);
}
}
const styles = StyleSheet.create({
home: {
- width: width,
+ width,
},
articles: {
width: width - theme.SIZES.BASE * 2,
paddingVertical: theme.SIZES.BASE,
+ justifyContent: "flex-end"
+ },
+ bottomButtonsContainer: {
+ flexDirection: "row",
+ justifyContent: "space-around",
+ alignItems: "center",
+ marginVertical: 20,
+ },
+ buttonContainer: {
+ flex: 1,
+ marginHorizontal: 20, // Adjust margin as needed
+ marginTop: 60,
+ marginBottom: -70,
+ paddingVertical: 15,
+ borderRadius: 15, // Add border radius for rounded corners
+ overflow: "hidden", // Ensure content stays within rounded borders
+ shadowColor: "#000", // Shadow color
+ shadowOffset: {
+ width: 0,
+ height: 2,
+ },
+ shadowOpacity: 0.25, // Shadow opacity
+ shadowRadius: 3.84, // Shadow radius
+ elevation: 5, // Add shadow elevation for Android
+ },
+ cameraButton: {
+ justifyContent: "center",
+ alignItems: "center",
+ paddingVertical: 15,
+ borderRadius: 10, // Make it round
+ backgroundColor: "purple",
+ marginVertical: -5,
+ height: 100, // Fixed height for the button
+ },
+ greenButton: {
+ backgroundColor: "#67B779"
+ },
+ buttonText: {
+ fontSize: 25,
+ color: "white",
+ fontFamily: "Gugi",
},
});
diff --git a/screens/MedicineDetailScreen.js b/screens/MedicineDetailScreen.js
new file mode 100644
index 0000000..6f0cf1b
--- /dev/null
+++ b/screens/MedicineDetailScreen.js
@@ -0,0 +1,107 @@
+import React, { useEffect, useState } from "react";
+import {
+ View,
+ Text,
+ StyleSheet,
+ ScrollView,
+ ActivityIndicator,
+} from "react-native";
+import axios from "axios";
+import { useSelector } from "react-redux";
+
+function decodeHtmlEntity(str) {
+ const entities = {
+ "<": "<",
+ ">": ">",
+ "&": "&",
+ """: '"',
+ "'": "'",
+ " ": " ",
+ "©": "©",
+ "®": "®",
+ // Add more HTML entities if needed
+ };
+
+ return Object.keys(entities)
+ .reduce((acc, entity) => {
+ const regex = new RegExp(entity, "g");
+ return acc.replace(regex, entities[entity]);
+ }, str)
+ .replace(/^"|"$/g, "")
+ .replace(/\\n/g, "\n"); // Decoding and handling quotes and new lines
+}
+
+const MedicineDetailScreen = ({ route }) => {
+ const { itemName } = route.params;
+ const [medicineDetail, setMedicineDetail] = useState(null);
+ const accessToken = useSelector((state) => state.auth.accessToken); // Access token from Redux store
+
+ useEffect(() => {
+ const fetchMedicineDetail = async () => {
+ try {
+ const response = await axios.get(
+ `http://35.216.104.91:8080/record/${itemName}`,
+ {
+ headers: {
+ Authorization: `Bearer ${accessToken}`, // Include the access token in the header
+ },
+ }
+ );
+ setMedicineDetail(response.data);
+ } catch (error) {
+ console.log("Error fetching medicine detail:", error);
+ }
+ };
+ fetchMedicineDetail();
+ }, [itemName, accessToken]);
+
+ if (!medicineDetail) {
+ return ;
+ }
+
+ const renderInfoSection = (title, content) => {
+ if (!content) return null;
+ // Use the decodeHtmlEntity function to decode HTML entities in content
+ return (
+
+ {title}
+ {decodeHtmlEntity(content)}
+
+ );
+ };
+
+ return (
+
+ {medicineDetail.itemName}
+ {renderInfoSection("효능", medicineDetail.efficiency)}
+ {renderInfoSection("주의사항", medicineDetail.warn)}
+ {renderInfoSection("부작용", medicineDetail.sideEffect)}
+ {renderInfoSection("분류", medicineDetail.typeName)}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ padding: 20,
+ },
+ title: {
+ fontSize: 22,
+ fontWeight: "bold",
+ marginBottom: 20,
+ },
+ section: {
+ marginBottom: 20,
+ },
+ sectionTitle: {
+ fontSize: 18,
+ fontWeight: "bold",
+ marginBottom: 10,
+ },
+ sectionContent: {
+ fontSize: 16,
+ },
+});
+
+export default MedicineDetailScreen;
diff --git a/screens/NewStackScreen.js b/screens/NewStackScreen.js
new file mode 100644
index 0000000..24e0fc2
--- /dev/null
+++ b/screens/NewStackScreen.js
@@ -0,0 +1,70 @@
+import React, { useState, useLayoutEffect, useEffect } from "react";
+import { useNavigation, useRoute } from "@react-navigation/native";
+import {
+ StyleSheet,
+ Dimensions,
+ ScrollView,
+ Text,
+ Button,
+ View,
+} from "react-native";
+import { Block, theme } from "galio-framework";
+import { useSelector } from "react-redux"; // Import useSelector
+import ModalComponent from "../components/ModalComponent";
+import DrugCard from "../components/DrugCard";
+import axios from "axios";
+
+const { width } = Dimensions.get("screen");
+
+const NewStackScreen = () => {
+ const accessToken = useSelector((state) => state.auth.accessToken); // Access token from Redux store
+ const navigation = useNavigation();
+ const route = useRoute();
+ const selectedImageData = route.params.selectedImageData;
+
+ const [modalVisible, setModalVisible] = useState(false);
+ const [drugName, setDrugName] = useState("");
+ const [startDate, setStartDate] = useState(new Date());
+ const [endDate, setEndDate] = useState(new Date());
+ const [isTaking, setIsTaking] = useState(true);
+ const [timesPerDay, setTimesPerDay] = useState(1);
+
+
+ const renderCards = () => {
+ const data = selectedImageData || []; // selectedImageData 자체가 데이터입니다.
+
+ // 데이터가 2차원 배열이므로, flat 메서드를 사용해 1차원 배열로 만듭니다.
+ const flattenedData = data.flat();
+
+ // NewStackScreen 컴포넌트 내에서 DrugCard 렌더링 부분
+ return flattenedData.map((item, index) => {
+ const actualItem = Array.isArray(item) ? item[0] : item;
+ return (
+
+ ); // accessToken을 prop으로 추가
+ });
+ };
+
+ return (
+
+
+ {renderCards()}
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ home: {
+ width: width,
+ },
+ articles: {
+ width: width - theme.SIZES.BASE * 2,
+ paddingVertical: theme.SIZES.BASE,
+ },
+});
+
+export default NewStackScreen;
diff --git a/screens/Onboarding.js b/screens/Onboarding.js
index 983d550..a9f8d97 100644
--- a/screens/Onboarding.js
+++ b/screens/Onboarding.js
@@ -1,12 +1,15 @@
+// Onboarding.js
import React from "react";
import {
ImageBackground,
Image,
StyleSheet,
StatusBar,
- Dimensions
+ Dimensions,
} from "react-native";
import { Block, Button, Text, theme } from "galio-framework";
+import KakaoLogin from "../components/KakaoLogin";
+import * as Font from "expo-font";
const { height, width } = Dimensions.get("screen");
@@ -14,6 +17,11 @@ import argonTheme from "../constants/Theme";
import Images from "../constants/Images";
class Onboarding extends React.Component {
+ handleLoginSuccess = () => {
+ // 다음 화면으로 이동
+ this.props.navigation.navigate("App"); // 다음 화면의 이름으로 변경하세요.
+ };
+
render() {
const { navigation } = this.props;
@@ -21,43 +29,25 @@ class Onboarding extends React.Component {
-
-
-
-
-
-
-
-
- Design
-
-
-
-
- System
-
-
-
-
- Fully coded React Native components.
-
-
-
-
+
+
+
+
+
@@ -67,7 +57,7 @@ class Onboarding extends React.Component {
const styles = StyleSheet.create({
container: {
- backgroundColor: theme.COLORS.BLACK
+ fontFamily: 'KoPubWorldDotum_Pro_Bold',
},
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..3f47ae1 100644
--- a/screens/Profile.js
+++ b/screens/Profile.js
@@ -1,24 +1,61 @@
import React from "react";
+import { connect } from "react-redux"; // Redux 스토어에 연결하기 위해 필요
+import axios from "axios";
import {
StyleSheet,
Dimensions,
ScrollView,
Image,
ImageBackground,
- Platform
+ Platform,
} from "react-native";
import { Block, Text, theme } from "galio-framework";
-
import { Button } from "../components";
import { Images, argonTheme } from "../constants";
import { HeaderHeight } from "../constants/utils";
-const { width, height } = Dimensions.get("screen");
+import * as Font from "expo-font";
-const thumbMeasure = (width - 48 - 32) / 3;
+const { width, height } = Dimensions.get("screen");
class Profile extends React.Component {
+ state = {
+ profileImage: "",
+ nickname: "",
+ };
+
+ componentDidMount() {
+ this.fetchProfileData();
+ console.log("accessToken", this.props.accessToken);
+ }
+
+ fetchProfileData = async () => {
+ const { accessToken } = this.props; // Redux 스토어에서 액세스 토큰을 props로 받아옵니다.
+
+ if (!accessToken) {
+ console.log("No accessToken found. User is not logged in.");
+ return; // Early return if no accessToken is present
+ }
+
+ try {
+ const response = await axios.get("http://35.216.104.91:8080/profile", {
+ headers: {
+ Authorization: `Bearer ${accessToken}`, // 헤더에 액세스 토큰 추가
+ },
+ });
+ this.setState({
+ profileImage: response.data.profileImage,
+ nickname: response.data.nickname,
+ });
+ console.log("프로필 성공", response.data);
+ } catch (error) {
+ console.error("Error fetching data: ", error);
+ }
+ };
+
render() {
+ const { profileImage, nickname } = this.state;
+
return (
@@ -29,251 +66,46 @@ class Profile extends React.Component {
>
-
-
-
-
-
-
-
-
- 2K
-
- Orders
-
-
-
- 10
-
- Photos
-
-
-
- 89
-
- Comments
-
-
-
+
-
- Jessica Jones, 27
-
-
- San Francisco, USA
-
-
-
-
-
-
- An artist of considerable range, Jessica name taken by
- Melbourne …
+ {nickname || "로그인이 필요합니다"}
-
-
-
-
- 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) => (
-
- ))}
-
-
-
-
- */}
);
}
@@ -282,61 +114,60 @@ class Profile extends React.Component {
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,
},
profileCard: {
- // position: "relative",
padding: theme.SIZES.BASE,
marginHorizontal: theme.SIZES.BASE,
marginTop: 65,
- borderTopLeftRadius: 6,
- borderTopRightRadius: 6,
+ borderTopLeftRadius: 8,
+ borderTopRightRadius: 8,
+ borderRadius: 8,
backgroundColor: theme.COLORS.WHITE,
shadowColor: "black",
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,
+ fontFamily: 'KoPubWorldDotum_Pro_Bold',
},
divider: {
width: "90%",
borderWidth: 1,
- borderColor: "#E9ECEF"
+ borderColor: "#E9ECEF",
},
- thumb: {
- borderRadius: 4,
- marginVertical: 4,
- alignSelf: "center",
- width: thumbMeasure,
- height: thumbMeasure
- }
});
-export default Profile;
+// Redux 스토어의 상태를 컴포넌트의 props로 매핑하는 함수
+const mapStateToProps = (state) => ({
+ accessToken: state.auth.accessToken,
+});
+
+// connect 함수를 사용해 Redux 스토어와 Profile 컴포넌트를 연결
+export default connect(mapStateToProps)(Profile);
diff --git a/store/actions/recordActions.js b/store/actions/recordActions.js
new file mode 100644
index 0000000..21d7cd6
--- /dev/null
+++ b/store/actions/recordActions.js
@@ -0,0 +1,33 @@
+// actions/recordActions.js
+import { FETCH_RECORDS, ADD_RECORD } from "./types";
+import axios from "axios";
+
+// 약물 정보 불러오기 액션
+export const fetchRecords = () => async (dispatch, getState) => {
+ const response = await axios.get("http://35.216.104.91:8080/record", {
+ headers: {
+ Authorization: `Bearer ${getState().auth.accessToken}`,
+ },
+ });
+ dispatch({
+ type: FETCH_RECORDS,
+ payload: response.data,
+ });
+};
+
+// 약물 정보 추가 액션
+export const addRecord = (record) => async (dispatch, getState) => {
+ const response = await axios.post(
+ "http://35.216.104.91:8080/medicine/save",
+ record,
+ {
+ headers: {
+ Authorization: `Bearer ${getState().auth.accessToken}`,
+ },
+ }
+ );
+ dispatch({
+ type: ADD_RECORD,
+ payload: response.data,
+ });
+};
diff --git a/store/actions/types.js b/store/actions/types.js
new file mode 100644
index 0000000..53ecea7
--- /dev/null
+++ b/store/actions/types.js
@@ -0,0 +1,3 @@
+// actions/types.js
+export const FETCH_RECORDS = "FETCH_RECORDS";
+export const ADD_RECORD = "ADD_RECORD";
diff --git a/store/reducers/authActions.js b/store/reducers/authActions.js
new file mode 100644
index 0000000..04974a3
--- /dev/null
+++ b/store/reducers/authActions.js
@@ -0,0 +1,13 @@
+// src/store/actions/authActions.js
+export const setAccessToken = (token) => {
+ return {
+ type: "SET_ACCESS_TOKEN",
+ payload: token,
+ };
+};
+
+export const removeAccessToken = () => {
+ return {
+ type: "REMOVE_ACCESS_TOKEN",
+ };
+};
diff --git a/store/reducers/authReducer.js b/store/reducers/authReducer.js
new file mode 100644
index 0000000..901cfa7
--- /dev/null
+++ b/store/reducers/authReducer.js
@@ -0,0 +1,23 @@
+// src/store/reducers/authReducer.js
+const initialState = {
+ accessToken: null,
+};
+
+const authReducer = (state = initialState, action) => {
+ switch (action.type) {
+ case "SET_ACCESS_TOKEN":
+ return {
+ ...state,
+ accessToken: action.payload,
+ };
+ case "REMOVE_ACCESS_TOKEN":
+ return {
+ ...state,
+ accessToken: null,
+ };
+ default:
+ return state;
+ }
+};
+
+export default authReducer;
diff --git a/store/reducers/recordReducer.js b/store/reducers/recordReducer.js
new file mode 100644
index 0000000..6d313dd
--- /dev/null
+++ b/store/reducers/recordReducer.js
@@ -0,0 +1,23 @@
+// reducers/recordReducer.js
+import { FETCH_RECORDS, ADD_RECORD } from "../actions/types";
+
+const initialState = {
+ records: [],
+};
+
+export default function (state = initialState, action) {
+ switch (action.type) {
+ case FETCH_RECORDS:
+ return {
+ ...state,
+ records: action.payload,
+ };
+ case ADD_RECORD:
+ return {
+ ...state,
+ records: [...state.records, action.payload],
+ };
+ default:
+ return state;
+ }
+}
diff --git a/store/store.js b/store/store.js
new file mode 100644
index 0000000..f18a2f3
--- /dev/null
+++ b/store/store.js
@@ -0,0 +1,16 @@
+import { applyMiddleware, combineReducers } from "redux";
+import { configureStore } from '@reduxjs/toolkit';
+import { composeWithDevTools } from "redux-devtools-extension";
+import thunk from "redux-thunk";
+import authReducer from "./reducers/authReducer";
+import recordReducer from "./reducers/recordReducer";
+
+const store = configureStore({
+ reducer: {
+ auth: authReducer,
+ records: recordReducer,
+ },
+ // 필요한 경우 여기에 다른 설정을 추가할 수 있습니다.
+});
+
+export default store;