-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: users can now see their watch history
- Loading branch information
Showing
15 changed files
with
514 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 3 additions & 0 deletions
3
prisma/migrations/20240320231750_modify_video_progress/migration.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
-- AlterTable | ||
ALTER TABLE "VideoProgress" ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
ADD COLUMN "videoDuration" INTEGER NOT NULL DEFAULT 0; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { CourseSkeleton } from '@/components/CourseCard'; | ||
|
||
export default function Loading() { | ||
return ( | ||
<div className="max-w-screen-xl justify-between mx-auto p-4 cursor-pointer grid grid-cols-1 gap-5 md:grid-cols-3"> | ||
{[1, 2, 3].map((v) => ( | ||
<CourseSkeleton key={v} /> | ||
))} | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import db from '@/db'; | ||
import { getServerSession } from 'next-auth'; | ||
import { authOptions } from '@/lib/auth'; | ||
import { Content, CourseContent, VideoProgress } from '@prisma/client'; | ||
import WatchHistoryClient from '@/components/WatchHistoryClient'; | ||
import { Fragment } from 'react'; | ||
|
||
export type TWatchHistory = VideoProgress & { | ||
content: Content & { | ||
parent: { id: number; courses: CourseContent[] } | null; | ||
}; | ||
}; | ||
|
||
const formatWatchHistoryDate = (date: Date) => { | ||
const now = new Date(); | ||
const diff = now.getTime() - date.getTime(); | ||
const diffInDays = diff / (1000 * 60 * 60 * 24); | ||
|
||
if (diffInDays < 1) { | ||
return 'Today'; | ||
} else if (diffInDays < 2) { | ||
return 'Yesterday'; | ||
} | ||
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); | ||
}; | ||
|
||
const groupByWatchedDate = (userVideoProgress: TWatchHistory[]) => { | ||
return userVideoProgress.reduce( | ||
(acc, item) => { | ||
const date = new Date(item.updatedAt); | ||
const formattedDate = formatWatchHistoryDate(date); | ||
|
||
if (!acc[formattedDate]) { | ||
acc[formattedDate] = []; | ||
} | ||
acc[formattedDate].push(item); | ||
return acc; | ||
}, | ||
{} as { [key: string]: TWatchHistory[] }, | ||
); | ||
}; | ||
|
||
async function getWatchHistory() { | ||
const session = await getServerSession(authOptions); | ||
const userId = session.user.id; | ||
|
||
const userVideoProgress: TWatchHistory[] = await db.videoProgress.findMany({ | ||
where: { | ||
userId, | ||
}, | ||
include: { | ||
content: { | ||
include: { | ||
parent: { | ||
select: { | ||
id: true, | ||
courses: true, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
orderBy: { | ||
updatedAt: 'desc', | ||
}, | ||
}); | ||
|
||
return userVideoProgress; | ||
} | ||
|
||
export default async function CoursesComponent() { | ||
const watchHistory = await getWatchHistory(); | ||
const watchHistoryGroupedByDate = groupByWatchedDate(watchHistory); | ||
|
||
return ( | ||
<div className="px-4 md:px-20 py-5"> | ||
<h1 className="text-2xl font-bold">Watch history</h1> | ||
<div className="flex flex-col gap-4 my-4 sm:my-8 max-w-full"> | ||
{Object.entries(watchHistoryGroupedByDate).map(([date, history]) => { | ||
return ( | ||
<Fragment key={date}> | ||
<h2 className="text-lg font-semibold mt-4 sm:mt-0">{date}</h2> | ||
<WatchHistoryClient history={history} /> | ||
</Fragment> | ||
); | ||
})} | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
'use client'; | ||
|
||
import { ContentCard } from '@/components/ContentCard'; | ||
import { TWatchHistory } from '../app/history/page'; | ||
import { useRouter } from 'next/navigation'; | ||
import { | ||
Carousel, | ||
CarouselContent, | ||
CarouselItem, | ||
CarouselNext, | ||
} from '@/components/ui/carousel'; | ||
|
||
const WatchHistoryClient = ({ history }: { history: TWatchHistory[] }) => ( | ||
<Carousel> | ||
<CarouselContent> | ||
{history.map((progress) => ( | ||
<CarouselItem | ||
className="basis-1/2 md:basis-1/3 lg:basis-1/5" | ||
key={progress.id} | ||
> | ||
<HistoryCard {...progress} /> | ||
</CarouselItem> | ||
))} | ||
</CarouselContent> | ||
<CarouselNext /> | ||
</Carousel> | ||
); | ||
|
||
const HistoryCard = ({ | ||
id, | ||
contentId, | ||
currentTimestamp, | ||
videoDuration, | ||
content: { type, title, thumbnail, hidden, parent }, | ||
}: TWatchHistory) => { | ||
const router = useRouter(); | ||
|
||
if (parent && !hidden && type === 'video') { | ||
const { id: folderId, courses } = parent; | ||
const courseId = courses[0].courseId; | ||
const videoUrl = `/courses/${courseId}/${folderId}/${contentId}`; | ||
const videoProgressPercent = Math.round( | ||
(currentTimestamp / videoDuration) * 100, | ||
); | ||
return ( | ||
<ContentCard | ||
type={type} | ||
key={id} | ||
title={title} | ||
image={thumbnail || ''} | ||
onClick={() => { | ||
router.push(videoUrl); | ||
}} | ||
videoProgressPercent={videoProgressPercent} | ||
hoverExpand={false} | ||
/> | ||
); | ||
} | ||
}; | ||
|
||
export default WatchHistoryClient; |
Oops, something went wrong.