Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
99d392c
feat: 기록장 업로드 프로그레스 바 - 임시
ljh130334 Aug 3, 2025
d6c7ed6
design: 반응형 수정3
heeeeyong Aug 3, 2025
a64e2c7
Merge pull request #70 from THIP-TextHip/main
heeeeyong Aug 3, 2025
1b25cd1
design: 반응형 수정4
heeeeyong Aug 3, 2025
6273e8b
Merge branch 'develop' of https://github.com/THIP-TextHip/THIP-Web in…
heeeeyong Aug 3, 2025
bef817f
Merge pull request #72 from THIP-TextHip/feat/api-auth
heeeeyong Aug 3, 2025
b89d210
feat: 기록장 탭 하단 업로드 프로그레스 바 구현
ljh130334 Aug 4, 2025
e380515
feat: 프로그레스바 스타일링 일부 수정
ljh130334 Aug 4, 2025
62ceda4
fix: TypeScript 오류 수정 - useEffect 의존성 및 Snackbar props
ljh130334 Aug 4, 2025
f2b1a27
fix: PollCreationSection focusStates 동기화를 useEffect로 이동
ljh130334 Aug 4, 2025
b1dec0c
refactor: Memory 컴포넌트 리팩토링 - 중복 코드 제거 및 최적화
ljh130334 Aug 4, 2025
e4f7cd9
Merge pull request #76 from THIP-TextHip/feat/progressbar
ljh130334 Aug 4, 2025
6a2adf7
feat: 파비콘, 타이틀 설정&OG 태그설정
heeeeyong Aug 5, 2025
7e7662a
feat: 파비콘, 타이틀 & OG태그 설정
heeeeyong Aug 5, 2025
364fabc
feat: 파비콘, 아이콘 설정&OG 태그설정 2
heeeeyong Aug 5, 2025
82ba29e
feat: 피드 글 상세보기 페이지 API 연동
heeeeyong Aug 5, 2025
ebbc544
feat: 내 띱 목록 페이지 경로 추가
heeeeyong Aug 5, 2025
e035e23
fix: 타입 선언 추가 및 수정
heeeeyong Aug 5, 2025
d6299f4
feat: 내 피드 탭 API 연동
heeeeyong Aug 5, 2025
5006877
feat: 전체 피드 조회 API 연동
heeeeyong Aug 5, 2025
7cd41fc
feat: 다른 사용자 피드 조회 API 연동
heeeeyong Aug 5, 2025
3402140
feat: 피드 저장 API 연동
heeeeyong Aug 5, 2025
b16d633
feat: 유저 프로필 및 팔로우 관련 API 연동
heeeeyong Aug 5, 2025
e754af1
feat: 피드 글 상세보기 페이지 API 연동
heeeeyong Aug 5, 2025
5e7afe5
fix: 팔로우 관련 API 로직 수정
heeeeyong Aug 5, 2025
f1c950c
fix: Reply Props 타입 변수명 수정
heeeeyong Aug 5, 2025
5d90cc6
Merge branch 'feat/api-feeds' of https://github.com/THIP-TextHip/THIP…
heeeeyong Aug 5, 2025
ee1b038
fix: 충돌 수정
heeeeyong Aug 5, 2025
cbb2fb3
fix: SubReply 타입 변수명 수정
heeeeyong Aug 5, 2025
4b4a2b0
design: TotalBar 반응형 적용
heeeeyong Aug 6, 2025
b7b7ce8
fix: 파비콘, 미리보기 이미지 경로 수정
heeeeyong Aug 6, 2025
a03ff13
feat: 피드 댓글 작성 API 연동
heeeeyong Aug 6, 2025
8e54c85
Merge branch 'develop' of https://github.com/THIP-TextHip/THIP-Web in…
heeeeyong Aug 6, 2025
c7242cc
Merge pull request #77 from THIP-TextHip/feat/api-feeds
heeeeyong Aug 6, 2025
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
8 changes: 6 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/svg+xml" href=".assets/custom_favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<title>THIP, 독서를 기록하는 가장 힙한 방법</title>
<meta property="og:title" content="THIP, 독서를 기록하는 가장 힙한 방법" />
<meta property="og:description" content="커뮤니티형 독서 기록 플랫폼. 띱. THIP." />
<meta property="og:image" content="https://thip.co.kr/assets/thumbnail.png" />
<meta property="og:url" content="https://thip.co.kr" />
</head>
<body>
<div id="root"></div>
Expand Down
11 changes: 11 additions & 0 deletions public/assets/custom_favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/thumbnail.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions src/api/comments/postReply.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { apiClient } from '../index';

export interface PostReplyRequest {
content: string;
isReplyRequest: boolean;
parentId: number | null;
postType: 'FEED' | 'RECORD' | 'VOTE';
}

export interface PostReplyResponse {
isSuccess: boolean;
code: number;
message: string;
data: {
commentId: number;
};
}

export const postReply = async (postId: number, request: PostReplyRequest) => {
const response = await apiClient.post<PostReplyResponse>(`/comments/${postId}`, request);
return response.data;
};
43 changes: 43 additions & 0 deletions src/api/feeds/getFeedDetail.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { apiClient } from '../index';

// 피드 상세 정보 타입 (PostData + 추가 필드들)
export interface FeedDetailData {
feedId: number;
creatorId: number;
creatorNickname: string;
creatorProfileImageUrl: string;
alias: string;
aliasColor: string;
postDate: string;
isbn: string;
bookTitle: string;
bookAuthor: string;
contentBody: string;
contentsUrl: string[];
likeCount: number;
commentCount: number;
isSaved: boolean;
isLiked: boolean;
tagList: string[];
}

// API 응답 타입
export interface FeedDetailResponse {
success: boolean;
code: number;
message: string;
data: FeedDetailData;
}

// 피드 상세 조회 API 함수
export const getFeedDetail = async (feedId: number) => {
const response = await apiClient.get<FeedDetailResponse>(`/feeds/${feedId}`);
return response.data;
};

/*
// 피드 상세 정보 조회
const feedDetail = await getFeedDetail(123);
console.log(feedDetail.data.feedId); // 123
console.log(feedDetail.data.tagList); // ["태그1", "태그2"]
*/
2 changes: 1 addition & 1 deletion src/api/feeds/getMyFeed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export interface GetMyFeedParams {
}

// 내 피드 조회 API 함수
export const getMyFeeds = async (params?: GetMyFeedParams): Promise<MyFeedResponse> => {
export const getMyFeeds = async (params?: GetMyFeedParams) => {
const queryParams = new URLSearchParams();

// cursor가 있을 때만 쿼리 파라미터에 추가
Expand Down
23 changes: 23 additions & 0 deletions src/api/feeds/getMyProfile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { apiClient } from '../index';
import type { MyProfileData } from '@/types/profile';

// API 응답 타입
export interface MyProfileResponse {
isSuccess: boolean;
code: number;
message: string;
data: MyProfileData;
}

// 내 프로필 조회 API 함수
export const getMyProfile = async () => {
const response = await apiClient.get<MyProfileResponse>('/feeds/mine/info');
return response.data;
};

/*
// 내 프로필 정보 조회
const myProfile = await getMyProfile();
console.log(myProfile.data.nickname); // 닉네임
console.log(myProfile.data.followerCount); // 팔로워 수
*/
40 changes: 40 additions & 0 deletions src/api/feeds/getOtherFeed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { apiClient } from '../index';

// 다른 사용자의 피드 아이템 타입
export interface OtherFeedItem {
feedId: number;
postDate: string;
isbn: string;
bookTitle: string;
bookAuthor: string;
contentBody: string;
contentsUrl: string[];
likeCount: number;
commentCount: number;
isSaved: boolean;
}

// API 응답 데이터 타입
export interface OtherFeedData {
feedList: OtherFeedItem[];
}

// API 응답 타입
export interface OtherFeedResponse {
success: boolean;
code: number;
message: string;
data: OtherFeedData;
}

// 다른 사용자 피드 조회 API 함수
export const getOtherFeed = async (userId: number) => {
const response = await apiClient.get<OtherFeedResponse>(`/feeds/users/${userId}`);
return response.data;
};

/*
다른 사용자의 피드 리스트 조회
const otherUserFeed = await getOtherFeed(123);
console.log(otherUserFeed.data.feedList); // OtherFeedItem[]
*/
2 changes: 1 addition & 1 deletion src/api/feeds/getTotalFeed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export interface GetTotalFeedParams {
cursor?: string; // 첫 페이지는 null 또는 없음, 다음 페이지부터는 nextCursor 값 사용
}

export const getTotalFeeds = async (params?: GetTotalFeedParams): Promise<TotalFeedResponse> => {
export const getTotalFeeds = async (params?: GetTotalFeedParams) => {
const queryParams = new URLSearchParams();

// cursor가 있을 때만 쿼리 파라미터에 추가
Expand Down
50 changes: 50 additions & 0 deletions src/api/feeds/postSave.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { apiClient } from '../index';

// 요청 바디 타입
export interface SaveFeedRequest {
type: boolean; // true: 저장, false: 저장 취소
}

// 응답 데이터 타입
export interface SaveFeedData {
feedId: number;
isSaved: boolean;
}

// API 응답 타입
export interface SaveFeedResponse {
isSuccess: boolean;
code: number;
message: string;
data?: SaveFeedData; // 성공 시에만 존재
}

// 피드 저장/저장 취소 API 함수
export const postSaveFeed = async (feedId: number, isSaved: boolean) => {
const requestBody: SaveFeedRequest = {
type: isSaved, // 현재 저장 상태로 변경
};

const response = await apiClient.post<SaveFeedResponse>(`/feeds/${feedId}/saved`, requestBody);
return response.data;
};

/*
사용 방법:

// 피드 저장
const saveResult = await postSaveFeed(123, true);
if (saveResult.isSuccess) {
console.log('저장 성공:', saveResult.data?.isSaved);
} else {
console.log('저장 실패:', saveResult.message);
}

// 피드 저장 취소
const unsaveResult = await postSaveFeed(123, false);
if (unsaveResult.isSuccess) {
console.log('저장 취소 성공:', unsaveResult.data?.isSaved);
} else {
console.log('저장 취소 실패:', unsaveResult.message);
}
*/
36 changes: 36 additions & 0 deletions src/api/users/getFollowerList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { apiClient } from '../index';
import type { FollowData } from '@/types/follow';

export interface FollowerListResponse {
isSuccess: boolean;
code: number;
message: string;
data: {
followers: FollowData[];
nextCursor: string;
isLast: boolean;
};
}

export interface GetFollowerListParams {
size?: number; // 1~10 사이, default: 10
cursor?: string | null; // 첫 요청시 null, 이후 nextCursor 값
}

export const getFollowerList = async (userId: string, params?: GetFollowerListParams) => {
const queryParams = new URLSearchParams();

const size = params?.size || 10;
if (size >= 1 && size <= 10) {
queryParams.append('size', size.toString());
}

if (params?.cursor) {
queryParams.append('cursor', params.cursor);
}

const url = `/users/${userId}/followers${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;

const response = await apiClient.get<FollowerListResponse>(url);
return response.data;
};
36 changes: 36 additions & 0 deletions src/api/users/getFollowingList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { apiClient } from '../index';
import type { FollowData } from '@/types/follow';

export interface FollowingListResponse {
isSuccess: boolean;
code: number;
message: string;
data: {
followings: FollowData[];
nextCursor: string;
isLast: boolean;
};
}

export interface GetFollowingListParams {
size?: number; // 1~10 사이, default: 10
cursor?: string | null; // 첫 요청시 null, 이후 nextCursor 값
}

export const getFollowingList = async (params?: GetFollowingListParams) => {
const queryParams = new URLSearchParams();

const size = params?.size || 10;
if (size >= 1 && size <= 10) {
queryParams.append('size', size.toString());
}

if (params?.cursor) {
queryParams.append('cursor', params.cursor);
}

const url = `/users/my-followings${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;

const response = await apiClient.get<FollowingListResponse>(url);
return response.data;
};
22 changes: 22 additions & 0 deletions src/api/users/getOtherProfile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { apiClient } from '../index';
import type { OtherProfileData } from '@/types/profile';
// API 응답 타입
export interface OtherProfileResponse {
isSuccess: boolean;
code: number;
message: string;
data: OtherProfileData;
}

// 다른 사용자 프로필 조회 API 함수
export const getOtherProfile = async (userId: number) => {
const response = await apiClient.get<OtherProfileResponse>(`/feeds/users/${userId}/info`);
return response.data;
};

/*
// 다른 사용자의 프로필 정보 조회
const otherProfile = await getOtherProfile(123);
console.log(otherProfile.data.nickname); // 닉네임
console.log(otherProfile.data.followerCount); // 팔로워 수
*/
27 changes: 27 additions & 0 deletions src/api/users/postFollow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { apiClient } from '../index';

export interface FollowRequest {
type: boolean; // true: 팔로우, false: 언팔로우
}

export interface FollowResponse {
isSuccess: boolean;
code: number;
message: string;
data: {
isFollowing: boolean; // 바뀐 팔로우 상태
};
}

export interface FollowErrorResponse {
isSuccess: false;
code: number;
message: string;
}

export const postFollow = async (followingUserId: number, type: boolean) => {
const response = await apiClient.post<FollowResponse>(`/users/following/${followingUserId}`, {
type,
});
return response.data;
};
17 changes: 15 additions & 2 deletions src/components/common/Post/PostFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import comment from '../../../assets/feed/comment.svg';
import save from '../../../assets/feed/save.svg';
import activeSave from '../../../assets/feed/activeSave.svg';
import lockIcon from '../../../assets/feed/lockIcon.svg';
import { postSaveFeed } from '@/api/feeds/postSave';

const Container = styled.div<{ isDetail: boolean }>`
width: 100%;
Expand Down Expand Up @@ -74,8 +75,20 @@ const PostFooter = ({
setLikeCount(prev => (liked ? prev - 1 : prev + 1));
};

const handleSave = () => {
setSaved(!saved);
const handleSave = async () => {
try {
const response = await postSaveFeed(feedId, !saved);

if (response.isSuccess) {
// 성공 시 상태 업데이트
setSaved(response.data?.isSaved ?? !saved);
console.log('저장 상태 변경 성공:', response.data?.isSaved);
} else {
console.error('저장 상태 변경 실패:', response.message);
}
} catch (error) {
console.error('저장 API 호출 실패:', error);
}
};

const handleComment = () => {
Expand Down
Loading