diff --git a/src/components/common/Post/PostBody.tsx b/src/components/common/Post/PostBody.tsx index 77553dc..aea81a8 100644 --- a/src/components/common/Post/PostBody.tsx +++ b/src/components/common/Post/PostBody.tsx @@ -13,8 +13,8 @@ const PostBody = ({ feedId, contentUrls = [], }: PostBodyProps) => { - const navigate = useNavigate(); - const hasImage = contentUrls.length > 0; + const previewImages = contentUrls.slice(0, 3); + const hasImage = previewImages.length > 0; const contentRef = useRef(null); const [isTruncated, setIsTruncated] = useState(false); @@ -44,8 +44,17 @@ const PostBody = ({ {hasImage && ( <> - {contentUrls.map((src: string, i: number) => ( - + {previewImages.map((src: string, i: number) => ( + {`피드 ))} )} diff --git a/src/components/feed/FollowList.styled.ts b/src/components/feed/FollowList.styled.ts index d378755..b9a4c5a 100644 --- a/src/components/feed/FollowList.styled.ts +++ b/src/components/feed/FollowList.styled.ts @@ -14,10 +14,30 @@ export const Container = styled.div` .title { display: flex; flex-direction: row; + align-items: center; + gap: 4px; color: var(--color-white); font-size: ${typography.fontSize['2xs']}; font-weight: var(--font-weight-medium); line-height: 20px; + + img { + width: 14px; + height: 14px; + flex-shrink: 0; + } + } + + .titleSkeletonIcon { + width: 14px; + height: 14px; + flex-shrink: 0; + } + + .titleSkeletonText { + display: flex; + align-items: center; + height: 20px; } `; @@ -26,6 +46,7 @@ export const FollowContainer = styled.div` flex-direction: row; justify-content: space-between; align-items: center; + min-height: 58px; img { cursor: pointer; @@ -73,6 +94,37 @@ export const FollowContainer = styled.div` border: 0.5px solid #888; } } + + .skeletonItem { + cursor: default; + } + } + + .arrowSkeleton { + width: 16px; + height: 16px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + } +`; + +export const FollowListLoading = styled.div` + display: flex; + align-items: center; + min-height: 58px; + + .placeholderList { + display: flex; + gap: 12px; + } + + .placeholderItem { + width: 36px; + height: 36px; + border-radius: 50%; + background-color: var(--color-darkgrey-dark); } `; diff --git a/src/components/feed/FollowList.tsx b/src/components/feed/FollowList.tsx index 5b7f7e0..cb3bf94 100644 --- a/src/components/feed/FollowList.tsx +++ b/src/components/feed/FollowList.tsx @@ -4,9 +4,14 @@ import rightArrow from '../../assets/feed/rightArrow.svg'; import people from '../../assets/feed/people.svg'; import character from '../../assets/feed/character.svg'; import { getRecentFollowing, type RecentWriterData } from '@/api/users/getRecentFollowing'; +import Skeleton from '@/shared/ui/Skeleton'; import { Container, FollowContainer, EmptyFollowerContainer } from './FollowList.styled'; -const FollowList = () => { +interface FollowListProps { + onLoadingChange?: (loading: boolean) => void; +} + +const FollowList = ({ onLoadingChange }: FollowListProps) => { const navigate = useNavigate(); const [myFollowings, setMyFollowings] = useState([]); const [loading, setLoading] = useState(true); @@ -14,7 +19,8 @@ const FollowList = () => { const fetchRecentFollowing = async () => { try { setLoading(true); - const response = await getRecentFollowing(); + const minLoadingTime = new Promise(resolve => setTimeout(resolve, 500)); + const [response] = await Promise.all([getRecentFollowing(), minLoadingTime]); if (response.isSuccess) { setMyFollowings(response.data.myFollowingUsers); @@ -33,6 +39,10 @@ const FollowList = () => { fetchRecentFollowing(); }, []); + useEffect(() => { + onLoadingChange?.(loading); + }, [loading, onLoadingChange]); + const hasFollowers = myFollowings.length > 0; const visible = hasFollowers ? myFollowings.slice(0, 10) : []; @@ -50,12 +60,37 @@ const FollowList = () => { return ( -
- -
내 띱
-
{loading ? ( - <> +
+
+ +
+
+ +
+
+ ) : ( +
+ 내 띱 +
내 띱
+
+ )} + {loading ? ( + +
+ {Array.from({ length: 6 }).map((_, i) => ( +
+ +
+ +
+
+ ))} +
+
+ +
+
) : hasFollowers ? (
diff --git a/src/components/feed/MyFeed.styled.ts b/src/components/feed/MyFeed.styled.ts index 3466d5d..12871ae 100644 --- a/src/components/feed/MyFeed.styled.ts +++ b/src/components/feed/MyFeed.styled.ts @@ -3,7 +3,6 @@ import { colors, typography } from '@/styles/global/global'; export const Container = styled.div` min-height: 100vh; - padding-top: 136px; padding-bottom: 125px; //이전 76px background-color: var(--color-black-main); `; diff --git a/src/components/feed/TotalFeed.styled.ts b/src/components/feed/TotalFeed.styled.ts index b119d34..39c1734 100644 --- a/src/components/feed/TotalFeed.styled.ts +++ b/src/components/feed/TotalFeed.styled.ts @@ -3,7 +3,7 @@ import { colors, typography } from '@/styles/global/global'; export const Container = styled.div` min-height: 100vh; - padding-top: 136px; + /* padding-top: 136px; */ padding-bottom: 125px; //이전 76px background-color: var(--color-black-main); `; diff --git a/src/components/feed/TotalFeed.tsx b/src/components/feed/TotalFeed.tsx index 65b6f17..0e7f64e 100644 --- a/src/components/feed/TotalFeed.tsx +++ b/src/components/feed/TotalFeed.tsx @@ -1,4 +1,3 @@ -import FollowList from './FollowList'; import FeedPost from './FeedPost'; import type { FeedListProps } from '../../types/post'; import { Container, EmptyState } from './TotalFeed.styled'; @@ -8,7 +7,6 @@ const TotalFeed = ({ showHeader, posts = [], isTotalFeed, isLast = false }: Feed return ( - {hasPosts ? ( posts.map((post, index) => ( { const navigate = useNavigate(); const location = useLocation(); + const initialTabFromState = (location.state as { initialTab?: string } | null)?.initialTab; + const [activeTab, setActiveTab] = useState(initialTabFromState ?? tabs[0]); + const [isFollowListLoading, setIsFollowListLoading] = useState(activeTab === '피드'); + const { waitForToken } = useSocialLoginToken(); const initialTabFromState = (location.state as { initialTab?: string } | null)?.initialTab; @@ -85,8 +90,17 @@ const Feed = () => { window.scrollTo(0, 0); }, [activeTab]); + useEffect(() => { + if (activeTab === '피드') { + setIsFollowListLoading(true); + } + }, [activeTab]); + const currentFeed = activeTab === '피드' ? totalFeed : myFeed; - const showInitialLoading = currentFeed.isLoading && currentFeed.items.length === 0; + const showFeedInitialLoading = totalFeed.isLoading && totalFeed.items.length === 0; + const showMyFeedInitialLoading = myFeed.isLoading && myFeed.items.length === 0; + const showInitialLoading = + activeTab === '피드' ? showFeedInitialLoading || isFollowListLoading : showMyFeedInitialLoading; return ( @@ -96,6 +110,7 @@ const Feed = () => { rightButtonClick={() => navigate('/notice')} /> + {activeTab === '피드' && } {showInitialLoading ? ( activeTab === '내 피드' ? ( diff --git a/src/shared/ui/Skeleton/feed/OtherFeedSkeleton.styled.ts b/src/shared/ui/Skeleton/feed/OtherFeedSkeleton.styled.ts index ee76843..8c95027 100644 --- a/src/shared/ui/Skeleton/feed/OtherFeedSkeleton.styled.ts +++ b/src/shared/ui/Skeleton/feed/OtherFeedSkeleton.styled.ts @@ -1,7 +1,7 @@ import styled from '@emotion/styled'; export const SkeletonContainer = styled.div<{ paddingTop?: number }>` - padding-top: ${({ paddingTop }) => (paddingTop !== undefined ? `${paddingTop}px` : '56px')}; + /* padding-top: ${({ paddingTop }) => (paddingTop !== undefined ? `${paddingTop}px` : '56px')}; */ background-color: var(--color-black-main); min-height: 100vh; `;