diff --git a/fastapi/translated_events.py b/fastapi/translated_events.py new file mode 100644 index 0000000..7063853 --- /dev/null +++ b/fastapi/translated_events.py @@ -0,0 +1,37 @@ +event_list = ["졸업식", "종업식", "소풍", "입학식", "수학여행", "현장체험학습", "방학식", "겨울방학", "여름방학", "봄방학", "개학", "전시회", "실기대회", + "미술대회", "수학올림피아드", "과학올림피아드", "신체검사", "예방접종", "수련회", "운동회", "견학", "답사", "민방위", "소방훈련", "개교기념일", "축제", + "졸업장 수여식"] +event_list_en = ["Graduation Ceremony", "Closing Ceremony", "Excursion", "Entrance Ceremony", "School Trip", + "Field Experience Study", "Vacation", "Winter Vacation", "Summer Vacation", "Spring Break", + "Opening Ceremony", "Exhibition", "Practical Competition", "Art Competition", + "Mathematics Olympiad", "Science Olympiad", "Physical Examination", "Vaccination", "Retreat", + "Sports Day", "Field Trip", "Exploration", "Civil defense", "Fire drill", "Anniversary of school", + "Festival", "Diploma Award Ceremony"] +event_list_th = ["พิธีสำเร็จการศึกษา", "พิธีปิด", "ทัศนศึกษา", "พิธีรับเข้าเรียน", "ทัศนศึกษา", + "การศึกษาประสบการณ์ภาคสนาม", + "พิธีเช้า", "วันหยุดฤดูหนาว", "วันหยุดฤดูร้อน", "วันหยุดฤดูใบไม้ผลิ", "โรงเรียนเริ่มต้น", "นิทรรศการ", + "การแข่งขันภาคปฏิบัติ", "การประกวดศิลปะ", "คณิตศาสตร์โอลิมปิก", "วิทยาศาสตร์โอลิมปิก", + "การตรวจร่างกาย", + "การฉีดวัคซีน", "หนี", "วันกีฬาสี", "ทัศนศึกษา", "สำรวจ", "ป้องกันภัยพลเรือน", "ซ้อมดับเพลิง", + "ครบรอบโรงเรียน", + "เทศกาล", "พิธีรับปริญญา"] +event_list_km = ["ពិធីចែកសញ្ញាបត្រ", "ពិធីបិទ", "ដំណើរកំសាន្ត", "ពិធីចូលរៀន", "ការធ្វើដំណើរសាលា", "ការសិក្សាបទពិសោធន៍", + "ពិធីអាហារពេលព្រឹក", "វិស្សមកាលរដូវរងារ", "វិស្សមកាលរដូវក្តៅ", "សម្រាកនិទាឃរដូវ", "សាលាចាប់ផ្តើម", + "ការតាំងពិព័រណ៍", + "ការប្រកួតប្រជែងជាក់ស្តែង", "ការប្រកួតសិល្បៈ", "គណិតវិទ្យាអូឡាំពិក", "វិទ្យាសាស្ត្រអូឡាំព្យាដ", + "ការប្រឡងរាងកាយ", + "ការចាក់វ៉ាក់សាំង", "សម្រាក", "ទិវាកីឡា", "ការធ្វើដំណើរវាល។ ", "ការរុករក", "ការការពារស៊ីវិល", + "សមយុទ្ធអគ្គីភ័យ", + "ខួបនៃសាលារៀន", "ពិធីបុណ្យ", "ពិធីបញ្ចប់ការសិក្សា"] +event_list_vi = ["Lễ tốt nghiệp", "Lễ tổng kết", "Chuyến tham quan", "Lễ nhập học", "Chuyến đi học", + "Nghiên cứu trải nghiệm thực tế", "Lễ ăn sáng", "Kỳ nghỉ đông", "Kỳ nghỉ hè", "Kỳ nghỉ xuân", + "Khai giảng", "Triển lãm", "Cuộc thi thực hành", "Cuộc thi nghệ thuật", "Olympic Toán học", + "Olympic Khoa học", "Kiểm tra thể chất", "Tiêm phòng", "Khóa tu", "Ngày hội thể thao", + "Chuyến dã ngoại ", " Khám phá ", " Phòng thủ dân sự ", " Diễn tập chữa cháy ", + " Lễ kỷ niệm thành lập trường ", " Lễ hội ", " Lễ tốt nghiệp "] +event_list_ja = ["卒業式", "従業式", "ピクニック", "入学式", "修学旅行", "現場体験学習", "方程式", "冬休み", "夏休み", "春休み", + "開校", "展示会", "実技大会", "美術大会", "数学オリンピアード", "科学オリンピアド", "身体検査", "予防接種", + "修練会", "運動会", "見学", "回答", "民防衛", "消防訓練", "開校記念日", "祭り", "卒業場授与式"] +event_list_zh = ["毕业典礼", "结业典礼", "远足", "入学典礼", "学校旅行", "实地体验学习", "早餐仪式", "寒假", "暑假", "春假", + "开学", "展览", "实践比赛", "艺术竞赛", "数学奥林匹克", "科学奥林匹克", "体格检查", "疫苗接种", "撤退", + "运动项目", "实地考察", "探索", "民防", "消防演习", "奠基日", "节日", "毕业典礼"] diff --git a/react-native/App.tsx b/react-native/App.tsx index e6b06ca..05b1e12 100644 --- a/react-native/App.tsx +++ b/react-native/App.tsx @@ -14,7 +14,6 @@ import JoinScreen from './screens/JoinScreen'; import HomeScreen from './screens/HomeScreen'; import TranslateScreen from './screens/TranslateScreen'; import SearchScreen from './screens/SearchScreen'; -import LogoutButton from './components/LogoutButton'; import SearchResultScreen from './screens/SearchResultScreen'; import IntrodcutionScreen from './screens/IntroductionScreen' @@ -76,10 +75,8 @@ export default function App() { component={HomeScreen} options={{ headerStyle: { backgroundColor: theme.colors.primary }, - title: "NotiNote", + title: "Home", headerBackVisible: false, - headerRight: () => , - headerTitle: (props) => ( // App Logo ( // App Logo + + ), }} /> ( // App Logo + + ), }} /> { - if (auth?.authData?.jwt_token && eventForm) { + if (auth?.authData?.access_token && eventForm) { console.log(eventForm, currentEvent); fetch(`http://localhost:8080/event/register?id=${currentEvent}`, { method: 'PUT', headers: { - 'JWT_TOKEN': auth.authData.jwt_token, + 'ACCESS-TOKEN': auth.authData.access_token, 'Content-Type': 'application/json;charset=UTF-8' }, body: JSON.stringify(eventForm), @@ -128,63 +128,11 @@ function BottomDrawer(props: BottomDrawerProps) { - {props.showKorean ? i18n.t('korean') : i18n.t('results')} + {props.showKorean ? i18n.t('korean') : i18n.t('translation')} - + - {props.isTranslateScreen && props.handleOpenSaveForm && - <> - - - - - - - {i18n.t('saveResults')} - - - - {i18n.t('child')} - - - - Title - setResultsForm({...resultsForm, ['title']: text})} - /> - - {i18n.t('helpertext')} - - - - - - - - - - - - - - } @@ -331,13 +279,62 @@ function BottomDrawer(props: BottomDrawerProps) { {props.isTranslateScreen && - - {i18n.t('close')} + + {i18n.t('retake')} - - {i18n.t('tryAgain')} - + {props.handleOpenSaveForm && + <> + + {i18n.t('save')} + + + + + {i18n.t('saveResults')} + + + + {i18n.t('child')} + + + + Title + setResultsForm({...resultsForm, ['title']: text})} + /> + + {i18n.t('helpertext')} + + + + + + + + + + + + + + } } @@ -392,9 +389,6 @@ const styles = StyleSheet.create({ highlighted: { backgroundColor: theme.colors.skyblue }, - rightSpace: { - paddingRight: 8 - }, full: { paddingBottom: 96 }, diff --git a/react-native/components/Home/HomeMenu.tsx b/react-native/components/Home/HomeMenu.tsx new file mode 100644 index 0000000..91fb934 --- /dev/null +++ b/react-native/components/Home/HomeMenu.tsx @@ -0,0 +1,40 @@ +import React from "react"; +import { View, Text, Image, TouchableOpacity } from "react-native"; +import { useAuth } from "../../contexts/Auth"; +import { useNavigation, StackActions } from '@react-navigation/native'; +import { Menu, MenuItem } from "react-native-material-menu"; + +export default function HomeMenu() { + const auth = useAuth(); + const navigation = useNavigation(); + const [isMenuVisible, setMenuVisibility] = React.useState(false); + + const showMenu = () => { + setMenuVisibility(true); + }; + const hideMenu = () => { + setMenuVisibility(false); + }; + + const logout = () => { + auth.signOut(); + navigation.dispatch(StackActions.popToTop()) + } + + return ( + + + + } + onRequestClose={hideMenu} + > + Logout + + ); +} diff --git a/react-native/components/Home/NoEventBox.tsx b/react-native/components/Home/NoEventBox.tsx new file mode 100644 index 0000000..c10747a --- /dev/null +++ b/react-native/components/Home/NoEventBox.tsx @@ -0,0 +1,28 @@ +import React from "react"; +import { View, Image, StyleSheet } from "react-native"; +import { Text } from 'native-base'; +import i18n from 'i18n-js' +import '../../locales/i18n'; + + +export default function NoEventBox() { + return ( + + + {i18n.t('noEvent')} + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + flexDirection: "column", + justifyContent: "center", + alignItems: "center", + }, + imageStyle: { + width: 180, + height: 180, + } +}); diff --git a/react-native/components/LogoutButton.tsx b/react-native/components/LogoutButton.tsx deleted file mode 100644 index 13213aa..0000000 --- a/react-native/components/LogoutButton.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React, { useState } from 'react'; -import { TouchableOpacity, Alert } from 'react-native'; -import { useNavigation, StackActions } from '@react-navigation/native'; -import { AntDesign } from '@expo/vector-icons'; -import { useAuth } from '../contexts/Auth'; - -const LogoutButton = () => { - const navigation = useNavigation(); - const auth = useAuth(); - const [showBox, setShowBox] = useState(true); - - const LogoutConfirm = () => { - return Alert.alert( - "Logout", - "Are you sure you want to log out?", [ - { - text: "Yes", - onPress: () => { - setShowBox(false); - auth.signOut(); - navigation.dispatch(StackActions.popToTop()) - }, - }, { - text: "No", - } - ] - ); - } - - return ( - - - - ); -}; -export default LogoutButton; diff --git a/react-native/locales/en.js b/react-native/locales/en.js index 57a26f0..9237d88 100644 --- a/react-native/locales/en.js +++ b/react-native/locales/en.js @@ -8,6 +8,7 @@ export default { start: 'Start NotiNote', /* Home */ translate: 'Translate', + translation: 'Translation', translateDesc: 'Translation and calendar registration are all possible just by taking a picture of the notice.', search: 'Search', searchDesc: 'You can find notices you have translated.', @@ -68,7 +69,7 @@ export default { tip_1: 'You can save the results and check them on the search screen!', registerFailed: 'Failed to add event. Please try again.', authFailed: "Authentication failed. Please try again.", - tryAgain: 'Try Again', + retake: 'Retake', join: 'Join', searchResult: "Search Result", noEvent: "There is no event today!" diff --git a/react-native/locales/ja.js b/react-native/locales/ja.js index 13bf367..4f1e25c 100644 --- a/react-native/locales/ja.js +++ b/react-native/locales/ja.js @@ -6,6 +6,7 @@ export default { swipe: 'スワイプして続行します ≫', start: 'NotiNoteを起動します', translate: '翻訳', + translation: '翻訳文', translateDesc: '通知の写真を撮るだけで、翻訳やカレンダー登録が可能です。', search: '検索', searchDesc: 'あなたが翻訳した通知を見つけることができます。', @@ -62,7 +63,7 @@ export default { gotIt: '了解しました', registerFailed: 'イベントの追加に失敗しました。 もう一度やり直してください。', authFailed: "認証に失敗しました。もう一度やり直してください。", - tryAgain: '再試行', + retake: '再撮影', join: '参加', searchResult: "検索結果", noEvent: "今日はイベントはありません!" diff --git a/react-native/locales/km.js b/react-native/locales/km.js index 04dd827..dcea50c 100644 --- a/react-native/locales/km.js +++ b/react-native/locales/km.js @@ -6,6 +6,7 @@ export default { swipe: 'អូសដើម្បីបន្ត ≫', start: 'ចាប់ផ្តើម NotiNote', translate: 'បកប្រែ', + translation: 'ការបកប្រែ', translateDesc: 'ការបកប្រែ និងការចុះឈ្មោះប្រតិទិនគឺអាចធ្វើទៅបានដោយគ្រាន់តែថតរូបការជូនដំណឹង។', search: 'ស្វែងរក', searchDesc: 'អ្នកអាចស្វែងរកការជូនដំណឹងដែលអ្នកបានបកប្រែ។', @@ -62,7 +63,7 @@ export default { gotIt: 'យល់ហើយ', registerFailed: 'បានបរាជ័យក្នុងការបន្ថែមព្រឹត្តិការណ៍។ សូម​ព្យាយាម​ម្តង​ទៀត។', authFailed: "ការផ្ទៀងផ្ទាត់បានបរាជ័យ។ សូមព្យាយាមម្តងទៀត", - tryAgain: 'ព្យាយាមម្តងទៀត', + retake: 'បាញ់ឡើងវិញ', join: 'ចូលរួម', searchResult: "លទ្ធផលស្វែងរក", noEvent: "មិនមានព្រឹត្តិការណ៍ថ្ងៃនេះទេ!" diff --git a/react-native/locales/ko.js b/react-native/locales/ko.js index 46cc752..477ca6e 100644 --- a/react-native/locales/ko.js +++ b/react-native/locales/ko.js @@ -6,6 +6,7 @@ export default { swipe: '계속하려면 넘겨주세요 ≫', start: 'NotiNote 시작하기', translate: '번역', + translation: '번역문', translateDesc: '가정통신문을 사진으로 찍기만 하면 번역부터 캘린더 등록까지 모두 가능합니다.', search: '검색', searchDesc: '이전에 번역한 가정통신문을 검색할 수 있습니다.', @@ -61,7 +62,7 @@ export default { gotIt: '확인', registerFailed: '이벤트를 추가하지 못했습니다. 다시 시도해 주세요.', authFailed: "인증에 실패했습니다. 다시 시도하십시오.", - tryAgain: '다시 시도', + retake: '다시 촬영', join: '가입', searchResult: "검색 결과", noEvent: "오늘은 이벤트가 없어요!" diff --git a/react-native/locales/th.js b/react-native/locales/th.js index 50e880f..e03c82a 100644 --- a/react-native/locales/th.js +++ b/react-native/locales/th.js @@ -6,6 +6,7 @@ export default { swipe: 'ปัดเพื่อดำเนินการต่อ ≫', start: 'เริ่ม NotiNote', translate: 'แปล', + translation: 'การแปล', translateDesc: 'การแปลและการลงทะเบียนปฏิทินทำได้เพียงแค่ถ่ายรูปประกาศ', ค้นหา: 'ค้นหา', searchDesc: 'คุณสามารถค้นหาประกาศที่คุณแปลได้', @@ -62,7 +63,7 @@ export default { gotIt: 'เข้าใจแล้ว', registerFailed: 'ไม่สามารถเพิ่มเหตุการณ์ได้ กรุณาลองอีกครั้ง.', authFailed: "การตรวจสอบสิทธิ์ล้มเหลว โปรดลองอีกครั้ง", - tryAgain: 'ลองอีกครั้ง', + retake: 'ถ่ายใหม่', join: 'เข้าร่วม', searchResult: "ผลการค้นหา", noEvent: "วันนี้ไม่มีกิจกรรม!" diff --git a/react-native/locales/vn.js b/react-native/locales/vn.js index f650bbd..9772815 100644 --- a/react-native/locales/vn.js +++ b/react-native/locales/vn.js @@ -6,6 +6,7 @@ export default { swipe: 'Vuốt để tiếp tục ≫', start: 'Bắt ​​đầu NotiNote', translate: 'dịch', + translation: 'dịch', translateDesc: 'Bạn có thể làm mọi thứ từ dịch thuật đến đăng ký lịch chỉ bằng cách chụp ảnh thư từ tại nhà.', search: 'tìm kiếm', searchDesc: 'Bạn có thể tìm kiếm thư từ trong gia đình đã được dịch trước đây.', @@ -61,7 +62,7 @@ export default { gotIt: 'Đã hiểu', registerFailed: 'Không thêm được sự kiện. Vui lòng thử lại.', authFailed: "Xác thực không thành công. Vui lòng thử lại.", - tryAgain: 'Thử lại', + retake: 'chụp lại', join: 'Tham gia', searchResult: "Kết quả tìm kiếm", noEvent: "Không có sự kiện hôm nay!" diff --git a/react-native/locales/zh.js b/react-native/locales/zh.js index 66a7b99..f4b8e96 100644 --- a/react-native/locales/zh.js +++ b/react-native/locales/zh.js @@ -6,6 +6,7 @@ export default { swipe: '滑动继续≫', start: '启动 NotiNote', translate: '翻译', + translation: '翻译', translateDesc: '您只需拍摄家庭信件的照片即可完成从翻译到日历登记的所有工作。', search: '搜索', searchDesc: '您可以搜索以前翻译的家庭信件。', @@ -61,7 +62,7 @@ export default { gotIt: '得到它', registerFailed: '添加事件失败。 请再试一次。', authFailed: "验证失败,请重试。", - tryAgain: '再试一次', + retake: '重拍', join: '加入', searchResult: "搜索结果", noEvent: "今天没有活动!" diff --git a/react-native/package.json b/react-native/package.json index 948689c..a5077c3 100644 --- a/react-native/package.json +++ b/react-native/package.json @@ -16,7 +16,8 @@ "@react-navigation/native": "^6.0.8", "@react-navigation/native-stack": "^6.5.0", "@types/i18n-js": "^3.8.2", - "axios": "^0.26.1", + "@types/react-native-material-menu": "^1.0.6", + "axios": "^0.27.2", "expo": "~44.0.0", "expo-app-loading": "~1.3.0", "expo-application": "^4.0.2", @@ -38,8 +39,11 @@ "react-dom": "17.0.1", "react-native": "0.64.3", "react-native-dotenv": "^3.3.1", + "react-native-element-dropdown": "^1.8.11", "react-native-elements": "^3.4.2", + "react-native-material-menu": "^2.0.0", "react-native-modal-datetime-picker": "^13.1.0", + "react-native-popup-menu": "^0.15.12", "react-native-safe-area-context": "3.3.2", "react-native-screens": "~3.10.1", "react-native-splash-screen": "^3.3.0", diff --git a/react-native/screens/HomeScreen.tsx b/react-native/screens/HomeScreen.tsx index f0676bb..52062bd 100644 --- a/react-native/screens/HomeScreen.tsx +++ b/react-native/screens/HomeScreen.tsx @@ -1,56 +1,81 @@ import React, { useEffect, useState } from 'react'; -import { StyleSheet, View, SafeAreaView, TouchableOpacity, Image, ImageBackground, Alert } from 'react-native'; -import { Text, Box } from 'native-base' -import { Ionicons } from '@expo/vector-icons'; +import { StyleSheet, View, SafeAreaView, TouchableOpacity, ImageBackground, Alert, Image } from 'react-native'; +import { Text } from 'native-base' import { theme } from '../core/theme'; import type { Navigation, UserData } from '../types'; import { useAuth } from '../contexts/Auth'; import { StackActions } from '@react-navigation/native'; - +import { MaterialIcons } from '@expo/vector-icons'; +import HomeMenu from '../components/Home/HomeMenu'; +import NoEventBox from '../components/Home/NoEventBox'; +import i18n from 'i18n-js' +import '../locales/i18n'; export default function HomeScreen({ navigation }: Navigation) { - const [events, setEvents] = useState<{event_num: number, children: { cid: number, cname: string, events: string[] }[]}>( + const cProfileImgSource = [require(`../assets/images/cprofile-images/profile-1.png`), require(`../assets/images/cprofile-images/profile-2.png`), require(`../assets/images/cprofile-images/profile-3.png`), + require(`../assets/images/cprofile-images/profile-4.png`), require(`../assets/images/cprofile-images/profile-5.png`), require(`../assets/images/cprofile-images/profile-6.png`), require(`../assets/images/cprofile-images/profile-7.png`), require(`../assets/images/cprofile-images/profile-8.png`), require(`../assets/images/cprofile-images/profile-9.png`)]; + const [events, setEvents] = useState<{event_num: number, children: { cid: number, cname: string, cprofileImg: number, events: string[] }[]}>( {event_num: 4, children: [ { cid: 1, cname: "Soo", + cprofileImg: 2, events: [ "the 17th Graduate Seremony", "Do-Dream Festival" - ] + ], }, { cid: 2, cname: "Hee", - events: [] + events: [], + cprofileImg: 1, } ] } ); - const [nowSelectedChildId, setNowSelectedChildId] = useState(1); - const [user, setUser] = useState(); + const SHOW_ALL = -1; + const [nowSelectedChildId, setNowSelectedChildId] = useState(SHOW_ALL); + const [user, setUser] = useState({ + uid: 1, + username: "Soo", + uemail: "kaithape@gmail.com", + uprofileImg: 1, + ulanguage: "english", + uchildren:[{ cid: 1, cname:"Soo", cprofileImg: 1 }, { cid: 2, cname:"Hee", cprofileImg: 4 }] + }); const auth = useAuth(); + useEffect(()=> { - setUser(auth?.userData); + if (auth?.userData) { + setUser(auth?.userData); + } - if (auth?.authData?.jwt_token) { + navigation.setOptions({ + headerRight: () => ( + + ) + }); + + if (auth?.authData?.access_token) { fetch('http://localhost:8080/user/children', { method: 'GET', headers: { - 'JWT_TOKEN': auth.authData.jwt_token + 'ACCESS-TOKEN': auth.authData.access_token }, redirect: 'follow' }) .then(response => response.json()) .then(data => { setEvents(data); - }) // console.log(data) + // console.log(data); + }) .catch((error) => { console.log(error) - if(error?.response?.status==401) { + if (error?.response?.status==401) { //redirect to login - Alert.alert("The session has expired. Please log in again."); + Alert.alert(i18n.t('sessionExpired')); auth.signOut(); navigation.dispatch(StackActions.popToTop()) } @@ -72,65 +97,69 @@ export default function HomeScreen({ navigation }: Navigation) { <>{ user && events && events.children?.length > 0 && ( - - - - - {"Hi, " + user.username + "!"} - You've got {events.event_num} events today. - - - + + + navigation.navigate('Translate')}> + + + {i18n.t('translate')} + + + + + navigation.navigate('Search')}> + + + {i18n.t('search')} + + + + + + - Today's Events + {i18n.t('todayEvent')} - {events.children?.map((notice, index) => + handleNowSelectedChildId(-1)}> + All + + {events.children?.map((child, index) => handleNowSelectedChildId(notice.cid)}> + backgroundColor: nowSelectedChildId === child.cid ? theme.colors.primary : "#ffffff", + }]} onPress={() => handleNowSelectedChildId(child.cid)}> + {notice.cname} + color: nowSelectedChildId !== child.cid ? theme.colors.primary : "#ffffff", + }]}>{child.cname} )} - {events.children.filter(child => child.cid === nowSelectedChildId).length > 0 && events.children.filter(child => child.cid === nowSelectedChildId)[0].events?.length ? ( - events.children?.filter(child => child.cid === nowSelectedChildId)[0].events?.map((item, index) => - - {/* {item.time} */} - {index+1 + '. ' + item} - - ) - ) : ( - - - There is no event today! - + {nowSelectedChildId === SHOW_ALL ? ( + events.children.reduce((prevValue, child) => prevValue + child.events.length, 0) > 0 ? ( + events.children.map((notice, index) => + + {notice.events.map((event, index) => { + return ( + {`[${notice.cname}] ` + event} + ) + })} + )) + : + ) : events.children.filter(child => child.cid === nowSelectedChildId)[0].events?.length ? ( + events.children?.filter(child => child.cid === nowSelectedChildId)[0].events?.map((item, index) => + + {index+1 + '. ' + item} + ) - } + ) : + } - - Functions - - navigation.navigate('Translate')}> - - - Translate - Translation, summarization, and calendar registration are all possible just by taking a picture of the notice. - - - - navigation.navigate('Search')}> - - - Search - You can find notices you have translated. - - - - )} ) @@ -179,13 +208,14 @@ const styles = StyleSheet.create({ childButton: { borderWidth: 1, borderColor: theme.colors.primary, - height: 30, - borderRadius: 16, - justifyContent: "center", + height: 40, + borderRadius: 32, + flexDirection: "row", + justifyContent: "space-between", alignItems: "center", paddingHorizontal: 16, alignSelf: 'flex-start', - marginRight: 8, + marginRight: 12, }, todayNoticeWrapper: { alignSelf: "flex-start", @@ -202,31 +232,36 @@ const styles = StyleSheet.create({ profielTextWrapper: { paddingRight: 30, }, + functionButtonImageBackground: { + flex: 1.16, + flexDirection: "row", + alignItems: "center", + }, functionButtonWrapper: { - flex: 1.5, - width: '88%', + flex: 1, paddingBottom: 30, + marginHorizontal: 20, + marginTop: -28 }, smallTitle: { - marginBottom: 8, + marginBottom: 0, }, buttonName: { fontSize: 24, }, bigButton: { - padding: 26, - marginBottom: 18, + padding: 34, + marginBottom: 20, borderRadius: 16, - shadowColor: "#999999", - shadowOpacity: 0.5, - shadowRadius: 8, - shadowOffset: { - height: 0, - width: 0, - }, + height: 100 + }, + bigButtonContentWrapper: { + flex:1, + flexDirection:'row', + justifyContent:'space-between' }, deepBlue: { - color: theme.colors.secondary, + color: "#333333", }, lightPink: { color: theme.colors.primary, @@ -236,5 +271,10 @@ const styles = StyleSheet.create({ flexDirection: 'column', justifyContent: 'center', alignItems: 'center', + }, + cprofileImage: { + width: 20, + height: 20, + marginRight: 12 } }) diff --git a/react-native/screens/IntroductionScreen.tsx b/react-native/screens/IntroductionScreen.tsx index e04d9bb..863daf9 100644 --- a/react-native/screens/IntroductionScreen.tsx +++ b/react-native/screens/IntroductionScreen.tsx @@ -1,126 +1,149 @@ -import React, { useEffect } from 'react'; -import type { Navigation } from '../types'; -import { StyleSheet, View, Image, SafeAreaView, TouchableHighlight, Alert } from 'react-native'; -import { Text } from 'native-base' -import { theme } from '../core/theme'; -import Swiper from 'react-native-swiper'; -import i18n from 'i18n-js' -import * as WebBrowser from 'expo-web-browser'; -import * as Google from 'expo-auth-session/providers/google'; +import React, { useEffect } from "react"; +import type { Navigation } from "../types"; +import { + StyleSheet, + View, + Image, + SafeAreaView, + TouchableHighlight, + Alert, +} from "react-native"; +import { Text } from "native-base"; +import { theme } from "../core/theme"; +import Swiper from "react-native-swiper"; +import i18n from "i18n-js"; +import * as WebBrowser from "expo-web-browser"; +import * as Google from "expo-auth-session/providers/google"; // import { GOOGLE_CLIENT_ID_WEB } from '@env'; -import { useAuth } from '../contexts/Auth'; -import '../locales/i18n'; +import { useAuth } from "../contexts/Auth"; +import "../locales/i18n"; WebBrowser.maybeCompleteAuthSession(); -const GOOGLE_CLIENT_ID_WEB='1044354965352-6qsilpb0i9ntmhbmktld5h68fphi85v5.apps.googleusercontent.com'; -const GOOGLE_CLIENT_ID_IOS='044354965352-j2jr4f1s04m3jhr2fikacbn5euhh8ndh.apps.googleusercontent.com'; +const GOOGLE_CLIENT_ID_WEB = + "1044354965352-6qsilpb0i9ntmhbmktld5h68fphi85v5.apps.googleusercontent.com"; +const GOOGLE_CLIENT_ID_IOS = + "044354965352-j2jr4f1s04m3jhr2fikacbn5euhh8ndh.apps.googleusercontent.com"; export default function HomeScreen({ navigation }: Navigation) { - const [request, response, promptAsync] = Google.useAuthRequest({ - expoClientId: GOOGLE_CLIENT_ID_WEB, - webClientId: GOOGLE_CLIENT_ID_WEB, - }) - const auth = useAuth(); + const [request, response, promptAsync] = Google.useAuthRequest({ + expoClientId: GOOGLE_CLIENT_ID_WEB, + webClientId: GOOGLE_CLIENT_ID_WEB, + }); + const auth = useAuth(); - useEffect(() => { - if (response?.type === 'success') { - const { authentication } = response; + // useEffect(() => { + // navigation.navigate("Home"); + // }) - if (authentication) { - auth.signIn(authentication?.accessToken); - } - else { - Alert.alert("Authentication failed. Please try again."); - } - } - else { - console.log('fail',response) - } - }, [response]); + useEffect(() => { + if (response?.type === "success") { + const { authentication } = response; - useEffect(() => { - if (auth?.userData?.uroleType === 'GUEST') { - navigation.navigate('Join'); - } else if (auth?.userData?.uroleType === 'USER') { - navigation.navigate('Home'); - } - }, [auth?.userData]); + if (authentication) { + auth.signIn(authentication?.accessToken); + } else { + Alert.alert("Authentication failed. Please try again."); + } + } else { + console.log("fail", response); + } + }, [response]); + + useEffect(() => { + if (auth?.userData?.uroleType === "GUEST") { + navigation.navigate("Join"); + } else if (auth?.userData?.uroleType === "USER") { + navigation.navigate("Home"); + } + }, [auth?.userData]); - return ( - - - - - - - {i18n.t('first_1')} - - Notinote - - {i18n.t('first_2')} - - - {i18n.t('swipe')} - - - - - {i18n.t('second')} - - {i18n.t('swipe')} - - - - - {i18n.t('third')} - - { - promptAsync(); - }} - > - - {i18n.t('start')} - - - - - - ) + return ( + + + + + + {i18n.t("first_1")} + Notinote + {i18n.t("first_2")} + + + + + + {i18n.t("second")} + + + + + + {i18n.t("third")} + + + + { + promptAsync(); + }} + > + + {i18n.t("start")} + + + + ); } const styles = StyleSheet.create({ - container: { - flex: 1, - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'center' - }, - imageStyle: { - width: 320, - height: 320 - }, - highlight: { - color: theme.colors.primary, - }, - swipe: { - marginTop: 100 - }, - description: { - width: "80%", - height: 100 - }, - startButton: { - width: "90%", - backgroundColor: theme.colors.primary, - padding: 10, - borderRadius: 8, - marginTop: 42 - }, - buttonStyle: { - textAlign: "center", - color: "white", - } -}) + container: { + flex: 1, + flexDirection: "column", + justifyContent: "center", + alignItems: "center", + }, + imageStyle: { + width: 320, + height: 320, + }, + highlight: { + color: theme.colors.primary, + }, + swipe: { + marginTop: 100, + }, + description: { + width: "80%", + height: 100, + }, + startButton: { + width: "90%", + backgroundColor: theme.colors.primary, + padding: 10, + borderRadius: 8, + marginTop: 20, + marginBottom: 32, + }, + buttonStyle: { + textAlign: "center", + color: "white", + }, + skipButton: { + width: "90%", + marginTop: 28, + }, + skipText: { + textAlign: "center", + color: theme.colors.primary, + }, +}); diff --git a/react-native/screens/JoinScreen.tsx b/react-native/screens/JoinScreen.tsx index 0ea78b7..e917c99 100644 --- a/react-native/screens/JoinScreen.tsx +++ b/react-native/screens/JoinScreen.tsx @@ -1,18 +1,23 @@ import React, { useState, useEffect } from 'react'; -import { StyleSheet, View, KeyboardAvoidingView, Alert, Platform, ScrollView, Image, GestureResponderEvent } from 'react-native'; -import { FormControl, Input, Button, VStack, Select, CheckIcon } from 'native-base'; +import { StyleSheet, KeyboardAvoidingView, Alert, Platform, ScrollView, Image, GestureResponderEvent, View, TouchableHighlight } from 'react-native'; +import { FormControl, Input, Button, VStack, Popover, Text, useToast, Box } from 'native-base'; +import { Dropdown } from 'react-native-element-dropdown'; import { nameValidator } from '../core/utils'; import type { Navigation, UserData, JoinData } from '../types'; import { theme } from '../core/theme'; import { useAuth } from '../contexts/Auth'; import i18n from 'i18n-js' import '../locales/i18n'; +import useFonts from '../hooks/useFonts'; +import AppLoading from 'expo-app-loading'; export default function JoinScreen({ navigation }: Navigation) { const [childrenNumber, setChildrenNumber] = useState('1'); - const imgSource = [require(`../assets/images/profile-images/profile-1.png`), require(`../assets/images/profile-images/profile-2.png`), require(`../assets/images/profile-images/profile-3.png`), + const uProfileImgSource = [require(`../assets/images/profile-images/profile-1.png`), require(`../assets/images/profile-images/profile-2.png`), require(`../assets/images/profile-images/profile-3.png`), require(`../assets/images/profile-images/profile-4.png`), require(`../assets/images/profile-images/profile-5.png`), require(`../assets/images/profile-images/profile-6.png`), require(`../assets/images/profile-images/profile-7.png`)]; + const cProfileImgSource = [require(`../assets/images/cprofile-images/profile-1.png`), require(`../assets/images/cprofile-images/profile-2.png`), require(`../assets/images/cprofile-images/profile-3.png`), + require(`../assets/images/cprofile-images/profile-4.png`), require(`../assets/images/cprofile-images/profile-5.png`), require(`../assets/images/cprofile-images/profile-6.png`), require(`../assets/images/cprofile-images/profile-7.png`), require(`../assets/images/cprofile-images/profile-8.png`), require(`../assets/images/cprofile-images/profile-9.png`)]; const colors = [{id: 1, hex: '#7986cb'}, {id: 3, hex: '#8e24aa'}, {id: 4, hex: '#e67c73'}, {id: 5, hex: '#f6bf26'}, {id: 7, hex: '#039be5'}, {id: 10, hex: '#0b8043'}] // 1 3 4 5 7 10 const [joinForm, setJoinForm] = useState({ @@ -20,18 +25,30 @@ export default function JoinScreen({ navigation }: Navigation) { uprofileImg: 1, username: '', ulanguage: '', - uchildren: colors.map(color => ({'cname': '', 'color': color?.id})) + uchildren: colors.map(color => ({ cname: '', cprofileImg: 1, color: color?.id })) }) + const [open, setOpen] = useState(-1); const [user, setUser] = useState(); const auth = useAuth(); + const toast = useToast(); + + const [fontsLoaded, SetFontsLoaded] = useState(false); + const LoadFontsAndRestoreToken = async () => { + await useFonts(); + }; useEffect(() => { if (auth?.userData?.uroleType==='USER') { - Alert.alert( - i18n.t('loginSuccess'), - i18n.t('loginSuccessText') - ) + toast.show({ // Design according to mui toast guidelines (https://material.io/components/snackbars#anatomy) + placement: "bottom", + render: () => { + return + 🎉  {i18n.t('loginSuccess')}  🎉 + {i18n.t('loginSuccessText')} + ; + } + }); navigation.navigate('Home'); } else if (auth?.userData?.uroleType==='GUEST') { @@ -45,6 +62,10 @@ export default function JoinScreen({ navigation }: Navigation) { } }, [user]); + useEffect(() => { + // console.log(joinForm); + }, [joinForm]); + const errorAlert = (error: string) => Alert.alert( i18n.t('joinFailed'), @@ -58,9 +79,21 @@ export default function JoinScreen({ navigation }: Navigation) { setJoinForm({ ...joinForm, ['uprofileImg']: profileType }); } - const handleChildren = (childNum: number, text: string) => { + const handleChildrenName = (childNum: number, value: string) => { + let array = joinForm?.uchildren; + if (array) { + array[childNum].cname = value; + } + setJoinForm({ ...joinForm, ['uchildren']: array }); + } + + const handleChildrenProfileImg = (childNum: number,value: number) => (event: GestureResponderEvent) => { let array = joinForm?.uchildren; - if (array) array[childNum].cname = text; + // console.log(array); + if (array) { + array[childNum].cprofileImg = value; + setOpen(-1); + } setJoinForm({ ...joinForm, ['uchildren']: array }); } @@ -70,30 +103,41 @@ export default function JoinScreen({ navigation }: Navigation) { let childrenArr = joinForm?.uchildren; childrenArr = childrenArr?.slice(0, Number(childrenNumber)); - joinForm.uchildren = childrenArr; const usernameError = nameValidator(joinForm.username); - const childrenNameError = joinForm.uchildren?.some(child => child.cname === ''); + const childrenNameError = childrenArr?.some(child => child.cname === ''); if (usernameError || childrenNameError || !joinForm.ulanguage) { errorAlert(i18n.t('fillAlarm')); return; } + joinForm.uchildren = childrenArr; + auth.signUp(joinForm); } }; + if (!fontsLoaded) { + return ( + SetFontsLoaded(true)} + onError={() => {}} + /> + ); + } + return ( - + - + {i18n.t('profileImage')} - {Array(7).fill(1).map((num, index) => - )} @@ -111,63 +155,114 @@ export default function JoinScreen({ navigation }: Navigation) { {i18n.t('selectLang')} - + { + setJoinForm({ ...joinForm, ['ulanguage']: item.value }) + }} + /> {i18n.t('childrenNum')} - + { + setChildrenNumber(item.value) + }} + /> - + {i18n.t('childrenName')} - - {Array(Number(childrenNumber)).fill(1).map((child, index) => + + {Array.from(Array(Number(childrenNumber)).keys()).map((child, index) => handleChildren(index, text)} + value={joinForm?.uchildren && joinForm.uchildren[child]?.cname} + onChangeText={(text) => handleChildrenName(child, text)} autoCapitalize="none" mb={2} InputRightElement={ - + setOpen(child)} + onClose={() => setOpen(-1)} + trigger={triggerProps => { + return + }} + > + + + + + {i18n.t('profileImage')} + + {Array.from(Array(Number(9)).keys()).map((num, i) => + + )} + + + + + + } /> )} - + + + {i18n.t("signUp")} + + ); @@ -175,19 +270,76 @@ export default function JoinScreen({ navigation }: Navigation) { const styles = StyleSheet.create({ container: { - paddingHorizontal: 25, - paddingVertical: 40, + paddingHorizontal: "5%", + paddingVertical: 30, backgroundColor: theme.colors.background, flex: 1, flexDirection: 'column', - justifyContent: 'center' + justifyContent: 'center', + alignContent: 'space-between' }, - profileImage: { + uprofileImage: { width: 52, height: 52, }, + cprofileImage: { + width: 32, + height: 32, + }, disabled: { opacity: 0.3 - } + }, + shadow: { + shadowColor: "#999999", + shadowOpacity: 0.5, + shadowRadius: 8, + shadowOffset: { + height: 0, + width: 0, + }, + }, + dropdown: { + height: 38, + borderColor: '#e5e5e5', + borderWidth: 0.6, + borderRadius: 5, + paddingHorizontal: 8, + marginTop: 1 + }, + label: { + position: 'absolute', + backgroundColor: 'white', + left: 22, + top: 8, + zIndex: 999, + paddingHorizontal: 8, + fontSize: 14, + fontFamily: 'Lora_400Regular', + }, + placeholderStyle: { + fontSize: 14, + fontFamily: 'Lora_400Regular', + color: '#a3a3a3' + }, + selectedTextStyle: { + fontSize: 14, + fontFamily: 'Lora_400Regular', + }, + inputSearchStyle: { + height: 36, + fontSize: 14, + fontFamily: 'Lora_400Regular', + }, + startButton: { + backgroundColor: theme.colors.primary, + padding: 10, + borderRadius: 8, + marginTop: 20, + marginBottom: 40, + }, + buttonStyle: { + textAlign: "center", + color: "white", + }, }); diff --git a/react-native/screens/SearchResultScreen.tsx b/react-native/screens/SearchResultScreen.tsx index a59a2b3..ede36c3 100644 --- a/react-native/screens/SearchResultScreen.tsx +++ b/react-native/screens/SearchResultScreen.tsx @@ -53,11 +53,11 @@ export default function SearchResultScreen(props: SearchResultScreenProps) { }] }) - if (auth?.authData?.jwt_token) { + if (auth?.authData?.access_token) { fetch(`http://localhost:8080/search/detail?date=${props.route.params.date}`, { method: 'GET', headers: { - 'JWT_TOKEN': auth.authData.jwt_token + 'ACCESS-TOKEN': auth.authData.access_token }, redirect: 'follow' }) diff --git a/react-native/screens/SearchScreen.tsx b/react-native/screens/SearchScreen.tsx index 8276a60..6958d1a 100644 --- a/react-native/screens/SearchScreen.tsx +++ b/react-native/screens/SearchScreen.tsx @@ -53,11 +53,11 @@ export default function SearchScreen({ navigation }: Navigation) { const [searchDate, setSearchDate] = useState(i18n.t('searchByDateDefault')); useEffect(() => { - if (auth?.authData?.jwt_token) { + if (auth?.authData?.access_token) { fetch('http://localhost:8080/search', { method: 'GET', headers: { - 'JWT_TOKEN': auth.authData.jwt_token + 'ACCESS-TOKEN': auth.authData.access_token }, redirect: 'follow' }) diff --git a/react-native/screens/TranslateScreen.tsx b/react-native/screens/TranslateScreen.tsx index 7d79b05..f99bc43 100644 --- a/react-native/screens/TranslateScreen.tsx +++ b/react-native/screens/TranslateScreen.tsx @@ -1,14 +1,12 @@ import React, { useState, useEffect } from 'react'; import { StyleSheet, View, TouchableOpacity, ImageBackground, Dimensions, Alert } from 'react-native'; +import { useToast, Box, Modal, Button, HStack, Text, Divider } from 'native-base'; import { Camera } from 'expo-camera'; import { Ionicons } from '@expo/vector-icons'; import { theme } from '../core/theme'; import type { Navigation, Result, ResultsForm } from '../types'; -import AppLoading from 'expo-app-loading'; -import useFonts from '../hooks/useFonts' import SwipeUpDown from 'react-native-swipe-up-down'; import BottomDrawer from '../components/BottomDrawer'; -import { useToast, Box } from 'native-base'; import mime from "mime"; import * as ImagePicker from 'expo-image-picker'; import { useAuth } from '../contexts/Auth'; @@ -27,23 +25,19 @@ const date = new Date(); export default function TranslateScreen({ navigation }: Navigation) { const [hasPermission, setHasPermission] = useState(false); - const [fontsLoaded, SetFontsLoaded] = useState(false); const [type, setType] = useState(Camera.Constants.Type.back); const [camera, setCamera] = useState(null); const [imageUri, setImageUri] = useState(''); - const [results, setResults] = useState({fullText: [], korean: '', trans_full: ''}); + const [results, setResults] = useState(); const [showKorean, setShowKorean] = useState(false); const [isFullDrawer, setFullDrawer] = useState(false); const [loading, setLoading] = useState(false); const [openSaveForm, setOpenSaveForm] = useState(false); + const [openInitialEventForm, setOpenInitialEventForm] = useState(false); const toast = useToast(); const auth = useAuth(); - const LoadFontsAndRestoreToken = async () => { - await useFonts(); - }; - useEffect(() => { (async () => { const { status } = await Camera.requestCameraPermissionsAsync(); @@ -55,20 +49,34 @@ export default function TranslateScreen({ navigation }: Navigation) { if (imageUri) { extractText } - }, [imageUri]) + }, [imageUri]); useEffect(() => { if (results?.fullText && results.fullText.filter(item => item.highlight === true).length > 0) { - const message = i18n.t('translateMessage_1') - toast.show({ // Design according to mui toast guidelines (https://material.io/components/snackbars#anatomy) - render: () => { - return - {message} - ; - } - }); + // const message = i18n.t('translateMessage_1') + // toast.show({ // Design according to mui toast guidelines (https://material.io/components/snackbars#anatomy) + // render: () => { + // return + // {message} + // ; + // } + // }); + if (results?.event_num) { + setOpenInitialEventForm(true); + } + else { + const message = "There are no extracted events!" + toast.show({ // Design according to mui toast guidelines (https://material.io/components/snackbars#anatomy) + placement: "top", + render: () => { + return + {message} + ; + } + }); + } } - }, [results]) + }, [results]); // DEV TEST // if (hasPermission === null) { @@ -78,16 +86,6 @@ export default function TranslateScreen({ navigation }: Navigation) { // return No access to camera! // } - if (!fontsLoaded) { - return ( - SetFontsLoaded(true)} - onError={() => {}} - /> - ); - } - const takePicture = async () => { if (camera) { const data = await camera.takePictureAsync(null); @@ -111,6 +109,7 @@ export default function TranslateScreen({ navigation }: Navigation) { const extractText = async(): Promise => { if (imageUri) { + // console.log(imageUri); let FormData = require('form-data'); const formdata = new FormData(); formdata.append("uploadfile", { @@ -119,22 +118,24 @@ export default function TranslateScreen({ navigation }: Navigation) { name: imageUri.split("/").pop() }); + console.log('ocr',formdata); + setLoading(true); - if (auth?.authData?.jwt_token) { + if (auth?.authData?.access_token) { await fetch("http://localhost:8080/notice/ocr", { method: 'POST', headers: { - 'JWT_TOKEN': auth.authData.jwt_token + 'ACCESS-TOKEN': auth.authData.access_token }, body: formdata, redirect: 'follow' }) .then(response => response.json()) .then(data => { - console.log(data) - setResults(data) - setLoading(false) + console.log(data); + setResults(data); + setLoading(false); }) .catch(function (error) { console.log(error?.response?.status) // 401 @@ -147,26 +148,31 @@ export default function TranslateScreen({ navigation }: Navigation) { } }); } - // TEST: mockup data -// setResults({ -// fullText: [ -// {id: 1, content: "1. Schedule of the closing ceremony and diploma presentation ceremony: Friday, January 4, 2019 at 9 o'clock for students to go to school.\n1) ", date: "", highlight: false, registered: false}, -// {id: 2, content: "Closing ceremony", date: "2022-01-04", highlight: true, registered: false}, -// {id: 3, content: ": 1st and 2nd graders, each classroom, 9:00-10:50 (no meals)\n2) ", date: "", highlight: false, registered: false}, -// {id: 4, content: "Diploma representation ceremony", date: "2022-01-04", highlight: true, registered: true}, -// {id: 5, content: ": 3rd grade, multi-purpose auditorium (2nd floor), 10:30-12:20\n2. School opening and entrance ceremony for new students: March 4th (Mon), 2019 at 9 o'clock for students to go to school.", date: "", highlight: false, registered: false}, -// ], -// korean: "가정통신문\n예당중학교\n8053-8388\n꿈은 크게. 마음은 넘게·\n행동은 바르게\n학부모님께\n희망찬 새해를 맞이하며 학부모님 가정에 건강과 행운이 함께 하시기를 기원 드립니다.\n드릴 말씀은, 2018학년도 종업식 및 졸업장 수여식과 2019학년도 개학 및 신입생 입학식을 다음과 같이 안내드리오니, 이후 3월 개학 때까지 학생들이 자기주도 학습 능력을 배양하고 다양한 체험 활동을 통하여 심신이 건강해지며 각종 유해 환경에 노출되지 않고 안전하고 줄거운 시간이 되도록 지도해 주시기 바랍니다.\n\ -// 1. 종업식 및 졸업장 수여식 일정 : 2019년 1월 4일(금), 학생 등교 9시\n\ -// 1) 종업식 : 1· 2학년, 각 교실, 9:00-10:50 (급식 없음)\n\ -// 2) 졸업장 수여식 : 3학년, 다목적 강당(2층), 10:30~12:20\n\ -// 2. 개학 및 신입생 입학식 : 2019년 3월 4일(월), 학생 등교 9시\n\ -// 1) 3월 4일 일정 : 월요일 정상수업 (급식 실시)\n\ -// (준비물: 교과서, 노트, 필기도구. 학생용 실내화(흰색) 등)\n\ -// 2) 신입생 입학식 : 다목적 강당(2층) 10시 30분, 신입생 등교 9시(신반 교실로 입장)\n", -// trans_full: '' -// }) } + + // TEST: mockup data + // setResults({ + // fullText: [ + // {id: 1, content: "1. Schedule of the closing ceremony and diploma presentation ceremony: Friday, January 4, 2019 at 9 o'clock for students to go to school.\n1) ", date: "", highlight: false, registered: false}, + // {id: 2, content: "Closing ceremony", date: "2022-01-04", highlight: true, registered: false}, + // {id: 3, content: ": 1st and 2nd graders, each classroom, 9:00-10:50 (no meals)\n2) ", date: "", highlight: false, registered: false}, + // {id: 4, content: "Diploma representation ceremony", date: "2022-01-04", highlight: true, registered: true}, + // {id: 5, content: ": 3rd grade, multi-purpose auditorium (2nd floor), 10:30-12:20\n2. School opening and entrance ceremony for new students: March 4th (Mon), 2019 at 9 o'clock for students to go to school.", date: "", highlight: false, registered: false}, + // ], + // korean: "가정통신문\n예당중학교\n8053-8388\n꿈은 크게. 마음은 넘게·\n행동은 바르게\n학부모님께\n희망찬 새해를 맞이하며 학부모님 가정에 건강과 행운이 함께 하시기를 기원 드립니다.\n드릴 말씀은, 2018학년도 종업식 및 졸업장 수여식과 2019학년도 개학 및 신입생 입학식을 다음과 같이 안내드리오니, 이후 3월 개학 때까지 학생들이 자기주도 학습 능력을 배양하고 다양한 체험 활동을 통하여 심신이 건강해지며 각종 유해 환경에 노출되지 않고 안전하고 줄거운 시간이 되도록 지도해 주시기 바랍니다.\n", + // trans_full: "", + // event_num: 2, + // events: [ + // { + // title: "opening ceremony", + // date: "2022-03-24" + // }, + // { + // title: "closing ceremony", + // date: "2022-03-24" + // } + // ] + // }) } const handleKorean = (): void => { @@ -193,20 +199,29 @@ export default function TranslateScreen({ navigation }: Navigation) { type: mime.getType(imageUri), name: imageUri.split("/").pop() }); - // formdata.append('noticeRequestDTO', new Blob([JSON.stringify(data)], {type: 'application/json'})); - formdata.append('cid', form?.cid); - formdata.append('title', form?.title); - formdata.append('date', new Date().toISOString().slice(0, 10)); - formdata.append('korean', results?.korean); - formdata.append('trans_full', results?.trans_full); + let data = { + cid: form?.cid, + title: form?.title, + date: new Date().toISOString().slice(0, 10), + korean: results?.korean, + fullText: results?.trans_full + } + formdata.append("noticeRequestDto", JSON.stringify(data)); + // formdata.append('noticeRequestDto', new Blob([JSON.stringify(data)], {type: 'application/json'})); + + // formdata.append('cid', form?.cid); + // formdata.append('title', form?.title); + // formdata.append('date', new Date().toISOString().slice(0, 10)); + // formdata.append('korean', results?.korean); + // formdata.append('trans_full', results?.trans_full); - console.log(formdata); + // console.log(formdata); - if (auth?.authData?.jwt_token) { + if (auth?.authData?.access_token) { fetch('http://localhost:8080/notice/save', { method: 'POST', headers: { - 'JWT_TOKEN': auth.authData.jwt_token, + 'ACCESS-TOKEN': auth.authData.access_token, }, body: formdata, redirect: 'follow' @@ -230,10 +245,6 @@ export default function TranslateScreen({ navigation }: Navigation) { } } - const closeResults = (): void => { - navigation.navigate('Home'); - } - const retakePicture = (): void => { setImageUri(''); setResults({id: 0, fullText: [], korean: ''}); @@ -247,43 +258,64 @@ export default function TranslateScreen({ navigation }: Navigation) { {imageUri ? ( /* After taking a picture and press the check button */ results?.fullText && results?.korean ? ( - - - } - itemFull={ - - } - onShowMini={() => setFullDrawer(false)} - onShowFull={() => setFullDrawer(true)} - animation="easeInEaseOut" - disableSwipeIcon - extraMarginTop={10} - swipeHeight={Dimensions.get('window').height*0.5} - /> + + + + } + itemFull={ + + } + onShowMini={() => setFullDrawer(false)} + onShowFull={() => setFullDrawer(true)} + animation="easeInEaseOut" + disableSwipeIcon + extraMarginTop={10} + swipeHeight={Dimensions.get('window').height*0.65} + /> + + + + {results?.event_num} Events Extracted + + + {results?.events?.map((item, index) => + + {item.date}  + {item.title} + + )} + + + + + + + + ) : ( /* After taking a picture, before OCR(pressing the check button) */ @@ -324,6 +356,7 @@ export default function TranslateScreen({ navigation }: Navigation) { + )} @@ -373,5 +406,9 @@ const styles = StyleSheet.create({ height: 56, width: 56, borderWidth: 2 + }, + backdrop: { + flex: 1, + backgroundColor: 'rgba(0,0,0, 0.60)' } }); diff --git a/react-native/services/authService.ts b/react-native/services/authService.ts index ca999b3..19f4b2e 100644 --- a/react-native/services/authService.ts +++ b/react-native/services/authService.ts @@ -10,10 +10,11 @@ const signIn = (accessToken: string): Promise => { } }) .then(response => { + // console.log('response headers',response.headers); let data = { header: { - jwt_token: response.headers.jwt_token, - refresh_token: response.headers.refresh_token + access_token: response.headers["access-token"], + refresh_token: response.headers["refresh-token"] }, body: response.data } @@ -32,8 +33,8 @@ const signUp = (data: JoinData): Promise => { .then(response => { let data = { header: { - jwt_token: response.headers.jwt_token, - refresh_token: response.headers.refresh_token + access_token: response.headers["access-token"], + refresh_token: response.headers["refresh-token"] }, body: response.data } diff --git a/react-native/types.ts b/react-native/types.ts index 4f2be18..2c9db0a 100644 --- a/react-native/types.ts +++ b/react-native/types.ts @@ -1,116 +1,124 @@ import type { NativeStackScreenProps } from '@react-navigation/native-stack'; export type RootStackParamList = { - Login: undefined; - Join: undefined; - Introduction: undefined; - Home: undefined; - Translate: undefined; - Search: undefined; - Calendar: undefined; - FullText: undefined; - SearchResult: undefined; + Login: undefined; + Join: undefined; + Introduction: undefined; + Home: undefined; + Translate: undefined; + Search: undefined; + Calendar: undefined; + FullText: undefined; + SearchResult: undefined; }; export type Navigation = NativeStackScreenProps; export type TextInput = { - errorText: string; - description: string; + errorText: string; + description: string; } interface Children { - cid: number, - cname?: string, - color?: number, + cid: number, + cname?: string, + cprofileImg: number, + color?: number, } -interface JoinData { - uid?: number, - uprofileImg?: number, - username?: string, - ulanguage?: string, - uchildren?: Children[] +interface UserInfo { + uid?: number, + uprofileImg?: number, + username?: string, + ulanguage?: string, } -interface UserData extends JoinData { +interface JoinData extends UserInfo { + uchildren?: { cname?: string, cprofileImg: number, color?: number }[] +} + +interface UserData extends UserInfo { + uchildren?: Children[], uemail?: string | undefined, uproviderType?: string | undefined, uroleType?: string | undefined, } interface AuthData { - jwt_token?: string, - refresh_token?: string, + access_token?: string, + refresh_token?: string, } interface AuthResponse { - header: AuthData, - body: UserData + header: AuthData, + body: UserData } interface AuthContextData { authData?: AuthData; userData?: UserData; loading: boolean; - update: boolean; + update: boolean; signUp(data: JoinData): Promise; signIn(accessToken: string): Promise; signOut(): void; - handleUpdate(): void; + handleUpdate(): void; }; interface Event { - id: number, - content: string, - date?: string, - highlight: boolean, - registered: boolean + id: number, + content: string, + date?: string, + highlight: boolean, + registered: boolean } interface Result { - id?: number, - imageUri?: string, - fullText: Event[], - korean: string, - trans_full?: string + id?: number, + imageUri?: string, + fullText: Event[], + korean: string, + trans_full?: string, + + event_num?: number, + events?: { title: string, date: string }[] } interface Notice { - date: string, - results: Result[] + date: string, + results: Result[] } interface Notices { - date: string, - saved_titles: string[] + date: string, + saved_titles: string[] } interface BottomDrawerProps { - results: Result, - showKorean?: boolean, - isFullDrawer?: boolean, - isTranslateScreen?: boolean, - openSaveForm?: boolean, - handleKorean?: () => void, - saveResults?: (form: ResultsForm) => void, - closeResults?: () => void, - retakePicture?: () => void, - handleOpenSaveForm?: () => void + results: Result, + showKorean?: boolean, + isFullDrawer?: boolean, + isTranslateScreen?: boolean, + openSaveForm?: boolean, + handleKorean?: () => void, + saveResults?: (form: ResultsForm) => void, + closeResults?: () => void, + retakePicture?: () => void, + handleOpenSaveForm?: () => void } interface EventForm { - title: string, - date: string, - cid: number, - description: string + title: string, + date: string, + cid: number, + description: string } interface ResultsForm { - cid: number, - title: string + cid: number, + title: string } export type { - UserData, JoinData, AuthData, AuthResponse, AuthContextData, Children, - Event, Result, Notice, Notices, BottomDrawerProps, EventForm, ResultsForm + UserData, JoinData, AuthData, AuthResponse, AuthContextData, Children, + Event, Result, Notice, Notices, BottomDrawerProps, EventForm, ResultsForm } diff --git a/react-native/yarn.lock b/react-native/yarn.lock index d818553..6a1def2 100644 --- a/react-native/yarn.lock +++ b/react-native/yarn.lock @@ -2413,6 +2413,14 @@ resolved "https://registry.yarnpkg.com/@types/react-native-dotenv/-/react-native-dotenv-0.2.0.tgz#32c58422a422c1adf68acce363ed791314d5a8e7" integrity sha512-ZxX+dU/yoQc0jTk+/NWttkiuXceJyN5FpOSqDl0WynN5GDzxwH7OMruQ47qcY8llo2RD3irjvzJ9BwC8gDiq0A== +"@types/react-native-material-menu@^1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/react-native-material-menu/-/react-native-material-menu-1.0.6.tgz#953dd6cfce18baee9930290e7926b12f5b6676e7" + integrity sha512-qGqqRo5a74n3jB+9wvtW7m+qriqFWpg0JnbpHIir9g39FxRFxe71b7M0WOMdsC2ue5I/OeyJLaCmwXKUFk3zug== + dependencies: + "@types/react" "*" + "@types/react-native" "*" + "@types/react-native-vector-icons@^6.4.6": version "6.4.10" resolved "https://registry.yarnpkg.com/@types/react-native-vector-icons/-/react-native-vector-icons-6.4.10.tgz#9bfd6e64dd37b8119425496b5e53ff91d034efa9" @@ -2646,12 +2654,13 @@ atob@^2.1.2: resolved "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -axios@^0.26.1: - version "0.26.1" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9" - integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA== +axios@^0.27.2: + version "0.27.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" + integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== dependencies: - follow-redirects "^1.14.8" + follow-redirects "^1.14.9" + form-data "^4.0.0" babel-core@^7.0.0-bridge.0: version "7.0.0-bridge.0" @@ -3940,10 +3949,10 @@ flow-parser@0.*, flow-parser@^0.121.0: resolved "https://registry.npmjs.org/flow-parser/-/flow-parser-0.121.0.tgz" integrity sha512-1gIBiWJNR0tKUNv8gZuk7l9rVX06OuLzY9AoGio7y/JT4V1IZErEMEq2TJS+PFcw/y0RshZ1J/27VfK1UQzYVg== -follow-redirects@^1.14.8: - version "1.14.9" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" - integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== +follow-redirects@^1.14.9: + version "1.15.1" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" + integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== fontfaceobserver@^2.1.0: version "2.1.0" @@ -4818,7 +4827,7 @@ lodash.throttle@^4.1.1: resolved "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz" integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= -lodash@^4.17.14, lodash@^4.17.15: +lodash@*, lodash@^4.17.14, lodash@^4.17.15: version "4.17.21" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -5945,6 +5954,13 @@ react-native-dotenv@^3.3.1: dependencies: dotenv "^10.0.0" +react-native-element-dropdown@^1.8.11: + version "1.8.11" + resolved "https://registry.yarnpkg.com/react-native-element-dropdown/-/react-native-element-dropdown-1.8.11.tgz#5c66945555b657348e0330a5b9f5e8a354a8ab50" + integrity sha512-7IdQr/XP/t8ErAk5XU2yE8evB5EbYgLhhLafbGMeJEMb3LjRYQDYkm/fr/IhWI0SHgnneEUzj49PhUK/XpcOAA== + dependencies: + lodash "*" + react-native-elements@^3.4.2: version "3.4.2" resolved "https://registry.yarnpkg.com/react-native-elements/-/react-native-elements-3.4.2.tgz#66602be9c5e0e0a2a831913adec80ff6518d1ee2" @@ -5972,6 +5988,11 @@ react-native-keyboard-aware-scroll-view@^0.9.5: prop-types "^15.6.2" react-native-iphone-x-helper "^1.0.3" +react-native-material-menu@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/react-native-material-menu/-/react-native-material-menu-2.0.0.tgz#139f407a52fbc0d47e5351d50ac769e05812da5c" + integrity sha512-SmO9PLE3E469EPbVWZqvdu6JGPPZIm7YjqDcWs2PPoY0k7w2V9tFo3BmmLXNzNZDCVCAi+PPSsL7h/5WkfHcSg== + react-native-modal-datetime-picker@^13.1.0: version "13.1.0" resolved "https://registry.yarnpkg.com/react-native-modal-datetime-picker/-/react-native-modal-datetime-picker-13.1.0.tgz#9aeb99d83f30e8ea1168d5c17cb84e21630278df" @@ -5979,6 +6000,11 @@ react-native-modal-datetime-picker@^13.1.0: dependencies: prop-types "^15.7.2" +react-native-popup-menu@^0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/react-native-popup-menu/-/react-native-popup-menu-0.15.12.tgz#386852f4245f8d661a5003776989b9b55c9ce381" + integrity sha512-33zMqHW+7rt5m2Bgg7xM8jy1guIdUCE8qJfznYYNRlIt4+fJX/rDcwmJIrkSGrvfhlhhEaG3otW6h25uFZLFfg== + react-native-ratings@8.0.4: version "8.0.4" resolved "https://registry.yarnpkg.com/react-native-ratings/-/react-native-ratings-8.0.4.tgz#efd5ebad8acc08bf98d34d39b18fb7a6813ef991" diff --git a/spring/notinote/src/main/java/com/answer/notinote/Auth/controller/AuthController.java b/spring/notinote/src/main/java/com/answer/notinote/Auth/controller/AuthController.java new file mode 100644 index 0000000..936f069 --- /dev/null +++ b/spring/notinote/src/main/java/com/answer/notinote/Auth/controller/AuthController.java @@ -0,0 +1,54 @@ +package com.answer.notinote.Auth.controller; + +import com.answer.notinote.Auth.service.AuthService; +import com.answer.notinote.User.dto.JoinRequestDto; +import com.answer.notinote.User.service.UserService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@RestController +@RequiredArgsConstructor +public class AuthController { + + private final AuthService authService; + + /** + * oauth2 로그인을 진행합니다. + * @param token + * @return + */ + @GetMapping("/login/oauth2") + public ResponseEntity oauthLogin(HttpServletResponse response, @RequestHeader("Authorization") String token) { + return ResponseEntity.ok(authService.oauthLogin(response, token)); + } + + /** + * 회원가입 폼 정보를 받아 유저의 권한을 USER로 바꾸고 로그인을 진행합니다. + * @param requestDto + * @return + */ + @PostMapping("/join") + public ResponseEntity join(HttpServletResponse response, @RequestBody JoinRequestDto requestDto) { + return ResponseEntity.ok(authService.join(response, requestDto)); + } + + @PostMapping("/refresh") + public ResponseEntity refresh(HttpServletRequest request, HttpServletResponse response) { + + return ResponseEntity.ok(authService.refreshToken(request, response)); + } + + /** + * 회원을 로그아웃합니다. + * @param request + * @return + */ + @DeleteMapping("/logout") + public ResponseEntity logout(HttpServletRequest request) { + return ResponseEntity.ok(authService.logout(request)); + } +} diff --git a/spring/notinote/src/main/java/com/answer/notinote/Auth/filter/JwtAuthenticationFilter.java b/spring/notinote/src/main/java/com/answer/notinote/Auth/filter/JwtAuthenticationFilter.java index 04cf14a..9df0cf5 100644 --- a/spring/notinote/src/main/java/com/answer/notinote/Auth/filter/JwtAuthenticationFilter.java +++ b/spring/notinote/src/main/java/com/answer/notinote/Auth/filter/JwtAuthenticationFilter.java @@ -21,27 +21,10 @@ public class JwtAuthenticationFilter extends GenericFilterBean { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { - String jwtToken = jwtTokenProvider.resolveToken((HttpServletRequest) request); - String refreshToken = jwtTokenProvider.resolveRefreshToken((HttpServletRequest) request); + String accessToken = jwtTokenProvider.resolveAccessToken((HttpServletRequest) request); + if (accessToken != null && jwtTokenProvider.validateToken(accessToken)) + setAuthentication(accessToken); - if (jwtToken != null) { - if (jwtTokenProvider.validateToken(jwtToken)) { - // jwt token이 유효한 경우 - setAuthentication(jwtToken); - } - else { - if (refreshToken != null) { - if (jwtTokenProvider.validateToken(refreshToken) && jwtTokenProvider.existsRefreshToken(refreshToken)) { - // jwt token이 만료되고, refresh token이 유효한 경우 - String email = jwtTokenProvider.getUserEmail(refreshToken); - String newToken = jwtTokenProvider.createToken(email); - jwtTokenProvider.setHeaderToken((HttpServletResponse) response, newToken); - - setAuthentication(newToken); - } - } - } - } chain.doFilter(request, response); } diff --git a/spring/notinote/src/main/java/com/answer/notinote/Auth/service/AuthService.java b/spring/notinote/src/main/java/com/answer/notinote/Auth/service/AuthService.java new file mode 100644 index 0000000..ff7ad05 --- /dev/null +++ b/spring/notinote/src/main/java/com/answer/notinote/Auth/service/AuthService.java @@ -0,0 +1,125 @@ +package com.answer.notinote.Auth.service; + +import com.answer.notinote.Auth.data.ProviderType; +import com.answer.notinote.Auth.data.RoleType; +import com.answer.notinote.Auth.repository.RefreshTokenRepository; +import com.answer.notinote.Auth.token.RefreshToken; +import com.answer.notinote.Auth.token.provider.JwtTokenProvider; +import com.answer.notinote.Auth.userdetails.GoogleUser; +import com.answer.notinote.Child.service.ChildService; +import com.answer.notinote.Exception.CustomException; +import com.answer.notinote.Exception.ErrorCode; +import com.answer.notinote.User.domain.entity.User; +import com.answer.notinote.User.domain.repository.UserRepository; +import com.answer.notinote.User.dto.JoinRequestDto; +import com.answer.notinote.User.dto.UserResponseDto; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@Service +@RequiredArgsConstructor +public class AuthService { + private final OAuth2Service oAuthService; + private final ChildService childService; + private final UserRepository userRepository; + private final RefreshTokenRepository refreshTokenRepository; + private final JwtTokenProvider jwtTokenProvider; + + public UserResponseDto oauthLogin(HttpServletResponse response, String token) { + ResponseEntity userInfoResponse = oAuthService.createGetRequest(token); + GoogleUser googleUser = oAuthService.getUserInfo(userInfoResponse); + + User user = userRepository.findByUemail(googleUser.getEmail()).orElse(null); + if (user == null) { + user = User.builder() + .uemail(googleUser.getEmail()) + .username(googleUser.getName()) + .uroleType(RoleType.GUEST) + .uproviderType(ProviderType.GOOGLE) + .build(); + userRepository.save(user); + } + else { + issueToken(response, user); + } + + return new UserResponseDto(user); + } + + @Transactional + public UserResponseDto join(HttpServletResponse response, JoinRequestDto requestDto) { + User user = userRepository.findById(requestDto.getUid()).orElseThrow( + () -> new CustomException(ErrorCode.USER_NOT_FOUND) + ); + + requestDto.getUchildren().forEach( childDto -> childService.create(childDto, user)); + + if (user.getUroleType() == RoleType.GUEST) { + user.setUsername(requestDto.getUsername()); + user.setUlanguage(requestDto.getUlanguage()); + user.setUroleType(RoleType.USER); + user.setUprofileImg(requestDto.getUprofileImg()); + userRepository.save(user); + + issueToken(response, user); + return new UserResponseDto(user); + } + else { + throw new CustomException(ErrorCode.USER_DUPLICATED); + } + } + + private void issueToken(HttpServletResponse response, User user) { + String accessToken = jwtTokenProvider.createToken(user); + String refreshToken = jwtTokenProvider.createRefreshToken(user); + + jwtTokenProvider.setAccessToken(response, accessToken); + jwtTokenProvider.setRefreshToken(response, refreshToken); + } + + @Transactional + public String refreshToken(HttpServletRequest request, HttpServletResponse response) { + String accessToken = jwtTokenProvider.resolveAccessToken(request); + String refreshToken = jwtTokenProvider.resolveRefreshToken(request); + + boolean validateRefreshToken = jwtTokenProvider.validateToken(refreshToken) && jwtTokenProvider.existsRefreshToken(refreshToken); + if (jwtTokenProvider.validateTokenExpired(accessToken) && validateRefreshToken) { + String email = jwtTokenProvider.getUserEmail(refreshToken); + User user = findUserByEmail(email); + + String newAccessToken = jwtTokenProvider.createToken(user); + jwtTokenProvider.setAccessToken(response, newAccessToken); + + return "ok"; + } else { + throw new CustomException(ErrorCode.TOKEN_NOT_EXPIRED); + } + } + + @Transactional + public Long logout(HttpServletRequest request) { + String token = jwtTokenProvider.resolveAccessToken(request); + String email = jwtTokenProvider.getUserEmail(token); + + User user = findUserByEmail(email); + + RefreshToken refreshToken = refreshTokenRepository.findByUser(user).orElseThrow( + () -> new CustomException(ErrorCode.TOKEN_NOT_FOUND) + ); + refreshTokenRepository.delete(refreshToken); + + return user.getUid(); + } + + private User findUserByEmail(String email) { + return userRepository.findByUemail(email).orElseThrow( + () -> new CustomException(ErrorCode.USER_NOT_FOUND) + ); + } + +} diff --git a/spring/notinote/src/main/java/com/answer/notinote/Auth/service/OAuthService.java b/spring/notinote/src/main/java/com/answer/notinote/Auth/service/OAuth2Service.java similarity index 94% rename from spring/notinote/src/main/java/com/answer/notinote/Auth/service/OAuthService.java rename to spring/notinote/src/main/java/com/answer/notinote/Auth/service/OAuth2Service.java index 55ba6fa..fdf73df 100644 --- a/spring/notinote/src/main/java/com/answer/notinote/Auth/service/OAuthService.java +++ b/spring/notinote/src/main/java/com/answer/notinote/Auth/service/OAuth2Service.java @@ -15,7 +15,7 @@ @Service @RequiredArgsConstructor -public class OAuthService { +public class OAuth2Service { private final ObjectMapper objectMapper; private final RestTemplate restTemplate = new RestTemplate(); @@ -32,7 +32,7 @@ public ResponseEntity createGetRequest(String oAuthToken) { return restTemplate.exchange(url, HttpMethod.GET, request, String.class); } catch (Exception e) { - throw new CustomException(ErrorCode.TOKEN_INVALID); + throw new CustomException(ErrorCode.TOKEN_EXPIRED); } } diff --git a/spring/notinote/src/main/java/com/answer/notinote/Auth/service/RefreshTokenService.java b/spring/notinote/src/main/java/com/answer/notinote/Auth/service/RefreshTokenService.java deleted file mode 100644 index 0581805..0000000 --- a/spring/notinote/src/main/java/com/answer/notinote/Auth/service/RefreshTokenService.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.answer.notinote.Auth.service; - -import com.answer.notinote.Auth.repository.RefreshTokenRepository; -import com.answer.notinote.Auth.token.RefreshToken; -import com.answer.notinote.Exception.CustomException; -import com.answer.notinote.Exception.ErrorCode; -import com.answer.notinote.User.domain.entity.User; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -@RequiredArgsConstructor -public class RefreshTokenService { - - private final RefreshTokenRepository refreshTokenRepository; - - @Transactional - public void delete(User user) { - RefreshToken token = refreshTokenRepository.findByUser(user).orElseThrow( - () -> new CustomException(ErrorCode.TOKEN_NOT_FOUND) - ); - - refreshTokenRepository.delete(token); - } -} diff --git a/spring/notinote/src/main/java/com/answer/notinote/Auth/token/provider/JwtTokenProvider.java b/spring/notinote/src/main/java/com/answer/notinote/Auth/token/provider/JwtTokenProvider.java index c6739a8..4a9dbbf 100644 --- a/spring/notinote/src/main/java/com/answer/notinote/Auth/token/provider/JwtTokenProvider.java +++ b/spring/notinote/src/main/java/com/answer/notinote/Auth/token/provider/JwtTokenProvider.java @@ -1,7 +1,7 @@ package com.answer.notinote.Auth.token.provider; -import com.answer.notinote.Auth.data.RoleType; import com.answer.notinote.Auth.repository.RefreshTokenRepository; +import com.answer.notinote.Auth.token.RefreshToken; import com.answer.notinote.Exception.CustomException; import com.answer.notinote.Exception.ErrorCode; import com.answer.notinote.User.domain.entity.User; @@ -33,14 +33,12 @@ public class JwtTokenProvider { @Value("${jwt.secret}") private String secretKey; - @Value("${jwt.refresh}") - private String refreshKey; + private long accessTokenValidTime = 60 * 60 * 1000L; + private long refreshTokenValidTime = 300 * 60 * 1000L; - private long tokenValidTime = 60 * 60 * 1000L; - private long refreshValidTime = 300 * 60 * 1000L; + private String accessTokenHeader = "Access-Token"; - private String tokenHeader = "JWT_TOKEN"; - private String refreshHeader = "REFRESH_TOKEN"; + private String refreshTokenHeader = "Refresh-Token"; private final UserDetailsService userDetailsService; private final RefreshTokenRepository refreshTokenRepository; @@ -51,15 +49,17 @@ public class JwtTokenProvider { @PostConstruct protected void init() { secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes()); - refreshKey = Base64.getEncoder().encodeToString(refreshKey.getBytes()); } - public String createToken(String email) { - return convertToToken(email, tokenValidTime, secretKey); + public String createToken(User user) { + return convertToToken(user.getUemail(), accessTokenValidTime, secretKey); } - public String createRefreshToken(String email) { - return convertToToken(email, refreshValidTime, refreshKey); + public String createRefreshToken(User user) { + String refreshToken = convertToToken(user.getUemail(), refreshTokenValidTime, secretKey); + refreshTokenRepository.save(new RefreshToken(user, refreshToken)); + + return refreshToken; } // 토큰에서 인증 정보 조회 @@ -68,25 +68,24 @@ public Authentication getAuthentication(String token) { return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); } - // 토큰에서 회원 정보 추출 public String getUserEmail(String token) { return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject(); } - public String resolveToken(HttpServletRequest request) { - return request.getHeader(tokenHeader); + public String resolveAccessToken(HttpServletRequest request) { + return request.getHeader(accessTokenHeader); } - public String resolveRefreshToken(HttpServletRequest request) { - return request.getHeader(refreshHeader); + public void setAccessToken(HttpServletResponse response, String token) { + response.setHeader(accessTokenHeader, token); } - public void setHeaderToken(HttpServletResponse response, String token) { - response.setHeader(tokenHeader, token); + public String resolveRefreshToken(HttpServletRequest request) { + return request.getHeader(refreshTokenHeader); } - public void setHeaderRefreshToken(HttpServletResponse response, String token) { - response.setHeader(refreshHeader, token); + public void setRefreshToken(HttpServletResponse response, String token) { + response.setHeader(refreshTokenHeader, token); } // 토큰의 유효성 & 만료일자 확인 @@ -108,6 +107,17 @@ public boolean validateToken(String token) { return false; } + public boolean validateTokenExpired(String token) { + try { + Jws claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token); + return claims.getBody().getExpiration().before(new Date()); + } catch(ExpiredJwtException e) { + return true; + } catch (Exception e) { + return false; + } + } + private String convertToToken(String email, Long validTime, String key) { User user = userRepository.findByUemail(email).orElseThrow( () -> new CustomException(ErrorCode.USER_NOT_FOUND) diff --git a/spring/notinote/src/main/java/com/answer/notinote/Child/domain/Child.java b/spring/notinote/src/main/java/com/answer/notinote/Child/domain/Child.java index 2056bcd..2421bb6 100644 --- a/spring/notinote/src/main/java/com/answer/notinote/Child/domain/Child.java +++ b/spring/notinote/src/main/java/com/answer/notinote/Child/domain/Child.java @@ -32,6 +32,9 @@ public class Child extends Timestamped { @Column(length = 20) String cname; + @Column + private Long cprofileImg; + @Column() Long color; @@ -41,6 +44,7 @@ public class Child extends Timestamped { public Child (ChildDto requestDto) { this.cname = requestDto.getCname(); this.color = requestDto.getColor(); + this.cprofileImg = requestDto.getCprofileImg(); } public void setUser(User user) { diff --git a/spring/notinote/src/main/java/com/answer/notinote/Child/dto/ChildDto.java b/spring/notinote/src/main/java/com/answer/notinote/Child/dto/ChildDto.java index 831dc72..5ed8031 100644 --- a/spring/notinote/src/main/java/com/answer/notinote/Child/dto/ChildDto.java +++ b/spring/notinote/src/main/java/com/answer/notinote/Child/dto/ChildDto.java @@ -23,12 +23,14 @@ public class ChildDto { Long cid; String cname; Long color; + Long cprofileImg; List events = new ArrayList<>(); public ChildDto(Child child) { this.cid = child.getCid(); this.cname = child.getCname(); this.color = child.getColor(); + this.cprofileImg = child.getCprofileImg(); for(Event event : child.getEvents()) { if (event.isRegistered() && event.getDate().isEqual(LocalDate.now())) diff --git a/spring/notinote/src/main/java/com/answer/notinote/Config/security/WebSecurityConfig.java b/spring/notinote/src/main/java/com/answer/notinote/Config/security/WebSecurityConfig.java index 24d7211..309d30b 100644 --- a/spring/notinote/src/main/java/com/answer/notinote/Config/security/WebSecurityConfig.java +++ b/spring/notinote/src/main/java/com/answer/notinote/Config/security/WebSecurityConfig.java @@ -21,7 +21,6 @@ @RequiredArgsConstructor @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { - private final JwtTokenProvider jwtTokenProvider; @Override @@ -35,7 +34,7 @@ protected void configure(HttpSecurity http) throws Exception { .and() // 모두 접근 가능한 URL .authorizeRequests() - .antMatchers("/","/login/oauth2","/login", "/join", + .antMatchers("/","/login/oauth2","/login", "/join", "/refresh", "/swagger-ui.html", "/swagger/**", "/swagger-resources/**", "/webjars/**", "/v2/api-docs").permitAll() .and() // USER만 접근 가능한 URL diff --git a/spring/notinote/src/main/java/com/answer/notinote/Event/domain/Event.java b/spring/notinote/src/main/java/com/answer/notinote/Event/domain/Event.java index 96ed521..6ee5a56 100644 --- a/spring/notinote/src/main/java/com/answer/notinote/Event/domain/Event.java +++ b/spring/notinote/src/main/java/com/answer/notinote/Event/domain/Event.java @@ -77,11 +77,8 @@ public void register(EventRegisterDto requestDto) { @Override public int compareTo(Event e) { - if (this.index_start < e.index_start) { - return -1; - } else if (this.index_start > e.index_start) { - return 1; - } + if (this.index_start < e.index_start) return -1; + if (this.index_start > e.index_start) return 1; return 0; } } diff --git a/spring/notinote/src/main/java/com/answer/notinote/Event/dto/EventResponseDto.java b/spring/notinote/src/main/java/com/answer/notinote/Event/dto/EventResponseDto.java index 0dd4260..263f524 100644 --- a/spring/notinote/src/main/java/com/answer/notinote/Event/dto/EventResponseDto.java +++ b/spring/notinote/src/main/java/com/answer/notinote/Event/dto/EventResponseDto.java @@ -17,6 +17,14 @@ public class EventResponseDto { public EventResponseDto(Event event) { this.eid = event.getEid(); this.title = event.getTitle(); + + // [이름] 제거 + if (this.title.charAt(0) == '[') { + while(this.title.charAt(0) != ']' || this.title.length() <= 1) + this.title = this.title.substring(1); + this.title = this.title.substring(1); + } + this.description = event.getDescription(); this.date = event.getDate(); } diff --git a/spring/notinote/src/main/java/com/answer/notinote/Exception/ErrorCode.java b/spring/notinote/src/main/java/com/answer/notinote/Exception/ErrorCode.java index c8b28de..057082d 100644 --- a/spring/notinote/src/main/java/com/answer/notinote/Exception/ErrorCode.java +++ b/spring/notinote/src/main/java/com/answer/notinote/Exception/ErrorCode.java @@ -10,8 +10,8 @@ public enum ErrorCode { USER_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 계정이 존재하지 않습니다."), USER_DUPLICATED(HttpStatus.CONFLICT, "이미 해당 계정의 유저가 존재합니다."), TOKEN_NOT_FOUND(HttpStatus.NOT_FOUND, "토큰이 존재하지 않습니다."), - TOKEN_INVALID(HttpStatus.BAD_REQUEST, "액세스 토큰이 만료되었습니다."), - NOT_SUPPORTED_TYPE(HttpStatus.CONFLICT, "지원하지 않는 로그인 형식입니다"), + TOKEN_EXPIRED(HttpStatus.BAD_REQUEST, "액세스 토큰이 만료되었습니다."), + TOKEN_NOT_EXPIRED(HttpStatus.BAD_REQUEST, "액세스 토큰이 만료되지 않았습니다."), NOT_FOUND(HttpStatus.NOT_FOUND, "해당 객체가 존재하지 않습니다."), ; diff --git a/spring/notinote/src/main/java/com/answer/notinote/Notice/controller/NoticeController.java b/spring/notinote/src/main/java/com/answer/notinote/Notice/controller/NoticeController.java index 68e3aed..71a0d64 100644 --- a/spring/notinote/src/main/java/com/answer/notinote/Notice/controller/NoticeController.java +++ b/spring/notinote/src/main/java/com/answer/notinote/Notice/controller/NoticeController.java @@ -17,7 +17,6 @@ import javax.servlet.http.HttpServletRequest; import java.io.IOException; -import java.time.LocalDate; import java.util.List; @@ -39,7 +38,7 @@ public NoticeController(NoticeService noticeService) { @RequestMapping(value = "/notice/ocr", method = RequestMethod.POST) public NoticeOCRDto executeOCR (@RequestPart MultipartFile uploadfile, HttpServletRequest userrequest) throws IOException { - String token = jwtTokenProvider.resolveToken(userrequest); + String token = jwtTokenProvider.resolveAccessToken(userrequest); String email = jwtTokenProvider.getUserEmail(token); User user = userService.findUserByEmail(email); String targetLanguage = user.getUlanguage(); @@ -55,12 +54,9 @@ public NoticeOCRDto executeOCR (@RequestPart MultipartFile uploadfile, HttpServl @RequestMapping(value = "/notice/save", method = RequestMethod.POST) public NoticeTitleListDto saveNotice( @RequestPart(value = "uploadfile") MultipartFile uploadfile, - @RequestPart(value = "title") String title, - @RequestPart(value = "date") String stringdate, - @RequestPart(value = "korean") String korean, - @RequestPart(value = "trans_full") String fullText, + @RequestPart(value = "noticeRequestDto") NoticeRequestDto noticeRequestDto, HttpServletRequest request) throws IOException { - + /* LocalDate date = LocalDate.parse(stringdate); NoticeRequestDto noticeRequestDto = NoticeRequestDto.builder() .title(title) @@ -68,6 +64,8 @@ public NoticeTitleListDto saveNotice( .korean(korean) .fullText(fullText) .build(); + + */ NoticeTitleListDto notice_title = noticeService.saveNotice(uploadfile, noticeRequestDto, request); //notice 저장 return notice_title; } diff --git a/spring/notinote/src/main/java/com/answer/notinote/Notice/domain/entity/Notice.java b/spring/notinote/src/main/java/com/answer/notinote/Notice/domain/entity/Notice.java index cf598eb..e082907 100644 --- a/spring/notinote/src/main/java/com/answer/notinote/Notice/domain/entity/Notice.java +++ b/spring/notinote/src/main/java/com/answer/notinote/Notice/domain/entity/Notice.java @@ -1,5 +1,6 @@ package com.answer.notinote.Notice.domain.entity; +import com.answer.notinote.Child.domain.Child; import com.answer.notinote.Event.domain.Event; import com.answer.notinote.User.domain.entity.User; import lombok.Builder; @@ -33,9 +34,13 @@ public class Notice { private String trans_full; @ManyToOne - @JoinColumn + @JoinColumn(name = "uid") private User user; + @ManyToOne + @JoinColumn(name = "cid") + private Child child; + private String title; @DateTimeFormat(pattern = "yyyy-MM-dd") @@ -48,7 +53,7 @@ public class Notice { @Builder - public Notice(String nimagename, String nimageoriginal, String nimageurl, String origin_full, String trans_full, LocalDate ndate, String title, User user) { + public Notice(String nimagename, String nimageoriginal, String nimageurl, String origin_full, String trans_full, LocalDate ndate, String title, User user, Child child) { this.nimagename = nimagename; this.nimageoriginal = nimageoriginal; this.nimageurl = nimageurl; @@ -57,6 +62,7 @@ public Notice(String nimagename, String nimageoriginal, String nimageurl, String this.ndate = ndate; this.title = title; this.user = user; + this.child = child; } diff --git a/spring/notinote/src/main/java/com/answer/notinote/Notice/dto/NoticeRequestDto.java b/spring/notinote/src/main/java/com/answer/notinote/Notice/dto/NoticeRequestDto.java index 8303a93..fef55cc 100644 --- a/spring/notinote/src/main/java/com/answer/notinote/Notice/dto/NoticeRequestDto.java +++ b/spring/notinote/src/main/java/com/answer/notinote/Notice/dto/NoticeRequestDto.java @@ -13,14 +13,16 @@ public class NoticeRequestDto { private LocalDate date; private String korean; private String fullText; + private Long cid; @Builder - public NoticeRequestDto (String title, LocalDate date, String korean, String fullText){ + public NoticeRequestDto (String title, LocalDate date, String korean, String fullText, Long cid){ this.title = title; this.date = date; this.korean = korean; this.fullText= fullText; + this.cid = cid; } } diff --git a/spring/notinote/src/main/java/com/answer/notinote/Notice/dto/NoticeSentenceDto.java b/spring/notinote/src/main/java/com/answer/notinote/Notice/dto/NoticeSentenceDto.java index 04a9b88..755402b 100644 --- a/spring/notinote/src/main/java/com/answer/notinote/Notice/dto/NoticeSentenceDto.java +++ b/spring/notinote/src/main/java/com/answer/notinote/Notice/dto/NoticeSentenceDto.java @@ -11,6 +11,7 @@ @Builder public class NoticeSentenceDto { int id; + long eid = -1; String content; LocalDate date; boolean highlight; diff --git a/spring/notinote/src/main/java/com/answer/notinote/Notice/service/NoticeService.java b/spring/notinote/src/main/java/com/answer/notinote/Notice/service/NoticeService.java index 718242c..8830c94 100644 --- a/spring/notinote/src/main/java/com/answer/notinote/Notice/service/NoticeService.java +++ b/spring/notinote/src/main/java/com/answer/notinote/Notice/service/NoticeService.java @@ -1,6 +1,7 @@ package com.answer.notinote.Notice.service; import com.answer.notinote.Auth.token.provider.JwtTokenProvider; +import com.answer.notinote.Child.service.ChildService; import com.answer.notinote.Event.domain.Event; import com.answer.notinote.Notice.dto.NoticeResponseBody; import com.answer.notinote.Event.dto.EventRequestDto; @@ -48,6 +49,8 @@ public class NoticeService { JwtTokenProvider jwtTokenProvider; @Autowired EventService eventService; + @Autowired + ChildService childService; public String detectText(MultipartFile uploadfile) throws IOException{ List requests = new ArrayList<>(); @@ -87,7 +90,7 @@ public String detectText(MultipartFile uploadfile) throws IOException{ public String transText(String korean, String targetLanguage) throws IOException { String text = korean; - String projectId = "notinote-341918"; + String projectId = "solution-challenge-342914"; ArrayList textlist = new ArrayList(); try (TranslationServiceClient client = TranslationServiceClient.create()) { @@ -234,7 +237,7 @@ public List detectEvent(Notice notice, String language) throws JsonProces public NoticeTitleListDto saveNotice(MultipartFile uploadfile, NoticeRequestDto noticeRequestDto, HttpServletRequest request) throws IOException{ //요청한 사용자 확인 - String token = jwtTokenProvider.resolveToken(request); + String token = jwtTokenProvider.resolveAccessToken(request); String useremail = jwtTokenProvider.getUserEmail(token); User user = userRepository.findByUemail(useremail).orElseThrow(IllegalArgumentException::new); @@ -256,11 +259,11 @@ public NoticeTitleListDto saveNotice(MultipartFile uploadfile, NoticeRequestDto .origin_full(noticeRequestDto.getKorean()) .trans_full(noticeRequestDto.getFullText()) .user(user) + .child(childService.findChildById(noticeRequestDto.getCid())) .build(); noticeRepository.save(notice); List events = detectEvent(notice, user.getUlanguage()); - List sentences = extractSentenceFromEvent(notice.getTrans_full(), events); return new NoticeTitleListDto(notice, sentences); @@ -302,6 +305,7 @@ public List extractSentenceFromEvent(String text, List String sentence = text.substring(event.getIndex_start(), event.getIndex_end()); NoticeSentenceDto dto = NoticeSentenceDto.builder() .id(id++) + .eid(event.getEid()) .content(sentence) .date(event.getDate()) .highlight(true) diff --git a/spring/notinote/src/main/java/com/answer/notinote/Search/service/SearchService.java b/spring/notinote/src/main/java/com/answer/notinote/Search/service/SearchService.java index 4d640eb..d08f51f 100644 --- a/spring/notinote/src/main/java/com/answer/notinote/Search/service/SearchService.java +++ b/spring/notinote/src/main/java/com/answer/notinote/Search/service/SearchService.java @@ -38,7 +38,7 @@ public class SearchService { public List searchList(HttpServletRequest request){ - String token = jwtTokenProvider.resolveToken(request); + String token = jwtTokenProvider.resolveAccessToken(request); String useremail = jwtTokenProvider.getUserEmail(token); User user = userRepository.findByUemail(useremail).orElseThrow(IllegalArgumentException::new); @@ -74,7 +74,7 @@ public List searchList(HttpServletRequest request){ public SearchDetailDto searchDetailList(String date, HttpServletRequest request) { - String token = jwtTokenProvider.resolveToken(request); + String token = jwtTokenProvider.resolveAccessToken(request); String useremail = jwtTokenProvider.getUserEmail(token); User user = userRepository.findByUemail(useremail).orElseThrow(IllegalArgumentException::new); LocalDate trans_date = LocalDate.parse(date, DateTimeFormatter.ISO_DATE); diff --git a/spring/notinote/src/main/java/com/answer/notinote/User/controller/UserController.java b/spring/notinote/src/main/java/com/answer/notinote/User/controller/UserController.java index e46b7c0..98e7b5f 100644 --- a/spring/notinote/src/main/java/com/answer/notinote/User/controller/UserController.java +++ b/spring/notinote/src/main/java/com/answer/notinote/User/controller/UserController.java @@ -1,12 +1,9 @@ package com.answer.notinote.User.controller; -import com.answer.notinote.Auth.data.RoleType; import com.answer.notinote.Auth.repository.RefreshTokenRepository; -import com.answer.notinote.Auth.token.RefreshToken; import com.answer.notinote.Auth.token.provider.JwtTokenProvider; import com.answer.notinote.User.dto.JoinRequestDto; import com.answer.notinote.User.domain.entity.User; -import com.answer.notinote.User.dto.UserResponseDto; import com.answer.notinote.User.service.UserService; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; @@ -18,68 +15,18 @@ @CrossOrigin @RestController @RequiredArgsConstructor -@RequestMapping("") +@RequestMapping("/user") public class UserController { private final UserService userService; private final JwtTokenProvider jwtTokenProvider; - private final RefreshTokenRepository refreshTokenRepository; - - /** - * oauth2 로그인을 진행합니다. - * @param response - * @param token - * @return - */ - @GetMapping("/login/oauth2") - public ResponseEntity oauthLogin(HttpServletResponse response, @RequestHeader("Authorization") String token) { - User user = userService.oauthLogin(token); - - issueJwtToken(response, user); - return ResponseEntity.ok(new UserResponseDto(user)); - } - - /** - * 회원가입 폼 정보를 받아 유저의 권한을 USER로 바꾸고 로그인을 진행합니다. - * @param requestDto - * @return - */ - @PostMapping("/join") - public ResponseEntity join(HttpServletResponse response, @RequestBody JoinRequestDto requestDto) { - User user = userService.join(requestDto); - issueJwtToken(response, user); - - return ResponseEntity.ok(new UserResponseDto(user)); - } - - /** - * 로그인한 유저의 ID를 받아 유저 정보와 JWT Token을 반환합니다. - * @param id - * @return - */ - @GetMapping("/login/{id}") - public ResponseEntity login(HttpServletResponse response, @PathVariable("id") long id) { - User user = userService.findUserById(id); - issueJwtToken(response, user); - return ResponseEntity.ok(new UserResponseDto(user)); - } - - /** - * 회원을 로그아웃합니다. - * @param request - * @return - */ - @DeleteMapping("/logout") - public ResponseEntity logout(HttpServletRequest request) { - return ResponseEntity.ok(userService.logout(request)); - } /** * 회원을 탙퇴 처리 합니다. * @param id * @return */ - @DeleteMapping("/user") + @DeleteMapping public Long delete(@RequestParam Long id) { return userService.delete(id); } @@ -89,22 +36,12 @@ public Long delete(@RequestParam Long id) { * @param request * @return */ - @GetMapping("/user/children") + @GetMapping("/children") public ResponseEntity getChildren(HttpServletRequest request) { - String token = jwtTokenProvider.resolveToken(request); + String token = jwtTokenProvider.resolveAccessToken(request); String email = jwtTokenProvider.getUserEmail(token); User user = userService.findUserByEmail(email); return ResponseEntity.ok(userService.findChildrenByUserId(user.getUid())); } - - private void issueJwtToken(HttpServletResponse response, User user) { - if(user.getUroleType() == RoleType.USER) { - String jwtToken = jwtTokenProvider.createToken(user.getUemail()); - String refreshToken = jwtTokenProvider.createRefreshToken(user.getUemail()); - jwtTokenProvider.setHeaderToken(response, jwtToken); - jwtTokenProvider.setHeaderRefreshToken(response, refreshToken); - refreshTokenRepository.save(new RefreshToken(user, refreshToken)); - } - } } diff --git a/spring/notinote/src/main/java/com/answer/notinote/User/service/UserService.java b/spring/notinote/src/main/java/com/answer/notinote/User/service/UserService.java index 428c814..7705bef 100644 --- a/spring/notinote/src/main/java/com/answer/notinote/User/service/UserService.java +++ b/spring/notinote/src/main/java/com/answer/notinote/User/service/UserService.java @@ -1,8 +1,9 @@ package com.answer.notinote.User.service; import com.answer.notinote.Auth.data.ProviderType; -import com.answer.notinote.Auth.service.RefreshTokenService; -import com.answer.notinote.Auth.service.OAuthService; +import com.answer.notinote.Auth.repository.RefreshTokenRepository; +import com.answer.notinote.Auth.service.OAuth2Service; +import com.answer.notinote.Auth.token.RefreshToken; import com.answer.notinote.Auth.token.provider.JwtTokenProvider; import com.answer.notinote.Auth.userdetails.GoogleUser; import com.answer.notinote.Child.domain.Child; @@ -15,75 +16,20 @@ import com.answer.notinote.Auth.data.RoleType; import com.answer.notinote.User.domain.entity.User; import com.answer.notinote.User.domain.repository.UserRepository; +import com.answer.notinote.User.dto.UserResponseDto; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.util.List; @Service @RequiredArgsConstructor public class UserService { - - private final OAuthService oAuthService; - private final RefreshTokenService refreshTokenService; - private final ChildService childService; - private final UserRepository userRepository; - private final JwtTokenProvider jwtTokenProvider; - - public User oauthLogin(String token) { - ResponseEntity userInfoResponse = oAuthService.createGetRequest(token); - GoogleUser googleUser = oAuthService.getUserInfo(userInfoResponse); - - User user = userRepository.findByUemail(googleUser.getEmail()).orElse(null); - if (user == null) { - user = User.builder() - .uemail(googleUser.getEmail()) - .username(googleUser.getName()) - .uroleType(RoleType.GUEST) - .uproviderType(ProviderType.GOOGLE) - .build(); - userRepository.save(user); - } - - return user; - } - - @Transactional - public User join(JoinRequestDto requestDto) { - User user = findUserById(requestDto.getUid()); - - requestDto.getUchildren().forEach( childDto -> { - childService.create(childDto, user); - }); - - if (user.getUroleType() == RoleType.GUEST) { - user.setUsername(requestDto.getUsername()); - user.setUlanguage(requestDto.getUlanguage()); - user.setUroleType(RoleType.USER); - user.setUprofileImg(requestDto.getUprofileImg()); - userRepository.save(user); - - return user; - } - else { - throw new CustomException(ErrorCode.USER_DUPLICATED); - } - } - - @Transactional - public Long logout(HttpServletRequest request) { - String token = jwtTokenProvider.resolveToken(request); - String email = jwtTokenProvider.getUserEmail(token); - - User user = findUserByEmail(email); - refreshTokenService.delete(user); - - return user.getUid(); - } @Transactional public Long delete(Long id) { @@ -95,12 +41,6 @@ public Long delete(Long id) { return id; } - public User findUserById(Long id) { - return userRepository.findById(id).orElseThrow( - () -> new CustomException(ErrorCode.USER_NOT_FOUND) - ); - } - public User findUserByEmail(String email) { return userRepository.findByUemail(email).orElseThrow( () -> new CustomException(ErrorCode.USER_NOT_FOUND) @@ -108,7 +48,9 @@ public User findUserByEmail(String email) { } public ChildrenResponseDto findChildrenByUserId(Long id) { - User user = findUserById(id); + User user = userRepository.findById(id).orElseThrow( + () -> new CustomException(ErrorCode.USER_NOT_FOUND) + );; ChildrenResponseDto response = new ChildrenResponseDto(); for (Child child : user.getUchildren()) { diff --git a/spring/notinote/tokens/StoredCredential b/spring/notinote/tokens/StoredCredential new file mode 100644 index 0000000..409ed97 Binary files /dev/null and b/spring/notinote/tokens/StoredCredential differ