Getting started on all content page

This commit is contained in:
austinkelsay 2024-08-27 17:59:45 -05:00
parent ec8b740c40
commit 654447994f
4 changed files with 227 additions and 13 deletions

View File

@ -0,0 +1,72 @@
import React, { useState, useEffect } from 'react';
import { Carousel } from 'primereact/carousel';
import ResourceTemplate from '@/components/content/carousels/templates/ResourceTemplate';
import TemplateSkeleton from '@/components/content/carousels/skeletons/TemplateSkeleton';
const responsiveOptions = [
{
breakpoint: '3000px',
numVisible: 3,
},
{
breakpoint: '1462px',
numVisible: 2,
},
{
breakpoint: '575px',
numVisible: 1,
}
];
export default function GenericCarousel({items}) {
const [carousels, setCarousels] = useState([]);
useEffect(() => {
const handleResize = () => {
const width = window.innerWidth;
let itemsPerCarousel = 3;
if (width <= 1462) {
itemsPerCarousel = 2;
}
if (width <= 575) {
itemsPerCarousel = 1;
}
const newCarousels = [];
for (let i = 0; i < items.length; i += itemsPerCarousel) {
newCarousels.push(items.slice(i, i + itemsPerCarousel));
}
setCarousels(newCarousels);
};
handleResize();
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, [items]);
return (
<>
{carousels.map((carouselItems, index) => (
<Carousel
key={index}
value={carouselItems}
itemTemplate={(item) =>
carouselItems.length > 0 ?
<ResourceTemplate key={item.id} resource={item} /> :
<TemplateSkeleton key={Math.random()} />
}
responsiveOptions={responsiveOptions}
className="mb-4"
pt={{
previousButton: { className: 'hidden' },
nextButton: { className: 'hidden' }
}}
/>
))}
</>
);
}

View File

@ -237,6 +237,11 @@ export default function DraftCourseDetails({ processedEvent, draftId, lessons })
useEffect(() => {
async function buildEvent(draft) {
if (!draft) {
console.error('Draft is null or undefined');
return null;
}
const event = new NDKEvent(ndk);
let type;
let encryptedContent;
@ -298,24 +303,42 @@ export default function DraftCourseDetails({ processedEvent, draftId, lessons })
}
async function buildDraftEvent(lesson) {
const { unsignedEvent, type } = await buildEvent(lesson);
return unsignedEvent
if (!lesson) {
console.error('Lesson is null or undefined');
return null;
}
const result = await buildEvent(lesson);
if (!result) {
console.error('Failed to build event');
return null;
}
const { unsignedEvent, type } = result;
return unsignedEvent;
}
if (!hasRunEffect.current && lessons.length > 0 && user && author) {
hasRunEffect.current = true;
lessons.forEach(async (lesson) => {
if (!lesson) {
console.error('Lesson is null or undefined');
return;
}
const isDraft = !lesson?.pubkey;
if (isDraft) {
const unsignedEvent = await buildDraftEvent(lesson);
setProcessedLessons(prev => [...prev, {
d: lesson?.id,
kind: lesson?.price ? 30402 : 30023,
pubkey: unsignedEvent.pubkey,
index: lesson.index,
unpublished: unsignedEvent
}]);
if (unsignedEvent) {
setProcessedLessons(prev => [...prev, {
d: lesson?.id,
kind: lesson?.price ? 30402 : 30023,
pubkey: unsignedEvent.pubkey,
index: lesson.index,
unpublished: unsignedEvent
}]);
}
} else {
setProcessedLessons(prev => [...prev, {
d: lesson?.d,

115
src/pages/content.js Normal file
View File

@ -0,0 +1,115 @@
import React, { useEffect, useState } from 'react';
import GenericCarousel from '@/components/content/carousels/GenericCarousel';
import { parseEvent, parseCourseEvent } from '@/utils/nostr';
import { useResources } from '@/hooks/nostr/useResources';
import { useWorkshops } from '@/hooks/nostr/useWorkshops';
import { useCourses } from '@/hooks/nostr/useCourses';
import { TabMenu } from 'primereact/tabmenu';
import 'primeicons/primeicons.css';
import { InputText } from 'primereact/inputtext';
const MenuTab = ({ items, activeIndex, onTabChange }) => {
const menuItems = items.map((item, index) => ({
label: item,
icon: 'pi pi-tag',
command: () => onTabChange(index)
}));
return (
<div className="w-full">
<TabMenu
model={menuItems}
activeIndex={activeIndex}
onTabChange={(e) => onTabChange(e.index)}
pt={{
menu: { className: 'bg-transparent border-none' },
action: { className: 'bg-transparent border-none' }
}}
/>
</div>
);
}
const ContentPage = () => {
const { resources, resourcesLoading } = useResources();
const { workshops, workshopsLoading } = useWorkshops();
const { courses, coursesLoading } = useCourses();
const [processedResources, setProcessedResources] = useState([]);
const [processedWorkshops, setProcessedWorkshops] = useState([]);
const [processedCourses, setProcessedCourses] = useState([]);
const [allContent, setAllContent] = useState([]);
const [allTopics, setAllTopics] = useState([]);
const [activeIndex, setActiveIndex] = useState(0);
const [searchQuery, setSearchQuery] = useState('');
useEffect(() => {
if (resources && !resourcesLoading) {
const processedResources = resources.map(resource => parseEvent(resource));
setProcessedResources(processedResources);
}
}, [resources, resourcesLoading]);
useEffect(() => {
if (workshops && !workshopsLoading) {
const processedWorkshops = workshops.map(workshop => parseEvent(workshop));
setProcessedWorkshops(processedWorkshops);
}
}, [workshops, workshopsLoading]);
useEffect(() => {
if (courses && !coursesLoading) {
const processedCourses = courses.map(course => parseCourseEvent(course));
setProcessedCourses(processedCourses);
}
}, [courses, coursesLoading]);
useEffect(() => {
const allTopics = new Set([...processedResources, ...processedWorkshops, ...processedCourses].map(item => item.topics).flat());
setAllTopics(Array.from(allTopics));
setAllContent([...processedResources, ...processedWorkshops, ...processedCourses]);
}, [processedResources, processedWorkshops, processedCourses]);
useEffect(() => {
console.log(allTopics);
}, [allTopics]);
const renderCarousels = () => {
const carousels = [];
for (let i = 0; i < allContent.length; i += 3) {
const items = allContent.slice(i, i + 3);
carousels.push(
<GenericCarousel
key={i}
items={items}
title={`Content Group ${i / 3 + 1}`}
type="all"
/>
);
}
return carousels;
};
return (
<div className="w-full px-4">
<div className="w-fit mx-4 mt-8 flex flex-col items-start">
<h1 className="text-3xl font-bold mb-4 ml-1">All Content</h1>
<InputText
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Search"
icon="pi pi-search"
/>
</div>
<MenuTab
items={allTopics}
activeIndex={activeIndex}
onTabChange={setActiveIndex}
className="max-w-[90%] mx-auto"
/>
{renderCarousels()}
</div>
);
};
export default ContentPage;

View File

@ -8,7 +8,7 @@ import { useNDKContext } from "@/context/NDKContext";
import { useSession } from "next-auth/react";
const DraftCourse = () => {
const { data: session } = useSession();
const { data: session, status } = useSession();
const [course, setCourse] = useState(null);
const [lessons, setLessons] = useState([]);
const [lessonsWithAuthors, setLessonsWithAuthors] = useState([]);
@ -46,7 +46,7 @@ const DraftCourse = () => {
useEffect(() => {
const fetchLessonDetails = async () => {
if (lessons.length > 0) {
if (lessons.length > 0 && status === "authenticated") {
await ndk.connect();
const newLessonsWithAuthors = await Promise.all(lessons.map(async (lesson) => {
@ -56,7 +56,7 @@ const DraftCourse = () => {
const parsedLessonObject = {
...lesson?.draft,
index: lesson.index,
author: session.user
author: session?.user || null // Use optional chaining and provide a fallback
}
return parsedLessonObject;
} else {
@ -83,7 +83,11 @@ const DraftCourse = () => {
};
fetchLessonDetails();
}, [lessons, ndk, fetchAuthor, session]);
}, [lessons, ndk, fetchAuthor, session, status]);
if (status === "loading") {
return <div>Loading...</div>;
}
return (
<>