juni
TeamProject_아웃소싱 프로젝트 본문
import { useEffect, useState } from 'react';
import { useAddVideo } from '../lib/supabase/videoApi';
import { searchYouTubeVideos } from '../lib/api/youtubeAPI';
import { ToastContainer, toast } from 'react-toastify';
import { supabase } from '../lib/supabase/supabase';
import { useInView } from 'react-intersection-observer';
import { useInfiniteQuery } from '@tanstack/react-query';
const ITEMS_PER_PAGE = 9;
const MainPage = () => {
const [query, setQuery] = useState('');
const [searchResults, setSearchResults] = useState([]);
const [user, setUser] = useState(null);
const addVideoMutation = useAddVideo();
// const {
// data: videos,
// fetchNextPage,
// hasNextPage,
// isFetchingNextPage,
// isPending,
// error
// } = useInfiniteQuery({
// queryKey: ['videos'],
// initialPageParam: 1,
// queryFn: async ({ pageParam }) => {
// const response = await todoApi.get('/todos', {
// params: { _page: pageParam, _limit: ITEMS_PER_PAGE }
// });
// return response.data;
// },
// getNextPageParam: (lastPage, allPages, lastPageParam) => {
// const nextPage = lastPageParam + 1;
// return lastPage.length === ITEMS_PER_PAGE ? nextPage : undefined;
// },
// select: ({ pages }) => pages.flat()
// });
// const { ref } = useInView({
// threshold: 1,
// onChange: (inView) => {
// if (inView && hasNextPage && !isFetchingNextPage) {
// fetchNextPage();
// }
// }
// });
const searchVideos = async (e) => {
e.preventDefault();
try {
const youtubeVideos = await searchYouTubeVideos(query);
setSearchResults(
youtubeVideos.map((video) => ({
video_title: video.snippet.title,
video_id: video.id.videoId
}))
);
} catch (error) {
console.error('Error fetching data from YouTube API:', error);
}
};
const handleAddVideo = (video) => {
const videoLike = {
...video,
video_like: user.id
};
try {
if (!toast.isActive('addVideo')) {
toast.success('해당 영상이 저장되었습니다.', {
toastId: 'addVideo'
});
addVideoMutation.mutate(videoLike);
}
} catch (error) {
console.error('Error adding video:', error);
}
};
useEffect(() => {
const fetchUser = async () => {
const {
data: { user }
} = await supabase.auth.getUser();
setUser(user);
};
fetchUser();
}, []);
// if (isPending) {
// return (
// <div style={{ fontSize: 36 }}>
// <p>로딩중...</p>
// </div>
// );
// }
// if (error) {
// console.error(error);
// return <div style={{ fontSize: 24 }}>에러가 발생했습니다: {error.message}</div>;
// }
return (
<div>
<ToastContainer className="mt-12" position="top-right" />
<form onSubmit={searchVideos}>
<h1 className="flex justify-center font-bold">YouTube Video Search</h1>
<div className="mb-8 flex items-center justify-center">
<input
className="mb-2 box-border rounded border-2 p-1"
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search term"
/>
<button
className="mb-2 ml-4 flex cursor-pointer items-center justify-center rounded border-2 bg-customPurple p-1 text-white no-underline hover:underline"
type="submit"
>
Search
</button>
</div>
</form>
<div className="grid grid-cols-3 gap-4">
{searchResults.map((video, idx) => {
const isLastItem = searchResults.length - 1 === idx;
return (
// <div key={video.video_id} ref={isLastItem ? ref : null}>
<div key={video.video_id}>
<h3 dangerouslySetInnerHTML={{ __html: video.video_title }} className="w-full truncate"></h3>
<div className="aspect-h-9 aspect-w-16">
<iframe
className="h-full w-full"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
<button className="mt-2 text-green-500" onClick={() => handleAddVideo(video)}>
Save
</button>
</div>
);
})}
</div>
</div>
);
};
export default MainPage;
현재 무한 스크롤 구현 중이나 처음 써보니 익숙치 않아 어려움을 겪는 중
queryFn에 넣어야 할 함수를 생각을 잘 해보아야 할 듯.
import { ToastContainer, toast } from 'react-toastify';
import { useVideos, useDeleteVideo } from '../lib/supabase/videoApi';
import { Link } from 'react-router-dom';
import { useEffect, useState } from 'react';
import { supabase } from '../lib/supabase/supabase';
const MyPage = () => {
const [user, setUser] = useState(null);
const { data: videos, error: fetchError, isLoading } = useVideos();
const deleteVideoMutation = useDeleteVideo();
const likeVideos = videos ? videos.filter((video) => video.video_like === user?.id) : [];
const handleDelete = (id) => {
try {
toast.success('해당 영상이 삭제되었습니다.');
deleteVideoMutation.mutate(id);
} catch (error) {
console.error('Error deleting video:', error);
}
};
useEffect(() => {
const fetchUser = async () => {
const {
data: { user }
} = await supabase.auth.getUser();
setUser(user);
};
fetchUser();
}, []);
if (isLoading) {
return <div>Loading...</div>;
}
if (fetchError) {
return <div>Error: {fetchError.message}</div>;
}
return (
<>
<ToastContainer className="mt-12" position="top-right" />
<div className="rounded-md2 mx-auto my-0 max-w-sm bg-slate-100 p-4">
<h2 className="mb-4">프로필 수정</h2>
<div className="mb-4">
<label className="mb-2 block">닉네임</label>
<input className="box-border w-full p-2" type="text" placeholder="nickname" />
</div>
<div className="flex gap-20">
<button className="mb-2 flex w-full cursor-pointer items-center justify-center rounded border-none bg-customBlue p-2 text-white no-underline hover:underline">
프로필 업데이트
</button>
<Link
className="mb-2 flex w-full cursor-pointer items-center justify-center rounded border-none bg-customPurple p-2 text-white no-underline hover:underline"
to="/"
>
돌아가기
</Link>
</div>
</div>
<div className="mt-8">
<h1 className="flex justify-center font-bold">Saved Videos</h1>
<div className="grid grid-cols-3 gap-10">
{likeVideos.map((video) => (
<div key={video.id}>
<h3 dangerouslySetInnerHTML={{ __html: video.video_title }} className="w-full truncate"></h3>
<div className="aspect-h-9 aspect-w-16">
<iframe
className="h-full w-full"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
<button className="mt-2 text-red-500" onClick={() => handleDelete(video.id)}>
Delete
</button>
</div>
))}
</div>
</div>
</>
);
};
export default MyPage;
마이 페이지는 계정별로 좋아요 한 영상을 관리 할 수 있음
'프로젝트 > 미니 프로젝트' 카테고리의 다른 글
SingleProject_TS-Favorite-Countries (0) | 2024.06.27 |
---|---|
TeamProject_아웃소싱 프로젝트 (0) | 2024.06.21 |
TeamProject_아웃소싱 프로젝트 (0) | 2024.06.19 |
버전 관리 예시 (0) | 2024.06.17 |
TeamProject_뉴스피드 프로젝트 (0) | 2024.06.10 |