Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
EXPO_PUBLIC_KAKAO_REST_API_KEY=fcc79e9199b5dbcaedfc00bb30b3d4af
EXPO_PUBLIC_SERVER_BASE_URL=https://api.dailysnap.app
3 changes: 2 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ node_modules
android
ios
build
dist
dist
tailwind.config.js
2 changes: 1 addition & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ node_modules
android
ios
build
dist
dist
37 changes: 37 additions & 0 deletions app.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
export default {
expo: {
name: "DailySnap-FE",
slug: "DailySnap-FE",
version: "1.0.0",
orientation: "portrait",
scheme: "dailysnap",
userInterfaceStyle: "light",
newArchEnabled: true,
splash: {
resizeMode: "contain",
backgroundColor: "#ffffff"
},
ios: {
supportsTablet: true,
bundleIdentifier: "com.jhsonny.DailySnapFE"
},
android: {
adaptiveIcon: {
backgroundColor: "#ffffff"
},
package: "com.jhsonny.DailySnapFE"
},
web: {
bundler: "metro",
output: "static",
favicon: "./assets/images/favicon.png"
},
plugins: [
"expo-router",
"expo-font"
],
experiments: {
typedRoutes: true
}
}
};
2 changes: 1 addition & 1 deletion app.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"output": "static",
"favicon": "./assets/images/favicon.png"
},
"plugins": ["expo-router", "expo-font"],
"plugins": ["expo-router", "expo-font", "@react-native-kakao/core"],
"experiments": {
"typedRoutes": true
}
Expand Down
46 changes: 46 additions & 0 deletions app/(tabs)/_layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Tabs } from "expo-router";

export default function TabsLayout() {
return (
<Tabs
screenOptions={{
headerShown: false,
}}
>
<Tabs.Screen
name="home"
options={{
title: "홈",
}}
/>

<Tabs.Screen
name="archive"
options={{
title: "아카이브",
}}
/>

<Tabs.Screen
name="upload"
options={{
title: "업로드",
}}
/>

<Tabs.Screen
name="awards"
options={{
title: "우수작",
}}
/>

<Tabs.Screen
name="profile"
options={{
title: "마이",
}}
/>
</Tabs>
);
}
6 changes: 6 additions & 0 deletions app/(tabs)/archive.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import React from "react";
import ArchivePage from "../../pages/archive/ArchivePage";

export default function ArchiveTab() {
return <ArchivePage />;
}
6 changes: 6 additions & 0 deletions app/(tabs)/awards.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import React from "react";
import AwardsPage from "../../pages/awards/AwardsPage";

export default function AwardsTab() {
return <AwardsPage />;
}
6 changes: 6 additions & 0 deletions app/(tabs)/home.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import React from "react";
import HomePage from "../../pages/home/HomePage";

export default function HomeTab() {
return <HomePage />;
}
6 changes: 6 additions & 0 deletions app/(tabs)/profile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import React from "react";
import ProfilePage from "../../pages/profile/ProfilePage";

export default function ProfileTab() {
return <ProfilePage />;
}
6 changes: 6 additions & 0 deletions app/(tabs)/upload.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import React from "react";
import UploadPage from "../../pages/upload/UploadPage";

export default function UploadTab() {
return <UploadPage />;
}
26 changes: 21 additions & 5 deletions app/_layout.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
import NetInfo from "@react-native-community/netinfo";
import { onlineManager, QueryClientProvider } from "@tanstack/react-query";
import { Stack } from "expo-router";
import { StatusBar } from "expo-status-bar";
import { SafeAreaProvider } from "react-native-safe-area-context";
import { queryClient } from "../shared/api/query-client";
import { AuthProvider } from "../features/auth/model/AuthContext";
import "../global.css";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Move global CSS import to the top of the file.

Global CSS imports should be placed before other imports to ensure styles are loaded first and avoid potential timing issues.

+import "../global.css";
 import NetInfo from "@react-native-community/netinfo";
 import { onlineManager, QueryClientProvider } from "@tanstack/react-query";
 import { Stack } from "expo-router";
 import { StatusBar } from "expo-status-bar";
 import { SafeAreaProvider } from "react-native-safe-area-context";
 import { queryClient } from "../shared/api/query-client";
 import { AuthProvider } from "../features/auth/model/AuthContext";
-import "../global.css";
🤖 Prompt for AI Agents
In app/_layout.tsx at line 8, move the import statement for "../global.css" to
the very top of the file before any other imports to ensure global styles are
loaded first and prevent timing issues.


onlineManager.setEventListener(setOnline => {
return NetInfo.addEventListener(state => {
setOnline(!!state.isConnected);
});
});

export { default as styled } from "nativewind";

export default function RootLayout() {
return (
<QueryClientProvider client={queryClient}>
<Stack>
<Stack.Screen name="index" />
</Stack>
</QueryClientProvider>
<SafeAreaProvider>
<QueryClientProvider client={queryClient}>
<AuthProvider>
<StatusBar style="auto" />
<Stack screenOptions={{ headerShown: false }}>
{/* 인증 관련 스크린 */}
<Stack.Screen name="index" />
<Stack.Screen name="login" />

{/* 메인 앱 스크린 (탭 네비게이션) */}
<Stack.Screen name="(tabs)" />
</Stack>
</AuthProvider>
</QueryClientProvider>
</SafeAreaProvider>
);
}
32 changes: 26 additions & 6 deletions app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,31 @@
import { SafeAreaView, ScrollView, Text } from "react-native";
/*eslint-disable */
import React, { useEffect } from "react";
import { useRouter } from "expo-router";
import { useAuth } from "../features/auth/model/AuthContext";
import { View, ActivityIndicator } from "react-native";

export default function App() {
const router = useRouter();
const { userInfo } = useAuth();

useEffect(() => {
const timer = setTimeout(() => {
if (userInfo) {
// 로그인된 사용자면, 홈 네비게이션으로 이동
router.replace("/(tabs)/home");
} else {
// 로그인되지 않았으면 로그인 페이지로 이동
router.replace("/login");
}
}, 100);

return () => clearTimeout(timer);
}, [userInfo, router]);

// 로딩 화면 표시. TODO: 추후에 디자인 된 로딩스피너로 수정
return (
<SafeAreaView style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<ScrollView style={{ width: "100%", paddingHorizontal: 16 }}>
<Text>App.tsx to start working on your app!</Text>
</ScrollView>
</SafeAreaView>
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<ActivityIndicator size="large" color="#195B35" />
</View>
);
}
6 changes: 6 additions & 0 deletions app/login.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import React from "react";
import LoginPage from "../pages/auth/LoginPage";

export default function Login() {
return <LoginPage />;
}
9 changes: 0 additions & 9 deletions app/login/index.tsx

This file was deleted.

19 changes: 19 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: [
'nativewind/babel',
[
'module-resolver',
{
root: ['./'],
extensions: ['.ios.js', '.android.js', '.js', '.ts', '.tsx', '.json'],
alias: {
'@': './',
},
},
],
],
};
};
3 changes: 0 additions & 3 deletions features/auth/hooks/useLogin.ts

This file was deleted.

58 changes: 58 additions & 0 deletions features/auth/model/AuthContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import type { ReactNode } from "react";
import React, { createContext, useContext, useState } from "react";

// 사용자 정보 타입 정의
interface UserInfo {
id: number;
email: string;
nickname: string;
profileImage?: string;
accessToken: string;
}
Comment on lines +5 to +11
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove duplicate interface definition.

The UserInfo interface is already defined in features/auth/model/types.ts. Import it instead of duplicating the definition to follow DRY principles and ensure consistency.

+import type { UserInfo } from "./types";
+
-// 사용자 정보 타입 정의
-interface UserInfo {
-  id: number;
-  email: string;
-  nickname: string;
-  profileImage?: string;
-  accessToken: string;
-}
-
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
interface UserInfo {
id: number;
email: string;
nickname: string;
profileImage?: string;
accessToken: string;
}
import type { UserInfo } from "./types";
🤖 Prompt for AI Agents
In features/auth/model/AuthContext.tsx around lines 5 to 11, remove the
duplicate UserInfo interface definition and instead import the UserInfo
interface from features/auth/model/types.ts. This will avoid redundancy and keep
the type definition consistent across the codebase.


interface AuthContextType {
userInfo: UserInfo | null; // 현재 로그인된 사용자 정보
login: (userInfo: UserInfo) => void; // 로그인 함수
logout: () => void; // 로그아웃 함수
isLoggedIn: boolean; // 로그인 상태 확인
}

// React Context 생성 (초기값은 undefined)
const AuthContext = createContext<AuthContextType | undefined>(undefined);

// 인증 컨텍스트를 사용하기 위한 커스텀 훅
export const useAuth = () => {
const context = useContext(AuthContext);
// AuthProvider 외부에서 사용하려고 하면 에러 발생
if (context === undefined) {
throw new Error("useAuth must be used within AuthProvider");
}
return context;
};

interface AuthProviderProps {
children: ReactNode;
}

// 인증 상태를 관리하는 Provider 컴포넌트
export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
// 사용자 정보 상태 관리 (null이면 로그아웃 상태)
const [userInfo, setUserInfo] = useState<UserInfo | null>(null);

const login = (userInfo: UserInfo) => {
setUserInfo(userInfo);
};

const logout = () => {
setUserInfo(null);
};

const isLoggedIn = !!userInfo;

// Context Provider로 하위 컴포넌트들에게 인증 상태와 함수들 제공
return (
<AuthContext.Provider value={{ userInfo, login, logout, isLoggedIn }}>
{children}
</AuthContext.Provider>
);
};
14 changes: 14 additions & 0 deletions features/auth/model/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export interface UserInfo {
id: number;
email: string;
nickname: string;
profileImage?: string;
accessToken: string;
}

export interface SocialLoginProps {
onLoginSuccess: (userInfo: UserInfo) => void;
onLoginError: (error: string) => void;
}

export type SocialProvider = "kakao" | "naver" | "google";
11 changes: 11 additions & 0 deletions features/auth/ui/GoogleLoginButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from "react";
import { TouchableOpacity, Text } from "react-native";
import type { SocialLoginProps } from "../model/types";

export const GoogleLoginButton: React.FC<SocialLoginProps> = ({ onLoginSuccess, onLoginError }) => {
return (
<TouchableOpacity>
<Text className="">구글로 시작하기</Text>
</TouchableOpacity>
);
Comment on lines +7 to +10
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Implement touch event handling and login logic.

The button currently lacks:

  1. onPress handler to trigger login flow
  2. Login implementation (similar to KakaoLoginButton)
  3. Styling (empty className attribute)
 <TouchableOpacity
+  onPress={() => {
+    // TODO: Implement Google login logic
+    onLoginError("Google login not implemented yet");
+  }}
 >
-  <Text className="">구글로 시작하기</Text>
+  <Text className="text-center py-3 px-6 bg-blue-500 text-white rounded-lg">
+    구글로 시작하기
+  </Text>
 </TouchableOpacity>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<TouchableOpacity>
<Text className="">구글로 시작하기</Text>
</TouchableOpacity>
);
<TouchableOpacity
onPress={() => {
// TODO: Implement Google login logic
onLoginError("Google login not implemented yet");
}}
>
<Text className="text-center py-3 px-6 bg-blue-500 text-white rounded-lg">
구글로 시작하기
</Text>
</TouchableOpacity>
);
🤖 Prompt for AI Agents
In features/auth/ui/GoogleLoginButton.tsx around lines 7 to 10, the
TouchableOpacity component is missing an onPress handler to initiate the Google
login flow, the actual login logic similar to KakaoLoginButton, and proper
styling instead of an empty className. Add an onPress prop that triggers the
Google login function, implement the login logic inside that handler, and
replace the empty className with appropriate styles for the button text.

};
Loading