From a471945766c432d0b5e3b0715fcd86e43208c892 Mon Sep 17 00:00:00 2001 From: Anubhav Jain Date: Sun, 10 Mar 2024 19:06:07 +0530 Subject: [PATCH 01/11] fix-dropdown-menu-component-modal-behaviour --- src/components/ThemeToggler.tsx | 2 +- src/components/comment/Comments.tsx | 4 ++-- src/components/ui/dropdown-menu.tsx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/ThemeToggler.tsx b/src/components/ThemeToggler.tsx index 7b65517e9..1a916f6a4 100644 --- a/src/components/ThemeToggler.tsx +++ b/src/components/ThemeToggler.tsx @@ -15,7 +15,7 @@ export function ThemeToggler() { const { setTheme } = useTheme(); return ( - + ); diff --git a/src/components/landing/appbar/nav-menu.tsx b/src/components/landing/appbar/nav-menu.tsx index 729b1e3b9..9ad4f5822 100644 --- a/src/components/landing/appbar/nav-menu.tsx +++ b/src/components/landing/appbar/nav-menu.tsx @@ -75,7 +75,6 @@ export function NavigationMenu() { - From 445505cb6532d65585bfe2a545611b3b057f5d23 Mon Sep 17 00:00:00 2001 From: krishhh16 Date: Sun, 17 Mar 2024 15:30:10 +0530 Subject: [PATCH 03/11] closes #212 --- src/app/courses/[...courseId]/page.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/app/courses/[...courseId]/page.tsx b/src/app/courses/[...courseId]/page.tsx index bcd364bdf..37bdee0f8 100644 --- a/src/app/courses/[...courseId]/page.tsx +++ b/src/app/courses/[...courseId]/page.tsx @@ -14,6 +14,19 @@ import { redirect } from 'next/navigation'; import { CourseView } from '@/components/CourseView'; import { QueryParams } from '@/actions/types'; +interface PurchaseType { + id: number; + title: string; + imageUrl: string; + description: string; + appxCourseId: number; + openToEveryone: boolean; + slug: string; + discordRoleId: string; + totalVideos?: number; + totalVideosWatched: number; +} + const checkAccess = async (courseId: string) => { const session = await getServerSession(authOptions); @@ -21,7 +34,7 @@ const checkAccess = async (courseId: string) => { return false; } const purchases = await getPurchases(session.user.email); - if (purchases.map((p: any) => p.id).includes(Number(courseId))) { + if (purchases.map((p: PurchaseType) => p.id).includes(Number(courseId))) { return true; } return false; From 4ea082f1feb2d88a1c5660e64eaa017a6ded28d3 Mon Sep 17 00:00:00 2001 From: devsargam Date: Thu, 21 Mar 2024 23:49:46 +0530 Subject: [PATCH 04/11] chore: add issue templte --- .github/ISSUE_TEMPLATE/bug.md | 28 ++++++++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature.md | 19 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug.md create mode 100644 .github/ISSUE_TEMPLATE/feature.md diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md new file mode 100644 index 000000000..32d6833c0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -0,0 +1,28 @@ +--- +name: Bug +about: Create a report to help us improve +title: "bug: " +labels: bug +assignees: '' +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Do something +2. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots or GIFs** +If applicable, add screenshots to help explain your problem. + +**Info (please complete the following information):** + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature.md b/.github/ISSUE_TEMPLATE/feature.md new file mode 100644 index 000000000..8f5b39a99 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature.md @@ -0,0 +1,19 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: 'feature: ' +labels: enhancement +assignees: '' +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From ede891bacdd49e2a97cd23517a297caad51e84a4 Mon Sep 17 00:00:00 2001 From: devsargam Date: Thu, 21 Mar 2024 23:50:03 +0530 Subject: [PATCH 05/11] chore: add pr templte --- .github/pull_request_template.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..5ae008d3c --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,9 @@ +### PR Fixes: +- 1 +- 2 + +Resolves #[Issue Number if there] + +### Checklist before requesting a review +- [ ] I have performed a self-review of my code +- [ ] I assure there is no similar/duplicate pull request regarding same issue From 4333d9a2db66e91699f19260076f293cd8da4b85 Mon Sep 17 00:00:00 2001 From: devsargam Date: Tue, 26 Mar 2024 01:54:28 +0530 Subject: [PATCH 06/11] feat: checkpoint 1 --- src/components/Courses.tsx | 40 +++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/components/Courses.tsx b/src/components/Courses.tsx index f5cf08c2f..2f348b448 100644 --- a/src/components/Courses.tsx +++ b/src/components/Courses.tsx @@ -3,27 +3,31 @@ import { Course } from '@prisma/client'; import { CourseCard } from './CourseCard'; import { useRouter } from 'next/navigation'; +import { RefreshDb } from './RefreshDb'; export const Courses = ({ courses }: { courses: Course[] }) => { const router = useRouter(); return ( -
- {courses?.map((course) => ( - { - if ( - course.title.includes('Machine Learning') || - course.title.includes('Harnoor') - ) { - router.push('https://harkirat.classx.co.in/'); - } else { - router.push(`/courses/${course.id}`); - } - }} - /> - ))} -
+
+
+ {courses?.map((course) => ( + { + if ( + course.title.includes('Machine Learning') || + course.title.includes('Harnoor') + ) { + router.push('https://harkirat.classx.co.in/'); + } else { + router.push(`/courses/${course.id}`); + } + }} + /> + ))} +
+ +
); }; From fc5cccf823b36a1a350db80c7281b3a45083d9c6 Mon Sep 17 00:00:00 2001 From: Vijay <74645268+vijaysingh2219@users.noreply.github.com> Date: Tue, 26 Mar 2024 03:27:09 +0530 Subject: [PATCH 07/11] feat: Improve keyboard navigation functionality to video player component - Added support for the following keys: - 'R' key restarts playback from the beginning. - 'M' key toggles mute/unmute functionality. - 'K' key toggles play/pause functionality. - 'J' key seeks backward by 10 seconds multiplied by the playback rate. - 'L' key seeks forward by 10 seconds multiplied by the playback rate. --- src/components/VideoPlayer2.tsx | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/components/VideoPlayer2.tsx b/src/components/VideoPlayer2.tsx index b624ea8df..431001354 100644 --- a/src/components/VideoPlayer2.tsx +++ b/src/components/VideoPlayer2.tsx @@ -143,11 +143,39 @@ export const VideoPlayer: FunctionComponent = ({ player.currentTime(player.currentTime() - 5); event.stopPropagation(); break; - case 'KeyF': // F key for fullscree + case 'KeyF': // F key for fullscreen if (player.isFullscreen_) document.exitFullscreen(); else player.requestFullscreen(); event.stopPropagation(); break; + case 'KeyR': // 'R' key to restart playback from the beginning + player.currentTime(0); + event.stopPropagation(); + break; + case 'KeyM': // 'M' key to toggle mute/unmute + if (player.volume() === 0) { + player.volume(1); + } else { + player.volume(0); + } + event.stopPropagation(); + break; + case 'KeyK': // 'K' key for play/pause toggle + if (player.paused()) { + player.play(); + } else { + player.pause(); + } + event.stopPropagation(); + break; + case 'KeyJ': // 'J' key for seeking backward 10 seconds multiplied by the playback rate + player.currentTime(player.currentTime() - (10 * player.playbackRate())); + event.stopPropagation(); + break; + case 'KeyL': // 'L' key for seeking forward 10 seconds multiplied by the playback rate + player.currentTime(player.currentTime() + (10 * player.playbackRate())); + event.stopPropagation(); + break; } } }; From 48493ce9d864008e4c1b14b9d6cce0dbd9079383 Mon Sep 17 00:00:00 2001 From: devsargam Date: Tue, 26 Mar 2024 03:41:27 +0530 Subject: [PATCH 08/11] feat: setup refresh option --- src/actions/refresh-db/index.ts | 66 +++++++++++++++++++++++++++++++++ src/components/RefreshDb.tsx | 31 ++++++++++++++++ src/db/Cache.ts | 6 ++- src/utiles/appx-check-mail.ts | 24 ++++++++++++ src/utiles/appx.ts | 22 ++--------- 5 files changed, 129 insertions(+), 20 deletions(-) create mode 100644 src/actions/refresh-db/index.ts create mode 100644 src/components/RefreshDb.tsx create mode 100644 src/utiles/appx-check-mail.ts diff --git a/src/actions/refresh-db/index.ts b/src/actions/refresh-db/index.ts new file mode 100644 index 000000000..bfec769ff --- /dev/null +++ b/src/actions/refresh-db/index.ts @@ -0,0 +1,66 @@ +'use server'; +import db from '@/db'; +import { Cache } from '@/db/Cache'; +import { getAllCourses } from '@/db/course'; +import { checkUserEmailForPurchase } from '@/utiles/appx-check-mail'; +import { Course } from '@prisma/client'; + +type RefreshDbFn = (args: { userId: string; email: string }) => Promise<{ + error: boolean; + message: string; +}>; + +export const refreshDb: RefreshDbFn = async ({ userId, email }) => { + // Only allow user to refetch every minute + if (Cache.getInstance().get('rate-limit', [userId])) { + return { + error: true, + message: 'Wait sometime before refetching', + }; + } + + const allCourses = (await getAllCourses()) as Course[]; + + // Check which course the user has purchased + const userCourses = await db.userPurchases.findMany({ + where: { + userId, + }, + }); + + const coursesWithoutUser = allCourses.filter((course) => { + return !userCourses.some((userCourse) => userCourse.courseId === course.id); + }); + + const responses: Course[] = []; + + const promises = coursesWithoutUser + .filter((x) => !x.openToEveryone) + .map(async (course) => { + const courseId = course.appxCourseId.toString(); + const data = await checkUserEmailForPurchase(email, courseId); + if (data.data === '1') { + responses.push(course); + } + }); + + await Promise.all(promises); + + responses.forEach(async (res) => { + try { + await db.userPurchases.create({ + data: { + userId, + courseId: res.id, + }, + }); + } catch { + return { error: true, message: 'Unable to insert courses' }; + } + }); + + Cache.getInstance().evict('courses', [email]); + Cache.getInstance().set('rate-limit', [userId], true, 60); + + return { error: false, message: 'Refetched Courses' }; +}; diff --git a/src/components/RefreshDb.tsx b/src/components/RefreshDb.tsx new file mode 100644 index 000000000..8303a6efa --- /dev/null +++ b/src/components/RefreshDb.tsx @@ -0,0 +1,31 @@ +'use client'; +import { refreshDb } from '@/actions/refresh-db'; +import { Button } from './ui/button'; +import { toast } from 'sonner'; +import { useSession } from 'next-auth/react'; + +export function RefreshDb() { + const session = useSession(); + console.log(session); + + const handleClick = async () => { + // @ts-ignore + const res = await refreshDb({ userId: session.data.user.id }); + if (res.error) { + toast.error(res.message); + } else { + toast.info(res.message); + } + }; + + if (session.status === 'loading') return <>Loading...; + + return ( +
+

Don't see all your courses?

+ +
+ ); +} diff --git a/src/db/Cache.ts b/src/db/Cache.ts index da447951c..bc0527e5b 100644 --- a/src/db/Cache.ts +++ b/src/db/Cache.ts @@ -51,5 +51,9 @@ export class Cache { return entry.value; } - evict() {} + evict(type: string, args: string[]) { + const key = `${type} ${JSON.stringify(args)}`; + this.inMemoryDb.delete(key); + return null; + } } diff --git a/src/utiles/appx-check-mail.ts b/src/utiles/appx-check-mail.ts new file mode 100644 index 000000000..5c54d745a --- /dev/null +++ b/src/utiles/appx-check-mail.ts @@ -0,0 +1,24 @@ +const APPX_AUTH_KEY = process.env.APPX_AUTH_KEY; +const APPX_CLIENT_SERVICE = process.env.APPX_CLIENT_SERVICE; +const APPX_BASE_API = process.env.APPX_BASE_API; + +const baseUrl = `${APPX_BASE_API}/get/checkemailforpurchase`; + +const headers = { + 'Client-Service': APPX_CLIENT_SERVICE, + 'Auth-Key': APPX_AUTH_KEY, +}; + +export async function checkUserEmailForPurchase( + email: string, + courseId: string, +) { + const params = new URLSearchParams({ + email, + itemtype: '10', + itemid: courseId, + }); + //@ts-ignore + const response = await fetch(`${baseUrl}?${params}`, { headers }); + return await response.json(); +} diff --git a/src/utiles/appx.ts b/src/utiles/appx.ts index d831e7725..a12bd0209 100644 --- a/src/utiles/appx.ts +++ b/src/utiles/appx.ts @@ -8,10 +8,8 @@ import { Course } from '@/store/atoms'; import { getServerSession } from 'next-auth'; import { Cache } from '@/db/Cache'; import prisma from '@/db'; +import { checkUserEmailForPurchase } from './appx-check-mail'; -const APPX_AUTH_KEY = process.env.APPX_AUTH_KEY; -const APPX_CLIENT_SERVICE = process.env.APPX_CLIENT_SERVICE; -const APPX_BASE_API = process.env.APPX_BASE_API; const LOCAL_CMS_PROVIDER = process.env.LOCAL_CMS_PROVIDER; export async function getPurchases(email: string) { @@ -86,27 +84,13 @@ export async function getPurchases(email: string) { return allCourses; } - const baseUrl = `${APPX_BASE_API}/get/checkemailforpurchase`; - - const headers = { - 'Client-Service': APPX_CLIENT_SERVICE, - 'Auth-Key': APPX_AUTH_KEY, - }; - const responses: Course[] = []; const promises = courses .filter((x) => !x.openToEveryone) .map(async (course) => { - const params = new URLSearchParams({ - email, - itemtype: '10', - itemid: course.appxCourseId.toString(), - }); - //@ts-ignore - const response = await fetch(`${baseUrl}?${params}`, { headers }); - const data = await response.json(); - + const courseId = course.appxCourseId.toString(); + const data = await checkUserEmailForPurchase(email, courseId); if (data.data === '1') { responses.push(course); } From a326fef91913e10425013e16495228c7ae8200e2 Mon Sep 17 00:00:00 2001 From: hkirat Date: Wed, 27 Mar 2024 03:21:12 +0530 Subject: [PATCH 09/11] Fixed refresh db --- src/actions/refresh-db/index.ts | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/actions/refresh-db/index.ts b/src/actions/refresh-db/index.ts index bfec769ff..7d4b1bd31 100644 --- a/src/actions/refresh-db/index.ts +++ b/src/actions/refresh-db/index.ts @@ -2,17 +2,30 @@ import db from '@/db'; import { Cache } from '@/db/Cache'; import { getAllCourses } from '@/db/course'; +import { authOptions } from '@/lib/auth'; import { checkUserEmailForPurchase } from '@/utiles/appx-check-mail'; import { Course } from '@prisma/client'; +import { getServerSession } from 'next-auth'; type RefreshDbFn = (args: { userId: string; email: string }) => Promise<{ error: boolean; message: string; }>; -export const refreshDb: RefreshDbFn = async ({ userId, email }) => { +export const refreshDb: RefreshDbFn = async () => { + const session = await getServerSession(authOptions); + const email = session?.user.email || ''; + const userId = session?.user.id; + + if (!email) { + return { + error: true, + message: 'You are not logged in', + }; + } + // Only allow user to refetch every minute - if (Cache.getInstance().get('rate-limit', [userId])) { + if (Cache.getInstance().get('rate-limit', [email])) { return { error: true, message: 'Wait sometime before refetching', @@ -39,6 +52,7 @@ export const refreshDb: RefreshDbFn = async ({ userId, email }) => { .map(async (course) => { const courseId = course.appxCourseId.toString(); const data = await checkUserEmailForPurchase(email, courseId); + if (data.data === '1') { responses.push(course); } @@ -60,7 +74,7 @@ export const refreshDb: RefreshDbFn = async ({ userId, email }) => { }); Cache.getInstance().evict('courses', [email]); - Cache.getInstance().set('rate-limit', [userId], true, 60); + Cache.getInstance().set('rate-limit', [email], true, 60); return { error: false, message: 'Refetched Courses' }; }; From 7c3917a2ff043b79f02d443545de7a6149c0e05b Mon Sep 17 00:00:00 2001 From: Nimit Haria Date: Fri, 29 Mar 2024 01:28:09 +0530 Subject: [PATCH 10/11] feat: printing notion notes --- src/app/pdf/[contentId]/page.tsx | 21 +++++++++++ src/components/Appbar.tsx | 7 ++-- src/components/NotionRenderer.tsx | 20 ++++++++++- src/components/landing/footer/footer.tsx | 5 ++- src/components/print/Print.tsx | 45 +++++++++++++++++++++++ src/components/print/PrintNotes.tsx | 46 ++++++++++++++++++++++++ src/hooks/useMountStatus.ts | 13 +++++++ 7 files changed, 153 insertions(+), 4 deletions(-) create mode 100644 src/app/pdf/[contentId]/page.tsx create mode 100644 src/components/print/Print.tsx create mode 100644 src/components/print/PrintNotes.tsx create mode 100644 src/hooks/useMountStatus.ts diff --git a/src/app/pdf/[contentId]/page.tsx b/src/app/pdf/[contentId]/page.tsx new file mode 100644 index 000000000..a80245598 --- /dev/null +++ b/src/app/pdf/[contentId]/page.tsx @@ -0,0 +1,21 @@ +import { NotionAPI } from 'notion-client'; +import db from '@/db'; +const notion = new NotionAPI(); +import PrintNotes from '@/components/print/PrintNotes'; + +export default async function PrintNotion({ + params: { contentId }, +}: { + params: { contentId: string }; +}) { + const notionMetadata = await db.notionMetadata.findFirst({ + where: { + contentId: parseInt(contentId, 10), + }, + }); + + if (notionMetadata?.notionId) { + const recordMap = await notion.getPage(notionMetadata?.notionId); + return ; + } +} diff --git a/src/components/Appbar.tsx b/src/components/Appbar.tsx index 369e7863b..368620be3 100644 --- a/src/components/Appbar.tsx +++ b/src/components/Appbar.tsx @@ -21,7 +21,10 @@ export const Appbar = () => { const currentPath = usePathname(); return ( <> - -
+
); }; diff --git a/src/components/landing/footer/footer.tsx b/src/components/landing/footer/footer.tsx index 3468ed435..a998ed697 100644 --- a/src/components/landing/footer/footer.tsx +++ b/src/components/landing/footer/footer.tsx @@ -6,10 +6,7 @@ import Logo from '../logo/logo'; const Footer = () => { return ( -