mirror of
https://github.com/AustinKelsay/plebdevs.git
synced 2025-04-19 10:51:20 +00:00
Add checks for user logged in, paid course and or subscriber for user being able to mark lesson as completed
This commit is contained in:
parent
79b8cf1ff8
commit
5c6bf72a99
@ -14,6 +14,7 @@ import useTrackVideoLesson from '@/hooks/tracking/useTrackVideoLesson';
|
|||||||
import { Menu } from "primereact/menu";
|
import { Menu } from "primereact/menu";
|
||||||
import { Toast } from "primereact/toast";
|
import { Toast } from "primereact/toast";
|
||||||
import MoreOptionsMenu from "@/components/ui/MoreOptionsMenu";
|
import MoreOptionsMenu from "@/components/ui/MoreOptionsMenu";
|
||||||
|
import { useSession } from "next-auth/react";
|
||||||
|
|
||||||
const MDDisplay = dynamic(
|
const MDDisplay = dynamic(
|
||||||
() => import("@uiw/react-markdown-preview"),
|
() => import("@uiw/react-markdown-preview"),
|
||||||
@ -35,6 +36,7 @@ const CombinedLesson = ({ lesson, course, decryptionPerformed, isPaid, setComple
|
|||||||
const windowWidth = useWindowWidth();
|
const windowWidth = useWindowWidth();
|
||||||
const isMobileView = windowWidth <= 768;
|
const isMobileView = windowWidth <= 768;
|
||||||
const isVideo = lesson?.type === 'video';
|
const isVideo = lesson?.type === 'video';
|
||||||
|
const { data: session } = useSession();
|
||||||
|
|
||||||
const { isCompleted: videoCompleted, isTracking: videoTracking, markLessonAsCompleted } = useTrackVideoLesson({
|
const { isCompleted: videoCompleted, isTracking: videoTracking, markLessonAsCompleted } = useTrackVideoLesson({
|
||||||
lessonId: lesson?.d,
|
lessonId: lesson?.d,
|
||||||
@ -45,46 +47,60 @@ const CombinedLesson = ({ lesson, course, decryptionPerformed, isPaid, setComple
|
|||||||
decryptionPerformed
|
decryptionPerformed
|
||||||
});
|
});
|
||||||
|
|
||||||
const menuItems = [
|
const buildMenuItems = () => {
|
||||||
{
|
const items = [];
|
||||||
label: 'Mark as completed',
|
|
||||||
icon: 'pi pi-check-circle',
|
const hasAccess = session?.user && (
|
||||||
command: async () => {
|
!isPaid ||
|
||||||
try {
|
decryptionPerformed ||
|
||||||
await markLessonAsCompleted();
|
session.user.role?.subscribed
|
||||||
setCompleted(lesson.id);
|
);
|
||||||
toastRef.current.show({
|
|
||||||
severity: 'success',
|
if (hasAccess) {
|
||||||
summary: 'Success',
|
items.push({
|
||||||
detail: 'Lesson marked as completed',
|
label: 'Mark as completed',
|
||||||
life: 3000
|
icon: 'pi pi-check-circle',
|
||||||
});
|
command: async () => {
|
||||||
} catch (error) {
|
try {
|
||||||
console.error('Failed to mark lesson as completed:', error);
|
await markLessonAsCompleted();
|
||||||
toastRef.current.show({
|
setCompleted(lesson.id);
|
||||||
severity: 'error',
|
toastRef.current.show({
|
||||||
summary: 'Error',
|
severity: 'success',
|
||||||
detail: 'Failed to mark lesson as completed',
|
summary: 'Success',
|
||||||
life: 3000
|
detail: 'Lesson marked as completed',
|
||||||
});
|
life: 3000
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to mark lesson as completed:', error);
|
||||||
|
toastRef.current.show({
|
||||||
|
severity: 'error',
|
||||||
|
summary: 'Error',
|
||||||
|
detail: 'Failed to mark lesson as completed',
|
||||||
|
life: 3000
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
},
|
}
|
||||||
{
|
|
||||||
|
items.push({
|
||||||
label: 'Open lesson',
|
label: 'Open lesson',
|
||||||
icon: 'pi pi-arrow-up-right',
|
icon: 'pi pi-arrow-up-right',
|
||||||
command: () => {
|
command: () => {
|
||||||
window.open(`/details/${lesson.id}`, '_blank');
|
window.open(`/details/${lesson.id}`, '_blank');
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
{
|
|
||||||
|
items.push({
|
||||||
label: 'View Nostr note',
|
label: 'View Nostr note',
|
||||||
icon: 'pi pi-globe',
|
icon: 'pi pi-globe',
|
||||||
command: () => {
|
command: () => {
|
||||||
window.open(`https://habla.news/a/${nAddress}`, '_blank');
|
window.open(`https://habla.news/a/${nAddress}`, '_blank');
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
];
|
|
||||||
|
return items;
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleYouTubeMessage = (event) => {
|
const handleYouTubeMessage = (event) => {
|
||||||
@ -270,7 +286,7 @@ const CombinedLesson = ({ lesson, course, decryptionPerformed, isPaid, setComple
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<MoreOptionsMenu
|
<MoreOptionsMenu
|
||||||
menuItems={menuItems}
|
menuItems={buildMenuItems()}
|
||||||
additionalLinks={lesson?.additionalLinks || []}
|
additionalLinks={lesson?.additionalLinks || []}
|
||||||
isMobileView={isMobileView}
|
isMobileView={isMobileView}
|
||||||
/>
|
/>
|
||||||
|
@ -12,6 +12,7 @@ import useWindowWidth from "@/hooks/useWindowWidth";
|
|||||||
import { nip19 } from "nostr-tools";
|
import { nip19 } from "nostr-tools";
|
||||||
import appConfig from "@/config/appConfig";
|
import appConfig from "@/config/appConfig";
|
||||||
import MoreOptionsMenu from "@/components/ui/MoreOptionsMenu";
|
import MoreOptionsMenu from "@/components/ui/MoreOptionsMenu";
|
||||||
|
import { useSession } from "next-auth/react";
|
||||||
|
|
||||||
const MDDisplay = dynamic(
|
const MDDisplay = dynamic(
|
||||||
() => import("@uiw/react-markdown-preview"),
|
() => import("@uiw/react-markdown-preview"),
|
||||||
@ -28,6 +29,7 @@ const CourseLesson = ({ lesson, course, decryptionPerformed, isPaid, setComplete
|
|||||||
const toastRef = useRef(null);
|
const toastRef = useRef(null);
|
||||||
const windowWidth = useWindowWidth();
|
const windowWidth = useWindowWidth();
|
||||||
const isMobileView = windowWidth <= 768;
|
const isMobileView = windowWidth <= 768;
|
||||||
|
const { data: session } = useSession();
|
||||||
|
|
||||||
const readTime = lesson?.content ? Math.max(30, Math.ceil(lesson.content.length / 20)) : 60;
|
const readTime = lesson?.content ? Math.max(30, Math.ceil(lesson.content.length / 20)) : 60;
|
||||||
|
|
||||||
@ -39,39 +41,51 @@ const CourseLesson = ({ lesson, course, decryptionPerformed, isPaid, setComplete
|
|||||||
decryptionPerformed
|
decryptionPerformed
|
||||||
});
|
});
|
||||||
|
|
||||||
const menuItems = [
|
const buildMenuItems = () => {
|
||||||
{
|
const items = [];
|
||||||
label: 'Mark as completed',
|
|
||||||
icon: 'pi pi-check-circle',
|
const hasAccess = session?.user && (
|
||||||
command: async () => {
|
!isPaid ||
|
||||||
try {
|
decryptionPerformed ||
|
||||||
await markLessonAsCompleted();
|
session.user.role?.subscribed
|
||||||
setCompleted && setCompleted(lesson.id);
|
);
|
||||||
toastRef.current.show({
|
|
||||||
severity: 'success',
|
if (hasAccess) {
|
||||||
summary: 'Success',
|
items.push({
|
||||||
detail: 'Lesson marked as completed',
|
label: 'Mark as completed',
|
||||||
life: 3000
|
icon: 'pi pi-check-circle',
|
||||||
});
|
command: async () => {
|
||||||
} catch (error) {
|
try {
|
||||||
console.error('Failed to mark lesson as completed:', error);
|
await markLessonAsCompleted();
|
||||||
toastRef.current.show({
|
setCompleted && setCompleted(lesson.id);
|
||||||
severity: 'error',
|
toastRef.current.show({
|
||||||
summary: 'Error',
|
severity: 'success',
|
||||||
detail: 'Failed to mark lesson as completed',
|
summary: 'Success',
|
||||||
life: 3000
|
detail: 'Lesson marked as completed',
|
||||||
});
|
life: 3000
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to mark lesson as completed:', error);
|
||||||
|
toastRef.current.show({
|
||||||
|
severity: 'error',
|
||||||
|
summary: 'Error',
|
||||||
|
detail: 'Failed to mark lesson as completed',
|
||||||
|
life: 3000
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
},
|
}
|
||||||
{
|
|
||||||
|
items.push({
|
||||||
label: 'Open lesson',
|
label: 'Open lesson',
|
||||||
icon: 'pi pi-arrow-up-right',
|
icon: 'pi pi-arrow-up-right',
|
||||||
command: () => {
|
command: () => {
|
||||||
window.open(`/details/${lesson.id}`, '_blank');
|
window.open(`/details/${lesson.id}`, '_blank');
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
{
|
|
||||||
|
items.push({
|
||||||
label: 'View Nostr note',
|
label: 'View Nostr note',
|
||||||
icon: 'pi pi-globe',
|
icon: 'pi pi-globe',
|
||||||
command: () => {
|
command: () => {
|
||||||
@ -85,10 +99,10 @@ const CourseLesson = ({ lesson, course, decryptionPerformed, isPaid, setComplete
|
|||||||
window.open(`https://habla.news/a/${addr}`, '_blank');
|
window.open(`https://habla.news/a/${addr}`, '_blank');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
];
|
|
||||||
|
return items;
|
||||||
// Add additional links to menu items if they exist
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!zaps || zapsLoading || zapsError) return;
|
if (!zaps || zapsLoading || zapsError) return;
|
||||||
@ -164,7 +178,7 @@ const CourseLesson = ({ lesson, course, decryptionPerformed, isPaid, setComplete
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<MoreOptionsMenu
|
<MoreOptionsMenu
|
||||||
menuItems={menuItems}
|
menuItems={buildMenuItems()}
|
||||||
additionalLinks={lesson?.additionalLinks || []}
|
additionalLinks={lesson?.additionalLinks || []}
|
||||||
isMobileView={isMobileView}
|
isMobileView={isMobileView}
|
||||||
/>
|
/>
|
||||||
|
@ -13,6 +13,7 @@ import appConfig from "@/config/appConfig";
|
|||||||
import useTrackDocumentLesson from "@/hooks/tracking/useTrackDocumentLesson";
|
import useTrackDocumentLesson from "@/hooks/tracking/useTrackDocumentLesson";
|
||||||
import { Toast } from "primereact/toast";
|
import { Toast } from "primereact/toast";
|
||||||
import MoreOptionsMenu from "@/components/ui/MoreOptionsMenu";
|
import MoreOptionsMenu from "@/components/ui/MoreOptionsMenu";
|
||||||
|
import { useSession } from "next-auth/react";
|
||||||
|
|
||||||
const MDDisplay = dynamic(
|
const MDDisplay = dynamic(
|
||||||
() => import("@uiw/react-markdown-preview"),
|
() => import("@uiw/react-markdown-preview"),
|
||||||
@ -32,6 +33,7 @@ const DocumentLesson = ({ lesson, course, decryptionPerformed, isPaid, setComple
|
|||||||
const toastRef = useRef(null);
|
const toastRef = useRef(null);
|
||||||
// todo implement real read time needs to be on form
|
// todo implement real read time needs to be on form
|
||||||
const readTime = 120;
|
const readTime = 120;
|
||||||
|
const { data: session } = useSession();
|
||||||
|
|
||||||
const { isCompleted, isTracking, markLessonAsCompleted } = useTrackDocumentLesson({
|
const { isCompleted, isTracking, markLessonAsCompleted } = useTrackDocumentLesson({
|
||||||
lessonId: lesson?.d,
|
lessonId: lesson?.d,
|
||||||
@ -41,46 +43,60 @@ const DocumentLesson = ({ lesson, course, decryptionPerformed, isPaid, setComple
|
|||||||
decryptionPerformed: decryptionPerformed,
|
decryptionPerformed: decryptionPerformed,
|
||||||
});
|
});
|
||||||
|
|
||||||
const menuItems = [
|
const buildMenuItems = () => {
|
||||||
{
|
const items = [];
|
||||||
label: 'Mark as completed',
|
|
||||||
icon: 'pi pi-check-circle',
|
const hasAccess = session?.user && (
|
||||||
command: async () => {
|
!isPaid ||
|
||||||
try {
|
decryptionPerformed ||
|
||||||
await markLessonAsCompleted();
|
session.user.role?.subscribed
|
||||||
setCompleted && setCompleted(lesson.id);
|
);
|
||||||
toastRef.current.show({
|
|
||||||
severity: 'success',
|
if (hasAccess) {
|
||||||
summary: 'Success',
|
items.push({
|
||||||
detail: 'Lesson marked as completed',
|
label: 'Mark as completed',
|
||||||
life: 3000
|
icon: 'pi pi-check-circle',
|
||||||
});
|
command: async () => {
|
||||||
} catch (error) {
|
try {
|
||||||
console.error('Failed to mark lesson as completed:', error);
|
await markLessonAsCompleted();
|
||||||
toastRef.current.show({
|
setCompleted && setCompleted(lesson.id);
|
||||||
severity: 'error',
|
toastRef.current.show({
|
||||||
summary: 'Error',
|
severity: 'success',
|
||||||
detail: 'Failed to mark lesson as completed',
|
summary: 'Success',
|
||||||
life: 3000
|
detail: 'Lesson marked as completed',
|
||||||
});
|
life: 3000
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to mark lesson as completed:', error);
|
||||||
|
toastRef.current.show({
|
||||||
|
severity: 'error',
|
||||||
|
summary: 'Error',
|
||||||
|
detail: 'Failed to mark lesson as completed',
|
||||||
|
life: 3000
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
},
|
}
|
||||||
{
|
|
||||||
|
items.push({
|
||||||
label: 'Open lesson',
|
label: 'Open lesson',
|
||||||
icon: 'pi pi-arrow-up-right',
|
icon: 'pi pi-arrow-up-right',
|
||||||
command: () => {
|
command: () => {
|
||||||
window.open(`/details/${lesson.id}`, '_blank');
|
window.open(`/details/${lesson.id}`, '_blank');
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
{
|
|
||||||
|
items.push({
|
||||||
label: 'View Nostr note',
|
label: 'View Nostr note',
|
||||||
icon: 'pi pi-globe',
|
icon: 'pi pi-globe',
|
||||||
command: () => {
|
command: () => {
|
||||||
window.open(`https://habla.news/a/${nAddress}`, '_blank');
|
window.open(`https://habla.news/a/${nAddress}`, '_blank');
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
];
|
|
||||||
|
return items;
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!zaps || zapsLoading || zapsError) return;
|
if (!zaps || zapsLoading || zapsError) return;
|
||||||
@ -183,7 +199,7 @@ const DocumentLesson = ({ lesson, course, decryptionPerformed, isPaid, setComple
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<MoreOptionsMenu
|
<MoreOptionsMenu
|
||||||
menuItems={menuItems}
|
menuItems={buildMenuItems()}
|
||||||
additionalLinks={lesson?.additionalLinks || []}
|
additionalLinks={lesson?.additionalLinks || []}
|
||||||
isMobileView={isMobileView}
|
isMobileView={isMobileView}
|
||||||
/>
|
/>
|
||||||
|
@ -13,6 +13,7 @@ import useWindowWidth from "@/hooks/useWindowWidth";
|
|||||||
import useTrackVideoLesson from '@/hooks/tracking/useTrackVideoLesson';
|
import useTrackVideoLesson from '@/hooks/tracking/useTrackVideoLesson';
|
||||||
import { Toast } from "primereact/toast";
|
import { Toast } from "primereact/toast";
|
||||||
import MoreOptionsMenu from "@/components/ui/MoreOptionsMenu";
|
import MoreOptionsMenu from "@/components/ui/MoreOptionsMenu";
|
||||||
|
import { useSession } from "next-auth/react";
|
||||||
|
|
||||||
const MDDisplay = dynamic(
|
const MDDisplay = dynamic(
|
||||||
() => import("@uiw/react-markdown-preview"),
|
() => import("@uiw/react-markdown-preview"),
|
||||||
@ -33,6 +34,7 @@ const VideoLesson = ({ lesson, course, decryptionPerformed, isPaid, setCompleted
|
|||||||
const mdDisplayRef = useRef(null);
|
const mdDisplayRef = useRef(null);
|
||||||
const menuRef = useRef(null);
|
const menuRef = useRef(null);
|
||||||
const toastRef = useRef(null);
|
const toastRef = useRef(null);
|
||||||
|
const { data: session } = useSession();
|
||||||
|
|
||||||
const { isCompleted, isTracking, markLessonAsCompleted } = useTrackVideoLesson({
|
const { isCompleted, isTracking, markLessonAsCompleted } = useTrackVideoLesson({
|
||||||
lessonId: lesson?.d,
|
lessonId: lesson?.d,
|
||||||
@ -43,46 +45,60 @@ const VideoLesson = ({ lesson, course, decryptionPerformed, isPaid, setCompleted
|
|||||||
decryptionPerformed
|
decryptionPerformed
|
||||||
});
|
});
|
||||||
|
|
||||||
const menuItems = [
|
const buildMenuItems = () => {
|
||||||
{
|
const items = [];
|
||||||
label: 'Mark as completed',
|
|
||||||
icon: 'pi pi-check-circle',
|
const hasAccess = session?.user && (
|
||||||
command: async () => {
|
!isPaid ||
|
||||||
try {
|
decryptionPerformed ||
|
||||||
await markLessonAsCompleted();
|
session.user.role?.subscribed
|
||||||
setCompleted(lesson.id);
|
);
|
||||||
toastRef.current.show({
|
|
||||||
severity: 'success',
|
if (hasAccess) {
|
||||||
summary: 'Success',
|
items.push({
|
||||||
detail: 'Lesson marked as completed',
|
label: 'Mark as completed',
|
||||||
life: 3000
|
icon: 'pi pi-check-circle',
|
||||||
});
|
command: async () => {
|
||||||
} catch (error) {
|
try {
|
||||||
console.error('Failed to mark lesson as completed:', error);
|
await markLessonAsCompleted();
|
||||||
toastRef.current.show({
|
setCompleted(lesson.id);
|
||||||
severity: 'error',
|
toastRef.current.show({
|
||||||
summary: 'Error',
|
severity: 'success',
|
||||||
detail: 'Failed to mark lesson as completed',
|
summary: 'Success',
|
||||||
life: 3000
|
detail: 'Lesson marked as completed',
|
||||||
});
|
life: 3000
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to mark lesson as completed:', error);
|
||||||
|
toastRef.current.show({
|
||||||
|
severity: 'error',
|
||||||
|
summary: 'Error',
|
||||||
|
detail: 'Failed to mark lesson as completed',
|
||||||
|
life: 3000
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
},
|
}
|
||||||
{
|
|
||||||
|
items.push({
|
||||||
label: 'Open lesson',
|
label: 'Open lesson',
|
||||||
icon: 'pi pi-arrow-up-right',
|
icon: 'pi pi-arrow-up-right',
|
||||||
command: () => {
|
command: () => {
|
||||||
window.open(`/details/${lesson.id}`, '_blank');
|
window.open(`/details/${lesson.id}`, '_blank');
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
{
|
|
||||||
|
items.push({
|
||||||
label: 'View Nostr note',
|
label: 'View Nostr note',
|
||||||
icon: 'pi pi-globe',
|
icon: 'pi pi-globe',
|
||||||
command: () => {
|
command: () => {
|
||||||
window.open(`https://habla.news/a/${nAddress}`, '_blank');
|
window.open(`https://habla.news/a/${nAddress}`, '_blank');
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
];
|
|
||||||
|
return items;
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleYouTubeMessage = (event) => {
|
const handleYouTubeMessage = (event) => {
|
||||||
@ -239,7 +255,7 @@ const VideoLesson = ({ lesson, course, decryptionPerformed, isPaid, setCompleted
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<MoreOptionsMenu
|
<MoreOptionsMenu
|
||||||
menuItems={menuItems}
|
menuItems={buildMenuItems()}
|
||||||
additionalLinks={lesson?.additionalLinks || []}
|
additionalLinks={lesson?.additionalLinks || []}
|
||||||
isMobileView={isMobileView}
|
isMobileView={isMobileView}
|
||||||
/>
|
/>
|
||||||
|
@ -141,7 +141,7 @@ const DocumentDetails = ({ processedEvent, topics, title, summary, image, price,
|
|||||||
return (
|
return (
|
||||||
<div className="w-full px-4">
|
<div className="w-full px-4">
|
||||||
|
|
||||||
<div className="w-full p-8 rounded-lg flex flex-col items-center justify-center bg-gray-800">
|
<div className="w-full p-8 rounded-lg flex flex-col items-center justify-center">
|
||||||
<div className="mx-auto py-auto">
|
<div className="mx-auto py-auto">
|
||||||
<i className="pi pi-lock text-[60px] text-red-500"></i>
|
<i className="pi pi-lock text-[60px] text-red-500"></i>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user