diff --git a/app/_layout.jsx b/app/_layout.jsx index 25f339e..1aeb44a 100755 --- a/app/_layout.jsx +++ b/app/_layout.jsx @@ -3,6 +3,8 @@ import CategoryAddIcon from '@/components/CategoryAddIcon'; import HeaderIcon from '@/components/HeaderIcon'; import HeaderMenu from '@/components/HeaderMenu'; import { env, getSentryConfig, getStoryBookConfig } from '@/constants/env'; +import CategoryBottomSheetProvider from '@/contexts/CategoryBottomSheetProvider'; +import FunnelProvider from '@/contexts/FunnelContext'; import LoginProvider from '@/contexts/LoginContext'; import '@/locales/index'; import { default as theme } from '@/theme/theme.json'; @@ -16,7 +18,6 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; import { default as mapping } from '../theme/mapping.json'; -import FunnelProvider from '@/contexts/FunnelContext'; const SENTRY_MODE = env.SENTRY_MODE; Sentry.init({ @@ -48,88 +49,93 @@ const RootLayout = () => { <> - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/categoryView/categoryEditView.jsx b/app/categoryView/categoryEditView.jsx index 5bb2b26..62c6a79 100644 --- a/app/categoryView/categoryEditView.jsx +++ b/app/categoryView/categoryEditView.jsx @@ -63,10 +63,10 @@ const CategoryEditView = () => { const handleUpdateCategory = () => { const updatedData = { title: categoryName, - color: colors[selectedColor], + color: 1, category_id: params.id, }; - updateCategory({ updatedData }); + updateCategory(updatedData); }; const renderColorItem = ({ item }) => ( diff --git a/app/categoryView/categoryListView.jsx b/app/categoryView/categoryListView.jsx index f850c33..77ca997 100644 --- a/app/categoryView/categoryListView.jsx +++ b/app/categoryView/categoryListView.jsx @@ -1,3 +1,4 @@ +import CategoryBottomSheet from '@/components/categoryView/CategoryBottomSheet'; import CategoryListItem from '@/components/categoryView/CategoryListItem'; import { LoginContext } from '@/contexts/LoginContext'; import useCategoriesQuery from '@/hooks/api/useCategoriesQuery'; @@ -33,6 +34,7 @@ const CategoryListView = () => { data={orderedCategories} renderItem={({ item }) => renderItem({ item })} /> + diff --git a/components/CategoryAddIcon.jsx b/components/CategoryAddIcon.jsx index 5bd3600..fa34dac 100644 --- a/components/CategoryAddIcon.jsx +++ b/components/CategoryAddIcon.jsx @@ -1,14 +1,25 @@ -import { TouchableOpacity, StyleSheet } from 'react-native'; +import { + ADD, + CategoryBottomSheetContext, +} from '@/contexts/CategoryBottomSheetProvider'; import { Icon, useTheme } from '@ui-kitten/components'; -import React from 'react'; -import { router } from 'expo-router'; +import React, { useContext } from 'react'; +import { StyleSheet, TouchableOpacity } from 'react-native'; const CategoryAddIcon = () => { const theme = useTheme(); + const { setMode, setCategoryId, openBottomSheet, setCategoryName } = + useContext(CategoryBottomSheetContext); + const handlePress = () => { + setMode(ADD); + setCategoryId(null); + setCategoryName(''); + openBottomSheet(); + }; return ( router.push('categoryView/categoryAddView')} + onPress={() => handlePress()} style={styles.iconContainer} > = ({ categoryId, categoryName }) => { -// return ( -// -// -// -// -// -// -// -// -// -// ); -// }; - -// const styles = StyleSheet.create({ -// headerContainer: { -// flexDirection: 'row', -// justifyContent: 'space-between', -// }, -// icon: { -// width: widthPercentage(16), -// height: heightPercentage(16), -// }, -// plusIconContainer: { -// backgroundColor: colors.Gray02, -// padding: widthPercentage(8), -// borderRadius: widthPercentage(4), -// }, -// }); - -// export default Category; diff --git a/components/categoryView/CategoryBottomSheet.tsx b/components/categoryView/CategoryBottomSheet.tsx new file mode 100644 index 0000000..4c4bf84 --- /dev/null +++ b/components/categoryView/CategoryBottomSheet.tsx @@ -0,0 +1,167 @@ +import { CategoryBottomSheetContext } from '@/contexts/CategoryBottomSheetProvider'; +import { + useCategoryAddMutation, + useCategoryUpdateMutation, +} from '@/hooks/api/useCategoryMutation'; +import fontStyles from '@/theme/fontStyles'; +import colors from '@/theme/theme.json'; +import { heightPercentage, widthPercentage } from '@/utils/responsiveSize'; +import BottomSheet, { + BottomSheetBackdrop, + BottomSheetView, +} from '@gorhom/bottom-sheet'; +import { Icon } from '@ui-kitten/components'; +import React, { useCallback, useContext } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Pressable, StyleSheet, Text, TextInput, View } from 'react-native'; + +enum CategoryModes { + ADD = 1, + EDIT = 2, +} + +const CategoryBottomSheet = () => { + const { mode, categoryId, categoryName, setCategoryName, closeBottomSheet } = + useContext(CategoryBottomSheetContext); + const { t } = useTranslation(); + const categoryAddText = t('components.categoryModal.addCategory'); + const categoryEditText = t('components.categoryModal.editCategory'); + const titleText = + mode === CategoryModes.ADD ? categoryAddText : categoryEditText; + const { bottomSheetRef } = useContext(CategoryBottomSheetContext); + const { mutate: addCategory } = useCategoryAddMutation(); + const { mutate: updateCategory } = useCategoryUpdateMutation(); + + const handlePress = () => { + if (mode === CategoryModes.ADD) { + handleAddCategory(); + } else { + handleEditCategory(); + } + closeBottomSheet(); + }; + + const handleAddCategory = () => { + const addCategoryData = { + title: categoryName, + color: 1, + }; + addCategory({ addCategoryData }); + }; + + const handleEditCategory = () => { + const updatedData = { + title: categoryName, + color: 1, + category_id: categoryId, + }; + updateCategory(updatedData); + }; + + const renderBackdrop = useCallback( + props => ( + + ), + [], + ); + + return ( + + + + {titleText} + + + + + + + + {t('components.categoryModal.categoryName')} + + + + + + + {t('components.categoryModal.confirm')} + + + + + + + ); +}; + +const styles = StyleSheet.create({ + bottomSheetView: { + flex: 1, + padding: widthPercentage(18), + backgroundColor: 'white', + }, + topContainer: { + backgroundColor: 'white', + flexDirection: 'row', + justifyContent: 'space-between', + marginTop: heightPercentage(20), + height: heightPercentage(60), + }, + middleContainer: { + justifyContent: 'space-between', + flex: 1, + }, + bottomContainer: { + marginBottom: heightPercentage(20), + }, + icon: { + width: heightPercentage(20), + height: heightPercentage(20), + }, + titleText: { + fontSize: fontStyles.Subtitle.S1.B_130.fontSize, + fontFamily: fontStyles.Subtitle.S1.B_130.fontFamily, + }, + nameText: { + fontSize: fontStyles.Paragraph.P1.M_100.fontSize, + fontFamily: fontStyles.Paragraph.P1.M_100.fontFamily, + marginBottom: widthPercentage(12), + }, + textInput: { + borderWidth: 1, + borderColor: '#E6E8EB', + borderRadius: widthPercentage(12), + height: heightPercentage(42), + paddingHorizontal: widthPercentage(16), + }, + buttonText: { + color: 'white', + fontSize: fontStyles.Subtitle.S1.M_100.fontSize, + fontFamily: fontStyles.Subtitle.S1.M_100.fontFamily, + }, + button: { + paddingHorizontal: widthPercentage(8), + paddingVertical: widthPercentage(16), + justifyContent: 'center', + alignItems: 'center', + backgroundColor: colors.Blue01, + borderRadius: widthPercentage(12), + }, +}); + +export default CategoryBottomSheet; diff --git a/components/categoryView/CategoryListItem.jsx b/components/categoryView/CategoryListItem.tsx similarity index 53% rename from components/categoryView/CategoryListItem.jsx rename to components/categoryView/CategoryListItem.tsx index d013e41..2b1c41a 100644 --- a/components/categoryView/CategoryListItem.jsx +++ b/components/categoryView/CategoryListItem.tsx @@ -1,22 +1,40 @@ -import { useRouter } from 'expo-router'; +import { + CategoryBottomSheetContext, + EDIT, +} from '@/contexts/CategoryBottomSheetProvider'; +import React, { useContext } from 'react'; import { Pressable, StyleSheet, View } from 'react-native'; import DragAndDropIcon from '../icons/DragAndDropIcon'; import CategoryButton from './CategoryButton'; -const CategoryListItem = ({ item, isDraggedOn = false }) => { - const router = useRouter(); +interface CategoryListItemProps { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + item: any; + isDraggedOn: boolean; +} - const handlePress = categoryItem => { - router.push({ - pathname: 'categoryView/categoryEditView', - params: { - ...categoryItem, - }, - }); +const CategoryListItem: React.FC = ({ + item, + isDraggedOn = false, +}) => { + const { openBottomSheet, setMode, setCategoryId, setCategoryName } = + useContext(CategoryBottomSheetContext); + + const handlePress = () => { + // router.push({ + // pathname: 'categoryView/categoryEditView', + // params: { + // ...categoryItem, + // }, + // }); + setMode(EDIT); + setCategoryId(item.id); + setCategoryName(item.title); + openBottomSheet(); }; return ( - handlePress(item)}> + handlePress()}> { + const bottomSheetRef = useRef(null); + const [mode, setMode] = useState(ADD); + const [categoryName, setCategoryName] = useState(''); + const [categoryId, setCategoryId] = useState(null); + + const openBottomSheet = () => { + if (bottomSheetRef.current) { + bottomSheetRef.current.snapToIndex(0); + } + }; + + const closeBottomSheet = () => { + if (bottomSheetRef.current) { + bottomSheetRef.current.close(); + } + }; + + return ( + + {children} + + ); +}; + +export default CategoryBottomSheetProvider; diff --git a/hooks/api/useCategoryMutation.js b/hooks/api/useCategoryMutation.js index 49e8ff3..c10cb43 100644 --- a/hooks/api/useCategoryMutation.js +++ b/hooks/api/useCategoryMutation.js @@ -18,9 +18,8 @@ export const useCategoryAddMutation = () => { }); }; -const updateCategoryFetcher = async ({ updatedData }) => { - const data = await Api.updateCategory({ updatedData }); - +const updateCategoryFetcher = async updatedData => { + const data = await Api.updateCategory(updatedData); return data; }; diff --git a/locales/en.json b/locales/en.json index 26a7df5..7efb2b9 100644 --- a/locales/en.json +++ b/locales/en.json @@ -43,7 +43,8 @@ "label": "category input", "placeholder": "Write a category", "color": "Color", - "close": "Close" + "close": "Close", + "title": "Add Category" }, "categoryEditView": { "category": "category", @@ -51,7 +52,8 @@ "color": "color", "edit": "Edit", "delete": "Delete", - "close": "Close" + "close": "Close", + "title": "Edit Category" } }, "components": { @@ -93,6 +95,12 @@ "createSubTodoWithAi": "Generate sub-todo", "createSubTodo": "Add sub-todo", "putInbox": "Put Inbox" + }, + "categoryModal": { + "addCategory": "Add Category", + "editCategory": "Edit Category", + "categoryName": "Category Name", + "confirm": "Confirm" } }, "common": { diff --git a/locales/ko.json b/locales/ko.json index 20c5d9e..53872e3 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -44,7 +44,8 @@ "label": "카테고리 입력", "placeholder": "카테고리를 입력해주세요", "color": "색상", - "close": "닫기" + "close": "닫기", + "title": "카테고리 생성" }, "categoryEditView": { "category": "카테고리", @@ -52,7 +53,8 @@ "color": "색상", "edit": "수정하기", "delete": "삭제하기", - "close": "닫기" + "close": "닫기", + "title": "카테고리 수정" } }, "components": { @@ -94,6 +96,12 @@ "createSubTodoWithAi": "하위 투두 AI로 만들기", "createSubTodo": "하위 투두 직접 만들기", "putInbox": "인박스에 넣기" + }, + "categoryModal": { + "addCategory": "카테고리 추가", + "editCategory": "카테고리 수정", + "categoryName": "카테고리 이름", + "confirm": "확인" } }, "common": {