Skip to content

Commit 33df370

Browse files
kimnamheeeec0912jyclaude
authored
배포 (#119)
* ✨ feat(components): add stories field * ✨ feat(stories): add link to story route * ✨ feat: msw 구조 수정 * ✨ feat: msw post db 업데이트 및 기본 레이아웃 구현 * ✨ feat: 사진 기본 ui 구현 * ✨ feat: 하트 색깔 및 불투명도 수정 * ✨ feat: 하트 올라가는 애니메이션 구현 * ✨ feat: 하트 애니메이션 구체화 및 버그 수정 * ✨ feat(msw): add common response type * 🐛 fix(msw): fix common response type field name * ✨ feat(msw): add mock users * ✨ feat(msw): add follow handlers * 💎 style(sidebar): add padding * ✨ feat(components): add follow list modal component needs to be fixed based on real api * ✨ feat(components): add mock data / match data type * 💎 style(search): adjust search drawer style * ✨ feat: ... (설정) 화면 모달 구현 * ✨ feat(search): apply debounce to input value * ✨ feat: msw 구현 (comment) * 🧹 chore: remove duplicate key * ✨ feat: 댓글 ui * ✨ feat: 댓글 설정창(...) ui 구현 * ✨ feat: ... 모달 로직 수정 * ✨ feat: 글 본문 및 프로필 ui 수정 (여백 정리) * ✨ feat(msw): warn unhandled requests for debugging * ✨ feat(gitignore): add env * ✨ feat(providers): add query client provider * ✨ feat(api): add test (health check) handler * ✨ feat(route): add test route for api connection test * ✨ feat: 액션 바 (상호작용) ui 구현 * ✨ feat: env 삭제 * 💎 style(sidebar): update layout stability * ✨ feat(sidebar): updage search drawer toggle logic * ✨ feat(sidebar): remain page width when opening search drawer * ✨ feat(components): add pagination * ✨ feat(components): add shared components lazyimage / card * ✨ feat(msw): add post mock data * ✨ feat(api): add feed schema / types * ✨ feat(api): add handler / api function * ✨ feat(home): add feed ui * ✨ feat(actions): add env variable on cd workflow * 💎 style(sidebar): collapse sidebar on search drawer toggle * 🐛 fix(route): update route from profile_id to post_id * ✨ feat(feed): link to post detail on click * ✨ feat(components): add dropdown * ✨ feat(msw): add album handler * ✨ feat(api): add album api call functions * ✨ feat(api): add album api call function * 🐛 fix(msw): adjust handler order * 💎 style(dropdown): remove circle icon * ✨ feat(album): add album dropdown * 🧪 test(album): add album dropdown tests * 🐛 fix(actions): apply secrets * 🧹 chore: update test:ci command option * ✨ feat(msw): add bookmarks handler * ✨ feat(bookmarks): add bookmarked posts ui * ✨ feat(postdetail): return to previous page when closing page * ✨ feat(postdetail): validate search params with zod * ✨ feat: 하단 푸터 - 위치 화면 구현 * ✨ feat: 하단 푸터 수정 (Instagram Lite 추가) * ✨ feat: 푸터 완성 * ✨ feat: 푸터 ui, 기능, 경로 설정 완성 * ✨ feat: 로그인 msw 설정 * ✨ feat: 로그인 검사 로직 추가 및 카카오톡 로그인 ui 구현 * ✨ feat: 파일 구조 변경 실제 구현과 유사하게 경로 변경 & 유지보수 및 관리 용이 목적 * ✨ feat: 푸터 완성 * ✨ feat: 비밀번호 찾기 창 ui 수정 * 💎 style(createmodal): fix modal ratio * ✨ feat(sidebar): 만들기 버튼을 게시글/스토리 업로드로 분리 - 기존 '만들기' 버튼을 '게시글 업로드'와 '스토리 업로드'로 분리 - 아이콘 구분: SquarePlus(게시글), CirclePlus(스토리) - 모바일 하단바 배경색 추가 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * ✨ feat(create-story): 스토리 업로드 기능 구현 - 사진 1장만 업로드 가능 (초과 시 toast 알림) - 9:16 비율 미리보기, 검은 배경으로 빈 공간 채움 - 본문/앨범 단계 없이 바로 공유하기 - 이미지 업로드 후 헤더 타이틀 숨김 처리 - 이탈 시 확인 다이얼로그 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * 💎 style(ui): 모달 외곽선 제거 및 안내 문구 word-break 추가 - DialogContent 기본 스타일에서 border 제거 - 이미지 업로드 안내 문구에 break-keep 추가 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * 💎 style(story): 레이아웃 수정 - xs 너비에서 미리보기, dropzone 영역의 높이가 지나치게 높아지는 문제 해결 * 🧹 chore(constants): add constant for api errors * ✨ feat(msw): add handlers for image upload * ✨ feat(msw): add handlers for stories * ♻️ refactor(component): change component name * ✨ feat(msw): add search handler * ✨ feat(explore): separate page component * ♻️ refactor: update variable names * 🧹 chore(msw): add mock data * ✨ feat(explore): add ui / api handling * ✨ feat(profile): add albums tab * ✨ feat(profile): add albums summary ui * 💎 style(album): update badge background color * ✨ feat(msw): add relations to msw mock data * ✨ feat(msw): add like related handlers * ✨ feat(msw): add bookmark toggle handler * ✨ feat: 비밀번호 찾기 기능 구현 * ✨ feat(like/bookmark): add like, bookmark toggle functionality * ✨ feat: footer 파일 위치 변경 * ✨ feat(home): add footer * ✨ feat(album): integrate get album detail api * ✨ feat: 컨플릭트 수정 * 🐛 fix(route): fix routeTree syntax error * ✨ feat: 회원가입 ui 구현 * ✨ feat: 회원가입 아이디 중복 체크 로직 구현 * ✨ feat: 회원가입 컴포넌트 분리 및 아이디 랜덤 생성 로직 수정 * ✨ feat: reset.tsx 구조 분리 * ✨ feat(msw): add post create handler * ✨ feat(create-post): add functionality and ui for creating post * ✨ feat(create-post): show close button on post create finish * ✨ feat(utils): add crop image util function * ✨ feat(create-story): integrate upload story api / image crop logic * 🐛 fix(test): add react query provider to tests * ✨ feat: 회원가입 정보 입력 화면 구현 * ✨ feat(stories): implement story feed api / integrate with component * 🐛 fix(route): update route tree file * 💎 style: layout stability * ✨ feat: 회원가입 구현 아직 토큰 관리 미흡, 이메일 전송 로직 미구현 * ✨ feat: 회원가입 라우트 간 보안 강화 로직 추가 * ✨ feat: 회원가입 푸터 라우트 연결 * ✨ feat: 푸터 버그 수정 * ✨ feat: 로그인 버그 수정 * ✨ feat: 로그인 버그 수정 * ✨ feat(msw): add search, recent search api handler * ✨ feat(search): add api function for search * ✨ feat(api): add followed / name to search api * ✨ feat(recent): add recent search keyword management * 🧹 chore: remove unused files * ✨ feat: 댓글 api 연결 * 🐛 fix(entity): update schema make name, profileImageUrl nullable * ✨ feat(ui): add fallback for null profileImages * ✨ feat: 댓글 추가 및 좋아요 api 연결 * ✨ feat(follow): integrate follow/unfollow apis * ✨ feat(user): add GET profile api function * ✨ feat(search): add staletime for cache control * 🐛 fix(entity): update schema make bio, name, profileImageUrl nullable * ✨ feat(routes): update profile routes * 🐛 fix(msw): fix field name success -> isSuccess * ♻️ refactor(schema): rename success to isSuccess in API response schemas Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * ♻️ refactor(api): update isSuccess field references in API functions Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * ♻️ refactor(auth): update isSuccess field references in auth components Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * ✨ feat(user): add fallback UI for null profileImageUrl Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * ♻️ refactor(search): update search UI components Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * ✨ feat(profile): update profile page and navigation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * ✨ feat: 로그인 토큰 구현 * 🐛 fix(msw): fix handler order * 🐛 fix(test): update api field name for test * ✨ feat: 댓글 api 연결 수정 및 로그인 토큰 작업, 회원가입 시 자동 로그인 구현 * ✨ feat: 로그인 버그 수정 * ✨ feat: 게시글 날짜 표현 방식 수정 * ✨ feat: 댓글창 ui 수정 * ✨ feat: 비행기 ui 삭제 * ✨ feat(]): 댓글 삭제 api * ✨ feat: 댓글 신고 기능 구현 * ✨ feat: 댓글 신고 기능 1차 구현 * ✨ feat: 댓글 아이콘 클릭 기능 구현 * ✨ feat: 댓글 수정 구현 * ✨ feat(]): 대댓글 로직 삭제 * ✨ feat: 버그 수정 * ✨ feat(shared): add query client * ✨ feat(entities): add getCurrentUser api and schema * ✨ feat(auth): integrate useCurrentUser and session state * ✨ feat(auth): add refresh flow in ky * ✨ feat(auth): integrate useAuth, AuthProvider to current user * 🧹 chore(app): use shared queryClient * ♻️ refactor(components): use useCurrentUser in auth/post * ✨ feat(msw): add /users/me handler * 🐛 fix(auth): remove cookie from request and fix retry logic * 🐛 fix: fix path * 🐛 fix(fix api field): followed -> isFollowed * ✨ feat: 좋아요, 북마크 api 연결 * ✨ feat: 좋아요 바 애니메이션 수정 * ✨ feat: 게시글 컴포넌트 ui, 기능, api 연결 완료 * ✨ feat: 좋아요, 북마크, 댓글 관련 api 연결 및 기능 구현 * ✨ feat: 말풍선 아이콘 좌우반전 * ✨ feat: ... 모달 분기 구현 * ✨ feat: 내 계정 정보 모달 추가 * 🐛 fix(album): add logedInUser query parameter / update api endpoint * 🐛 fix(feed): update field name liked -> isLiked / bookmarked -> isBookmarked * 🐛 fix(post): update field name liked -> isLiked / bookmarked -> isBookmarked * 🐛 fix(create-post): fix upload strategies * 🐛 fix(story): fix story upload api content-type * 🐛 fix(follow): add loggedInUser query parameter * 🐛 fix(user): update field name me -> isMe * 🐛 fix(post / user): update field name * 🐛 fix(auth): add refresh token * ✨ feat(posts): add profile posts api * 🐛 fix(post): constrain mime types * ✨ feat: 수정 * ✨ feat: 게시글 삭제 기능 구현 * ✨ feat: 최종 수정 --------- Co-authored-by: c0912jy <c0912jy@gmail.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 339815f commit 33df370

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+2128
-1003
lines changed

src/components/auth/LoginCard.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ interface LoginResponse {
2020
message: string
2121
data: {
2222
accessToken: string
23+
refreshToken: string
2324
} | null
2425
}
2526

@@ -92,6 +93,7 @@ const LoginCard = () => {
9293

9394
if (res.isSuccess && res.data) {
9495
localStorage.setItem('accessToken', res.data.accessToken)
96+
localStorage.setItem('refreshToken', res.data.refreshToken)
9597
await invalidateCurrentUser()
9698
navigate({ to: '/' })
9799
} else {
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
interface AccountInfoModalProps {
2+
onClose: () => void
3+
nickname: string
4+
profileImageUrl: string | null
5+
}
6+
7+
export default function AccountInfoModal({
8+
onClose,
9+
nickname,
10+
profileImageUrl,
11+
}: AccountInfoModalProps) {
12+
return (
13+
<div className="fixed inset-0 z-[110] flex items-center justify-center bg-black/60 p-4">
14+
<div
15+
className="w-full max-w-[400px] overflow-hidden rounded-[12px] bg-white"
16+
onClick={(e) => e.stopPropagation()}
17+
>
18+
<div className="border-b border-gray-200 py-3 text-center font-bold">
19+
이 계정 정보
20+
</div>
21+
22+
<div className="flex flex-col items-center p-8">
23+
<div className="mb-4 h-20 w-20 overflow-hidden rounded-full bg-gray-100">
24+
{profileImageUrl ? (
25+
<img
26+
src={profileImageUrl}
27+
alt={nickname}
28+
className="h-full w-full object-cover"
29+
/>
30+
) : (
31+
<img
32+
src="/default-profile.png"
33+
alt="기본 프로필"
34+
className="h-full w-full object-cover"
35+
/>
36+
)}
37+
</div>
38+
<h2 className="text-[16px] font-bold">{nickname}</h2>
39+
<p className="mt-4 px-4 text-center text-[12px] leading-normal text-gray-500">
40+
신뢰할 수 있는 커뮤니티를 유지하기 위해 Instagram 계정에 대한 정보가
41+
표시됩니다.{' '}
42+
<a
43+
href="https://help.instagram.com/697961817256175"
44+
target="_blank"
45+
rel="noopener noreferrer"
46+
className="font-semibold text-[#00376B] hover:underline"
47+
>
48+
이 정보가 중요한 이유를 확인해보세요.
49+
</a>
50+
</p>
51+
</div>
52+
53+
<button
54+
onClick={onClose}
55+
className="w-full border-t border-gray-200 py-4 font-semibold active:bg-gray-100"
56+
>
57+
닫기
58+
</button>
59+
</div>
60+
<div className="absolute inset-0 -z-10" onClick={onClose} />
61+
</div>
62+
)
63+
}

src/components/post/CommentItem.tsx

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useMemo, useState } from 'react'
22
import { Heart, MoreHorizontal } from 'lucide-react'
3+
import { Link } from '@tanstack/react-router'
34
import { formatRelativeTime } from '../../utils/date.ts'
45
import CommentMenuModal from './CommentMenuModal'
56
import { Avatar, AvatarFallback, AvatarImage } from '@/shared/ui/avatar.tsx'
@@ -84,18 +85,30 @@ export default function CommentItem({
8485
onDoubleClick={() => onDoubleClick(comment.id)}
8586
>
8687
<div className="flex flex-1 items-start gap-3">
87-
<Avatar className="mt-0.5 h-8 w-8 shrink-0">
88-
<AvatarImage
89-
src={comment.profileImageUrl}
90-
alt={comment.nickname}
91-
className="object-cover"
92-
/>
93-
<AvatarFallback>{comment.nickname[0]}</AvatarFallback>
94-
</Avatar>
88+
<Link
89+
to="/$userId"
90+
params={{ userId: String(comment.userId) }}
91+
className="mt-0.5 shrink-0"
92+
onClick={(e) => e.stopPropagation()}
93+
>
94+
<Avatar className="h-8 w-8">
95+
<AvatarImage
96+
src={comment.profileImageUrl}
97+
alt={comment.nickname}
98+
className="object-cover"
99+
/>
100+
<AvatarFallback>{comment.nickname[0]}</AvatarFallback>
101+
</Avatar>
102+
</Link>
95103
<div className="text-sm leading-tight">
96-
<span className="mr-2 font-semibold text-black">
104+
<Link
105+
to="/$userId"
106+
params={{ userId: String(comment.userId) }}
107+
className="mr-2 font-semibold text-black transition-opacity hover:opacity-60"
108+
onClick={(e) => e.stopPropagation()}
109+
>
97110
{comment.nickname}
98-
</span>
111+
</Link>
99112
<span className="break-all text-black">{comment.content}</span>
100113

101114
<div className="mt-[7px] flex h-4 items-center gap-3 text-xs font-semibold text-gray-500">
@@ -112,7 +125,7 @@ export default function CommentItem({
112125
className="p-1 opacity-0 transition-opacity group-hover:opacity-100"
113126
onClick={handleMenuClick}
114127
>
115-
<MoreHorizontal className="h-4 w-4 text-gray-500" />
128+
<MoreHorizontal className="h-4 w-4 scale-x-[-1] text-gray-500" />
116129
</button>
117130
</div>
118131
</div>

src/components/post/EmbedModal.tsx

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { useState } from 'react'
2+
import { X } from 'lucide-react'
3+
4+
interface EmbedModalProps {
5+
onClose: () => void
6+
postId: number
7+
nickname: string
8+
}
9+
10+
export default function EmbedModal({
11+
onClose,
12+
postId,
13+
nickname,
14+
}: EmbedModalProps) {
15+
const [isCopied, setIsCopied] = useState(false)
16+
17+
const postUrl = `${window.location.origin}/posts/${postId}`
18+
19+
const embedCode =
20+
`<blockquote class="instagram-media" data-instgrm-captioned data-instgrm-permalink="${postUrl}" style="background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:540px; min-width:326px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><div style="padding:16px;"><a href="${postUrl}" style=" background:#FFFFFF; line-height:0; padding:0 0; text-align:center; text-decoration:none; width:100%;" target="_blank"><div style=" display: flex; flex-direction: row; align-items: center;"><div style="background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 40px; margin-right: 14px; width: 40px;"></div><div style="display: flex; flex-direction: column; flex-grow: 1; justify-content: center;"><div style=" background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; margin-bottom: 6px; width: 100px;"></div><div style=" background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; width: 60px;"></div></div></div><div style="padding: 19% 0;"></div><div style="display:block; height:50px; margin:0 auto 12px; width:50px;"><svg width="50px" height="50px" viewBox="0 0 60 60" version="1.1" xmlns="https://www.w3.org/2000/svg" xmlns:xlink="https://www.w3.org/1999/xlink"><g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g transform="translate(-511.000000, -20.000000)" fill="#000000"><g transform="translate(512.000000, 21.000000)"><path d="M30,0 C13.458,0 0,13.458 0,30 C0,46.542 13.458,60 30,60 C46.542,60 60,46.542 60,30 C60,13.458 46.542,0 30,0 Z M30,55 C16.215,55 5,43.785 5,30 C5,16.215 16.215,5 30,5 C43.785,5 55,16.215 55,30 C55,43.785 43.785,55 30,55 Z"></path></g></g></g></svg></div><div style="padding-top: 8px;"><div style=" color:#3897f0; font-family:Arial,sans-serif; font-size:14px; font-weight:550; line-height:18px;">이 게시물 보기</div></div></a><p style=" margin:8px 0 0 0; padding:0 4px;"><a href="${postUrl}" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">${nickname}님의 공유 게시물</a></p></div></blockquote>`.trim()
21+
22+
const handleCopy = async () => {
23+
try {
24+
await navigator.clipboard.writeText(embedCode)
25+
setIsCopied(true)
26+
setTimeout(() => {
27+
onClose()
28+
}, 2000)
29+
} catch (err) {
30+
console.error(err)
31+
}
32+
}
33+
34+
return (
35+
<div className="fixed inset-0 z-[110] flex items-center justify-center bg-black/60 p-4">
36+
<div
37+
className="relative w-full max-w-[500px] overflow-hidden rounded-[24px] bg-white p-6 shadow-xl"
38+
onClick={(e) => e.stopPropagation()}
39+
>
40+
<button
41+
onClick={onClose}
42+
className="absolute top-4 right-4 p-1 hover:opacity-60"
43+
>
44+
<X className="h-6 w-6 text-black" />
45+
</button>
46+
47+
<div className="mt-6 mb-4">
48+
<div className="scrollbar-hide h-[72px] overflow-y-auto rounded border border-gray-200 bg-white p-3 font-mono text-[14px] leading-normal break-all text-black">
49+
{embedCode}
50+
</div>
51+
</div>
52+
53+
<button
54+
onClick={handleCopy}
55+
disabled={isCopied}
56+
className={`w-full rounded-[12px] py-3 text-[16px] font-bold text-white transition-all ${
57+
isCopied
58+
? 'bg-[#4a5df9]'
59+
: 'bg-[#4a5df9] hover:opacity-90 active:opacity-80'
60+
}`}
61+
>
62+
{isCopied ? '퍼가기 코드가 복사되었습니다' : 'Embed 태그 복사'}
63+
</button>
64+
65+
<p className="mt-4 text-[13px] leading-tight text-gray-500">
66+
이 embed 태그를 사용하면 Instagram의{' '}
67+
<a
68+
href="https://help.instagram.com/581066165581870"
69+
target="_blank"
70+
rel="noopener noreferrer"
71+
className="text-[#00376B]"
72+
>
73+
API 이용 약관
74+
</a>
75+
에 동의하게 됩니다.
76+
</p>
77+
</div>
78+
<div className="absolute inset-0 -z-10" onClick={onClose} />
79+
</div>
80+
)
81+
}

0 commit comments

Comments
 (0)