Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Making this branch complete #24

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
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 package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
"postcss": "8.4.49",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-draggable": "4.4.6",
"react-intersection-observer": "9.15.1",
"react-router-dom": "7.1.1",
"tailwindcss": "3.4.17"
},
Expand Down
92 changes: 57 additions & 35 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,58 +1,80 @@
import './index.css';

import { createContext } from 'react';
import { createContext, useState } from 'react';
import { Navigate, Route, Routes } from 'react-router-dom';

import { useAuth } from './hooks/useAuth';
import ExplorePage from './pages/ExplorePage';
import FriendMapPage from './pages/FriendMapPage';
import LoginPage from './pages/LoginPage';
import MainPage from './pages/MainPage';
import MessagePage from './pages/MessagePage';
import PostDetailPage from './pages/PostDetailPage';
import ProfileEditPage from './pages/ProfileEditPage';
import ProfilePage from './pages/ProfilePage';
import RegisterPage from './pages/RegisterPage';
import type { LoginContextType } from './types/auth';
import type { SearchContextType } from './types/search';

export const LoginContext = createContext<LoginContextType | null>(null);
export const SearchContext = createContext<SearchContextType | null>(null);

export const App = () => {
const auth = useAuth();
const [isSearchOpen, setIsSearchOpen] = useState(false);

return (
<LoginContext.Provider value={auth}>
<Routes>
<Route
path="/"
element={
auth.isLoggedIn ? (
<MainPage />
) : (
<LoginPage handleIsLoggedIn={auth.handleIsLoggedIn} />
)
}
/>
<Route
path="/register"
element={
auth.isLoggedIn ? (
<Navigate to="/" />
) : (
<RegisterPage handleIsLoggedIn={auth.handleIsLoggedIn} />
)
}
/>
<Route
path="/explore"
element={auth.isLoggedIn ? <ExplorePage /> : <Navigate to="/" />}
/>
<Route
path="/:username"
element={auth.isLoggedIn ? <ProfilePage /> : <Navigate to="/" />}
/>
<Route
path="/accounts/edit"
element={auth.isLoggedIn ? <ProfileEditPage /> : <Navigate to="/" />}
/>
</Routes>
<SearchContext.Provider value={{ isSearchOpen, setIsSearchOpen }}>
<Routes>
<Route
path="/"
element={
auth.isLoggedIn ? (
<MainPage />
) : (
<LoginPage handleIsLoggedIn={auth.handleIsLoggedIn} />
)
}
/>
<Route
path="/register"
element={
auth.isLoggedIn ? (
<Navigate to="/" />
) : (
<RegisterPage handleIsLoggedIn={auth.handleIsLoggedIn} />
)
}
/>
<Route
path="/explore"
element={auth.isLoggedIn ? <ExplorePage /> : <Navigate to="/" />}
/>
<Route
path="/messages"
element={auth.isLoggedIn ? <MessagePage /> : <Navigate to="/" />}
/>
<Route
path="/:username"
element={auth.isLoggedIn ? <ProfilePage /> : <Navigate to="/" />}
/>
<Route
path="/accounts/edit"
element={
auth.isLoggedIn ? <ProfileEditPage /> : <Navigate to="/" />
}
/>
<Route
path="/post/:postId"
element={auth.isLoggedIn ? <PostDetailPage /> : <Navigate to="/" />}
/>
<Route
path="/map"
element={auth.isLoggedIn ? <FriendMapPage /> : <Navigate to="/" />}
/>
</Routes>
</SearchContext.Provider>
</LoginContext.Provider>
);
};
78 changes: 78 additions & 0 deletions src/api/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { myProfile } from './profile';

interface SignupRequest {
username: string;
password: string;
full_name: string;
email: string;
phone_number: string;
}

export const signup = async (formData: SignupRequest) => {
const response = await fetch(
'https://waffle-instaclone.kro.kr/api/user/signup',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData),
},
);

if (!response.ok) {
const errorData = (await response.json()) as { detail?: string };
throw new Error(errorData.detail ?? '회원가입에 실패했습니다.');
}
};

interface SignInResponse {
access_token: string;
refresh_token: string;
}

export const signin = async (username: string, password: string) => {
const response = await fetch(
'https://waffle-instaclone.kro.kr/api/user/signin',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
username,
password,
}),
},
);

// First check if we got a JSON response
const contentType = response.headers.get('content-type');
if (contentType?.includes('application/json') === false) {
const text = await response.text();
console.error('Non-JSON response:', text);
throw new Error('Server returned non-JSON response');
}

if (response.ok) {
try {
const data = (await response.json()) as SignInResponse;
localStorage.setItem('access_token', data.access_token);
localStorage.setItem('refresh_token', data.refresh_token);
const profileResponse = await myProfile(data.access_token);
if (profileResponse === null) {
throw new Error('Failed to fetch user profile');
}
return profileResponse;
} catch (err) {
console.error('Error parsing JSON response:', err);
throw new Error('Invalid response format from server');
}
}

if (response.status === 401) {
throw new Error('아이디 또는 비밀번호가 일치하지 않습니다.');
}

throw new Error('로그인 중 오류가 발생했습니다.');
};
85 changes: 85 additions & 0 deletions src/api/comment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import type { Comment } from '../types/post';

export const fetchComments = async (postId: string) => {
const response = await fetch(
`https://waffle-instaclone.kro.kr/api/comment/list/${postId}`,
{
headers: {
Authorization: `Bearer ${localStorage.getItem('access_token') as string}`,
},
},
);
return response.json() as Promise<Comment[]>;
};

interface CommentRequest {
comment_text: string;
post_id: number;
parent_id: number;
}

interface CommentResponse {
comment_id: number;
user_id: number;
post_id: number;
parent_id: number;
comment_text: string;
}

export const createComment = async (
data: CommentRequest,
): Promise<CommentResponse> => {
const response = await fetch(
`https://waffle-instaclone.kro.kr/api/comment/`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${localStorage.getItem('access_token') as string}`,
},
body: JSON.stringify(data),
},
);

if (!response.ok) {
throw new Error('Failed to create comment');
}

return (await response.json()) as CommentResponse;
};

// export const updateComment = async (
// commentId: number,
// commentText: string
// ): Promise<Comment> => {
// const response = await fetch(`https://waffle-instaclone.kro.kr/api/comment/${commentId}`, {
// method: 'PATCH',
// headers: {
// 'Content-Type': 'application/json',
// },
// body: JSON.stringify({ comment_text: commentText }),
// credentials: 'include',
// });

// if (!response.ok) {
// throw new Error('Failed to update comment');
// }

// return response.json() as Promise<Comment>;
// };

export const deleteComment = async (commentId: number): Promise<void> => {
const response = await fetch(
`https://waffle-instaclone.kro.kr/api/comment/${commentId}`,
{
method: 'DELETE',
headers: {
Authorization: `Bearer ${localStorage.getItem('access_token') as string}`,
},
},
);

if (!response.ok) {
throw new Error('Failed to delete comment');
}
};
Loading