Skip to content

Commit

Permalink
[FE] 팀피드 스레드 조회 api 연결 (#241)
Browse files Browse the repository at this point in the history
* feat: 스레드 조회 API 작성

* feat: 스레드 조회 msw 작성

* feat: 스레드 조회 API 작성

* feat: 스레드 조회 msw 작성

* refactor: handlers에 feed handler 추가

* refactor: searchParams last-thread-id로 변경

* fix: 스레드 조회 msw 19개씩 가져오는 문제 fix

* feat: 스레드 조회 getNextPageParam 구현

* feat: 스레드 마지막 관찰하는 intersection 훅 구현

* feat: 스레드 리스트 구현

* feat: 스레드 리스트 스토리 구현

* refactor: ThreadData 네이밍 변경

* refactor: 의존성 배열 추가

* refactor: MutableRefObject사용해여 IntersectionObserver 구현
  • Loading branch information
hafnium1923 authored Jul 31, 2023
1 parent 424523d commit 967ed35
Show file tree
Hide file tree
Showing 11 changed files with 973 additions and 1 deletion.
13 changes: 13 additions & 0 deletions frontend/src/apis/feed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { http } from '~/apis/http';
import { THREAD_SIZE } from '~/constants/feed';
import type { Thread } from '~/types/feed';

export const fetchThreads = (teamPlaceId: number, lastThreadId?: number) => {
const query = lastThreadId
? `last-thread-id=${lastThreadId}&size=${THREAD_SIZE}`
: `size=${THREAD_SIZE}`;

return http.get<{
threads: Thread[];
}>(`/api/team-place/${teamPlaceId}/feed/threads?${query}`);
};
20 changes: 20 additions & 0 deletions frontend/src/components/ThreadList/ThreadList.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { Meta, StoryObj } from '@storybook/react';
import ThreadList from '~/components/ThreadList/ThreadList';

const meta = {
title: 'Feed/ThreadList',
component: ThreadList,
tags: ['autodocs'],
} satisfies Meta<typeof ThreadList>;

export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: {},
};

export const Small: Story = {
args: { size: 'sm' },
};
18 changes: 18 additions & 0 deletions frontend/src/components/ThreadList/ThreadList.styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { css, styled } from 'styled-components';

export const Container = styled.div`
display: flex;
flex-direction: column;
overflow: auto;
width: 100%;
max-height: 700px;
height: auto;
gap: 30px;
`;

export const lastThreadText = css`
display: flex;
align-self: center;
`;
55 changes: 55 additions & 0 deletions frontend/src/components/ThreadList/ThreadList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { useFetchThreads } from '~/hooks/queries/useFetchThreads';
import * as S from './ThreadList.styled';
import type { ThreadSize } from '~/types/size';
import { THREAD_TYPE } from '~/constants/feed';
import Thread from '~/components/Thread/Thread';
import Notification from '~/components/Notification/Notification';
import { useRef } from 'react';
import { useIntersectionObserver } from '~/hooks/useIntersectionObserver';
import Text from '~/components/common/Text/Text';

interface ThreadListProps {
size?: ThreadSize;
}

const ThreadList = (props: ThreadListProps) => {
const { size = 'md' } = props;
const { threadPages, hasNextPage, fetchNextPage } = useFetchThreads(1);
const observeRef = useRef<HTMLDivElement>(null);

const onIntersect: IntersectionObserverCallback = ([entry]) =>
entry.isIntersecting && fetchNextPage();

useIntersectionObserver(observeRef, onIntersect);

return (
<S.Container>
{threadPages?.pages.map((page) =>
page.threads.map((thread) => {
const { id, type, profileImageUrl, content, ...rest } = thread;
const profileUrl = profileImageUrl === null ? '' : profileImageUrl;

return type === THREAD_TYPE.THREAD ? (
<Thread
key={id}
size={size}
profileImageUrl={profileUrl}
content={content}
{...rest}
/>
) : (
<Notification key={id} size={size} content={content} />
);
}),
)}
{!hasNextPage && (
<Text size="lg" weight="bold" css={S.lastThreadText}>
마지막 스레드 입니다.
</Text>
)}
<div ref={observeRef} />
</S.Container>
);
};

export default ThreadList;
6 changes: 6 additions & 0 deletions frontend/src/constants/feed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const THREAD_TYPE = {
THREAD: 'thread',
NOTIFICATION: 'notification',
};

export const THREAD_SIZE = 20;
22 changes: 22 additions & 0 deletions frontend/src/hooks/queries/useFetchThreads.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useInfiniteQuery } from '@tanstack/react-query';
import { fetchThreads } from '~/apis/feed';
import { THREAD_SIZE } from '~/constants/feed';

export const useFetchThreads = (teamPlaceId: number) => {
const {
data: threadPages,
hasNextPage,
fetchNextPage,
} = useInfiniteQuery(
['threadData', teamPlaceId],
({ pageParam = undefined }) => fetchThreads(teamPlaceId, pageParam),
{
getNextPageParam: (lastPage) => {
if (lastPage.threads.length !== THREAD_SIZE) return undefined;
return lastPage.threads[THREAD_SIZE - 1].id;
},
},
);

return { threadPages, hasNextPage, fetchNextPage };
};
21 changes: 21 additions & 0 deletions frontend/src/hooks/useIntersectionObserver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useEffect, type RefObject, useRef } from 'react';

export const useIntersectionObserver = <T extends HTMLElement>(
targetRef: RefObject<T>,
onIntersect: IntersectionObserverCallback,
) => {
const observer = useRef<IntersectionObserver>();
useEffect(() => {
if (targetRef && targetRef.current) {
observer.current = new IntersectionObserver(onIntersect, {
root: null,
rootMargin: '0px',
threshold: 1.0,
});

observer.current.observe(targetRef.current);
}

return () => observer && observer.current?.disconnect();
}, [targetRef, onIntersect]);
};
Loading

0 comments on commit 967ed35

Please sign in to comment.