diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index be87848..4ccc396 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,17 +1,2 @@ -# Organizer -* @kjoon418 @junseok0304 - -# Backend -* @jihoo2002 @bomin0214 @TwooTwoo @ggok0265 - -# Web -* @ten0213 - # Mobile * @Han6262 - -# PM -* @iamseoyoung @tto-oy - -# Design -* @hyeonji44 diff --git a/.github/auto_assign.yml b/.github/auto_assign.yml index 7c96020..dbd3a4a 100644 --- a/.github/auto_assign.yml +++ b/.github/auto_assign.yml @@ -1,2 +1,19 @@ -addReviewers: false -addAssignees: author \ No newline at end of file +# Set to true to add reviewers to pull requests +addReviewers: true + +# Set to true to add assignees to pull requests +addAssignees: true + +# A list of reviewers to be added to pull requests (GitHub user name) +reviewers: + - reviewerA + - reviewerB + - reviewerC + +# A list of keywords to be skipped the process that add reviewers if pull requests include it +skipKeywords: + - wip + +# A number of reviewers added to the pull request +# Set 0 to add all the reviewers (default: 0) +numberOfReviewers: 0 \ No newline at end of file diff --git a/App.js b/App.js new file mode 100644 index 0000000..db78f17 --- /dev/null +++ b/App.js @@ -0,0 +1,3 @@ +import App from "./src/App"; + +export default App; \ No newline at end of file diff --git a/assets/adaptive-icon.png b/assets/adaptive-icon.png new file mode 100644 index 0000000..03d6f6b Binary files /dev/null and b/assets/adaptive-icon.png differ diff --git a/assets/favicon.png b/assets/favicon.png new file mode 100644 index 0000000..e75f697 Binary files /dev/null and b/assets/favicon.png differ diff --git a/assets/icon.png b/assets/icon.png new file mode 100644 index 0000000..a0b1526 Binary files /dev/null and b/assets/icon.png differ diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000..1b14e03 Binary files /dev/null and b/assets/logo.png differ diff --git a/assets/splash-icon.png b/assets/splash-icon.png new file mode 100644 index 0000000..03d6f6b Binary files /dev/null and b/assets/splash-icon.png differ diff --git a/src/App.js b/src/App.js new file mode 100644 index 0000000..3be6d8f --- /dev/null +++ b/src/App.js @@ -0,0 +1,22 @@ +import React from "react"; +import { StyleSheet, View } from "react-native"; +import { NavigationContainer } from "@react-navigation/native"; +import AuthStack from "../src/navigaitons/AuthStack"; +import { UserProvider } from "./context/UserContext"; + +export default function App() { + return ( + + + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: "#fff", + }, +}); diff --git a/src/api/auth.js b/src/api/auth.js new file mode 100644 index 0000000..2748f01 --- /dev/null +++ b/src/api/auth.js @@ -0,0 +1,11 @@ +export const signInAsync = (email, password) => { + return new Promise((resolve, reject) => { + setTimeout(() => { + if (email === "dlwns1221@gmail.com" && password === "qlalfqjsgh") { + resolve({ id: "u1", name: "어드민", email }); + } else { + reject(new Error("INVALID_CREDENTIALS")); + } + }, 1000); + }); +}; diff --git a/src/componets/Button.js b/src/componets/Button.js new file mode 100644 index 0000000..bd7e28a --- /dev/null +++ b/src/componets/Button.js @@ -0,0 +1,40 @@ +import React from "react"; +import { Pressable, Text, StyleSheet } from "react-native"; +import PropTypes from "prop-types"; + +export default function Button({ title, onPress, disabled }) { + return ( + + {title} + + ); +} + +Button.propTypes = { + title: PropTypes.string.isRequired, + onPress: PropTypes.func, + disabled: PropTypes.bool, +}; + +const styles = StyleSheet.create({ + button: { + width: "100%", + height: 48, + borderRadius: 10, + justifyContent: "center", + alignItems: "center", + marginTop: 28, + }, + text: { + fontSize: 15, + fontWeight: "600", + color: "#ffffff", + }, +}); diff --git a/src/componets/Input.js b/src/componets/Input.js new file mode 100644 index 0000000..6e57b96 --- /dev/null +++ b/src/componets/Input.js @@ -0,0 +1,73 @@ +import React, { useState } from "react"; +import { View, Text, TextInput, StyleSheet } from "react-native"; +import PropTypes from "prop-types"; +import { MaterialIcons } from "@expo/vector-icons"; + +export const KeyboardTypes = { + DEFAULT: "default", + EMAIL: "email-address", +}; + +function Input({ title, placeholder, keyboardType , secureTextEntry, iconName, value, onChangeText }) { + const [isFocused, setIsFocused] = useState(false); + return ( + + {!!title && {title}} + + + setIsFocused(true)} + onBlur={() => setIsFocused(false)} + /> + + + ); +} + +Input.propTypes = { + title: PropTypes.string.isRequired, + placeholder: PropTypes.string, + iconName: PropTypes.string, + value : PropTypes.string, + onChangeText : PropTypes.func, +}; + +export default Input; + +const styles = StyleSheet.create({ + container: { + width: "100%", +}, + title: { + fontSize: 14, + color: "#7b7171ff", + marginTop: 20, + marginBottom: 5, +}, + input: { + flex: 1, + color: "black", + }, + wrap: { + width: "100%", + height: 49, + borderWidth: 1, + borderRadius: 7, + borderColor: "#2F6BFF", + flexDirection: "row", + alignItems: "center", + paddingHorizontal: 10, + }, + icon: { + marginRight: 5, + }, +}); \ No newline at end of file diff --git a/src/context/UserContext.js b/src/context/UserContext.js new file mode 100644 index 0000000..4cd7311 --- /dev/null +++ b/src/context/UserContext.js @@ -0,0 +1,13 @@ +import React, { createContext, useState } from "react"; + +export const UserContext = createContext(); + +export function UserProvider({ children }) { + const [user, setUser] = useState(null); + + return ( + + {children} + + ); +} diff --git a/src/navigaitons/AuthStack.js b/src/navigaitons/AuthStack.js new file mode 100644 index 0000000..e8663d7 --- /dev/null +++ b/src/navigaitons/AuthStack.js @@ -0,0 +1,35 @@ +import React from "react"; +import { createNativeStackNavigator } from "@react-navigation/native-stack"; +import SignInScreen from "../screens/SignInScreen"; +import ListScreen from "../screens/ListScreen"; + +const Stack = createNativeStackNavigator(); + +const AuthStack = () => { + return ( + + + + + ); +}; + +export default AuthStack; diff --git a/src/screens/ListScreen.js b/src/screens/ListScreen.js new file mode 100644 index 0000000..a4f53b0 --- /dev/null +++ b/src/screens/ListScreen.js @@ -0,0 +1,67 @@ +import React, { useContext, useState, memo } from "react"; +import { View, Text, FlatList, Pressable, StyleSheet } from "react-native"; +import { UserContext } from "../context/UserContext"; + +export default function ListScreen() { + const { user } = useContext(UserContext); + const [todos, setTodos] = useState([ + { id: 1, text: "잠자기", done: false }, + { id: 2, text: "밥먹기", done: false }, + { id: 3, text: "GDG 과제하기..", done: true}, + ]); + + const toggle = (id) => { + setTodos((prev) => + prev.map((t) => (t.id === id ? { ...t, done: !t.done } : t)) + ); + }; + + const remove = (id) => { + setTodos((prev) => prev.filter((t) => t.id !== id)); + }; + + const TodoItem = memo(({ item }) => ( + + toggle(item.id)}> + {item.done ? "O" : "X"} {/*체크 아이콘 나중에 수정할 예정*/} + + + + {item.text} + + + remove(item.id)}> + 쓰레기통 {/*스레기통 아이콘 나중에 수정할 예정*/} + + + )); + + return ( + + 리스트 페이지 + + + {user ? `로그인: ${user.email}` : "로그인 정보 없음"} + + + item.id.toString()} + renderItem={({ item }) => } + ListEmptyComponent={할 일을 추가해주세요} + /> + + ); +} + +const styles = StyleSheet.create({ + container: { flex: 1, paddingTop: 50, paddingHorizontal: 20 }, + title: { fontSize: 22, fontWeight: "bold", marginBottom: 10 }, + user: { marginBottom: 20, fontSize: 14 }, + row: { flexDirection: "row", alignItems: "center", paddingVertical: 10 }, + check: { fontSize: 24, marginRight: 10 }, + text: { flex: 1, fontSize: 16 }, + done: { textDecorationLine: "line-through", color: "#777" }, + delete: { fontSize: 20, marginLeft: 10 }, + empty: { textAlign: "center", marginTop: 30, fontSize: 16 }, +}); diff --git a/src/screens/SignInScreen.js b/src/screens/SignInScreen.js new file mode 100644 index 0000000..0ac8abe --- /dev/null +++ b/src/screens/SignInScreen.js @@ -0,0 +1,98 @@ +import React, { useState, useEffect, useContext } from "react"; +import { View, Image, StyleSheet, KeyboardAvoidingView, Platform, Pressable, Keyboard, Alert } from "react-native"; +import Input, { KeyboardTypes } from "../componets/Input"; +import Button from "../componets/Button"; +import { signInAsync } from "../api/auth"; +import { UserContext } from "../context/UserContext"; + + +export default function SignInScreen({ navigation }) { + const { user, setUser } = useContext(UserContext); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [disabled, setDisabled] = useState(true); + const [loading, setLoading] = useState(false); + + useEffect(() => { + setDisabled(!(email && password)); + }, [email, password]); + + const onSubmit = async () => { + if (disabled || loading) return; + + Keyboard.dismiss(); + setLoading(true); + + + + try { + const user = await signInAsync(email.trim(), password); + + setUser(user); + + + Alert.alert("로그인 성공입니다."); + navigation.navigate("List"); + } catch (error) { + Alert.alert("로그인 실패", "아이디나 비밀번호를 확인해주세여"); + + console.log('로그인 에러:', error) + } finally { + setLoading(false); + } +}; + + +return ( + + Keyboard.dismiss()}> + + + + + + +