Added loading skeletons for carousels

This commit is contained in:
austinkelsay 2024-04-22 15:46:52 -05:00
parent f258866e07
commit edf97c74fa
7 changed files with 90 additions and 59 deletions

View File

@ -3,6 +3,7 @@ import { Carousel } from 'primereact/carousel';
import { parseEvent } from '@/utils/nostr'; import { parseEvent } from '@/utils/nostr';
import { useNostr } from '@/hooks/useNostr'; import { useNostr } from '@/hooks/useNostr';
import CourseTemplate from '@/components/content/carousels/templates/CourseTemplate'; import CourseTemplate from '@/components/content/carousels/templates/CourseTemplate';
import TemplateSkeleton from '@/components/content/carousels/skeletons/TemplateSkeleton';
const responsiveOptions = [ const responsiveOptions = [
{ {
@ -22,38 +23,38 @@ const responsiveOptions = [
} }
]; ];
export default function CoursesCarousel() { export default function CoursesCarousel() {
const [processedCourses, setProcessedCourses] = useState([]); const [processedCourses, setProcessedCourses] = useState([]);
const [courses, setCourses] = useState([]); const [loading, setLoading] = useState(true);
const { fetchCourses } = useNostr(); const { fetchCourses } = useNostr();
useEffect(() => { useEffect(() => {
const fetch = async () => { const fetch = async () => {
setLoading(true);
try { try {
const courses = await fetchCourses(); const fetchedCourses = await fetchCourses();
console.log('courses:', courses); if (fetchedCourses && fetchedCourses.length > 0) {
setCourses(courses); const processed = fetchedCourses.map(course => parseEvent(course));
setProcessedCourses(processed);
} else {
console.log('No courses fetched or empty array returned');
}
setLoading(false);
} catch (error) { } catch (error) {
console.error('Error fetching courses:', error); console.error('Error fetching courses:', error);
setLoading(false);
} }
}; };
fetch(); fetch();
}, [fetchCourses]); }, [fetchCourses]);
useEffect(() => {
const processCourses = courses.map(course => {
const { id, content, title, summary, image, published_at } = parseEvent(course);
return { id, content, title, summary, image, published_at };
}
);
setProcessedCourses(processCourses);
}, [courses]);
return ( return (
<> <>
<h2 className="ml-[6%] mt-4">courses</h2> <h2 className="ml-[6%] mt-4">Courses</h2>
<Carousel value={[...processedCourses, ...processedCourses]} numVisible={2} itemTemplate={CourseTemplate} responsiveOptions={responsiveOptions} /> <Carousel value={loading ? [{}, {}, {}] : [...processedCourses, ...processedCourses]}
numVisible={2}
itemTemplate={loading ? TemplateSkeleton : CourseTemplate}
responsiveOptions={responsiveOptions} />
</> </>
); );
} }

View File

@ -5,6 +5,7 @@ import { useNostr } from '@/hooks/useNostr';
import { useImageProxy } from '@/hooks/useImageProxy'; import { useImageProxy } from '@/hooks/useImageProxy';
import { parseEvent } from '@/utils/nostr'; import { parseEvent } from '@/utils/nostr';
import ResourceTemplate from '@/components/content/carousels/templates/ResourceTemplate'; import ResourceTemplate from '@/components/content/carousels/templates/ResourceTemplate';
import TemplateSkeleton from '@/components/content/carousels/skeletons/TemplateSkeleton';
const responsiveOptions = [ const responsiveOptions = [
{ {
@ -26,36 +27,36 @@ const responsiveOptions = [
export default function ResourcesCarousel() { export default function ResourcesCarousel() {
const [processedResources, setProcessedResources] = useState([]); const [processedResources, setProcessedResources] = useState([]);
const [resources, setResources] = useState([]); const [loading, setLoading] = useState(true);
const router = useRouter();
const { fetchResources } = useNostr(); const { fetchResources } = useNostr();
const { returnImageProxy } = useImageProxy();
useEffect(() => { useEffect(() => {
const fetch = async () => { const fetch = async () => {
setLoading(true);
try { try {
const resources = await fetchResources(); const fetchedResources = await fetchResources();
console.log('resources:', resources); if (fetchedResources && fetchedResources.length > 0) {
setResources(resources); const processed = fetchedResources.map(resource => parseEvent(resource));
setProcessedResources(processed);
} else {
console.log('No resources fetched or empty array returned');
}
setLoading(false);
} catch (error) { } catch (error) {
console.error('Error fetching resources:', error); console.error('Error fetching resources:', error);
setLoading(false);
} }
}; };
fetch(); fetch();
}, [fetchResources]); }, [fetchResources]);
useEffect(() => {
const processResources = resources.map(resource => {
const { id, content, title, summary, image, published_at } = parseEvent(resource);
return { id, content, title, summary, image, published_at };
});
setProcessedResources(processResources);
}, [resources]);
return ( return (
<> <>
<h2 className="ml-[6%] mt-4">resources</h2> <h2 className="ml-[6%] mt-4">Resources</h2>
<Carousel value={[...processedResources, ...processedResources]} numVisible={2} itemTemplate={ResourceTemplate} responsiveOptions={responsiveOptions} /> <Carousel value={loading ? [{}, {}, {}] : [...processedResources, ...processedResources]}
numVisible={2}
itemTemplate={loading ? TemplateSkeleton : ResourceTemplate}
responsiveOptions={responsiveOptions} />
</> </>
); );
} }

View File

@ -5,6 +5,7 @@ import { useNostr } from '@/hooks/useNostr';
import { useImageProxy } from '@/hooks/useImageProxy'; import { useImageProxy } from '@/hooks/useImageProxy';
import { parseEvent } from '@/utils/nostr'; import { parseEvent } from '@/utils/nostr';
import WorkshopTemplate from '@/components/content/carousels/templates/WorkshopTemplate'; import WorkshopTemplate from '@/components/content/carousels/templates/WorkshopTemplate';
import TemplateSkeleton from '@/components/content/carousels/skeletons/TemplateSkeleton';
const responsiveOptions = [ const responsiveOptions = [
{ {
@ -26,36 +27,36 @@ const responsiveOptions = [
export default function WorkshopsCarousel() { export default function WorkshopsCarousel() {
const [processedWorkshops, setProcessedWorkshops] = useState([]); const [processedWorkshops, setProcessedWorkshops] = useState([]);
const [workshops, setWorkshops] = useState([]); const [loading, setLoading] = useState(true);
const router = useRouter();
const { fetchWorkshops } = useNostr(); const { fetchWorkshops } = useNostr();
const { returnImageProxy } = useImageProxy();
useEffect(() => { useEffect(() => {
const fetch = async () => { const fetch = async () => {
setLoading(true);
try { try {
const workshops = await fetchWorkshops(); const fetchedWorkshops = await fetchWorkshops();
console.log('workshops:', workshops); if (fetchedWorkshops && fetchedWorkshops.length > 0) {
setWorkshops(workshops); const processed = fetchedWorkshops.map(workshop => parseEvent(workshop));
setProcessedWorkshops(processed);
} else {
console.log('No workshops fetched or empty array returned');
}
setLoading(false);
} catch (error) { } catch (error) {
console.error('Error fetching workshops:', error); console.error('Error fetching workshops:', error);
setLoading(false);
} }
}; };
fetch(); fetch();
}, [fetchWorkshops]); }, [fetchWorkshops]);
useEffect(() => {
const processWorkshops = workshops.map(workshop => {
const { id, content, title, summary, image, published_at } = parseEvent(workshop);
return { id, content, title, summary, image, published_at };
});
setProcessedWorkshops(processWorkshops);
}, [workshops]);
return ( return (
<> <>
<h2 className="ml-[6%] mt-4">workshops</h2> <h2 className="ml-[6%] mt-4">Workshops</h2>
<Carousel value={[...processedWorkshops, ...processedWorkshops]} numVisible={2} itemTemplate={WorkshopTemplate} responsiveOptions={responsiveOptions} /> <Carousel value={loading ? [{}, {}, {}] : [...processedWorkshops, ...processedWorkshops]}
numVisible={2}
itemTemplate={loading ? TemplateSkeleton : WorkshopTemplate}
responsiveOptions={responsiveOptions} />
</> </>
); );
} }

View File

@ -0,0 +1,28 @@
import React from "react";
import { Skeleton } from "primereact/skeleton";
const TemplateSkeleton = () => {
return (
<div className="flex flex-col items-start mx-auto px-4 mt-8 rounded-md">
{/* Image Skeleton */}
{/* <div className="w-full" style={{ paddingBottom: "56.25%" }}> */}
<Skeleton width="100%" height="18rem"></Skeleton>
{/* </div> */}
{/* Title Skeleton */}
<Skeleton width="70%" className="mt-4 mb-2" height="2rem"></Skeleton>
{/* Summary Skeleton */}
<Skeleton width="90%" className="mb-2" height="1rem"></Skeleton>
<Skeleton width="90%" className="mb-4" height="1rem"></Skeleton>
{/* Date and Zap Amount Skeleton */}
<div className="flex justify-between w-full">
<Skeleton width="30%" height="1rem"></Skeleton>
<Skeleton width="5%" height="1rem"></Skeleton>
</div>
</div>
);
};
export default TemplateSkeleton;

View File

@ -40,7 +40,7 @@ const CourseTemplate = (course) => {
return ( return (
<div <div
className="flex flex-col items-center mx-auto px-4 cursor-pointer mt-8 rounded-md" className="flex flex-col items-center mx-auto px-4 mt-8 rounded-md"
> >
<div <div
onClick={() => router.push(`/details/${course.id}`)} onClick={() => router.push(`/details/${course.id}`)}
@ -65,8 +65,8 @@ const CourseTemplate = (course) => {
<p className="text-xs text-gray-400"> <p className="text-xs text-gray-400">
{formatTimestampToHowLongAgo(course.published_at)} {formatTimestampToHowLongAgo(course.published_at)}
</p> </p>
<p className="text-xs"> <p className="text-xs cursor-pointer">
<i className="pi pi-bolt"></i> {zapAmount} <i className="pi pi-bolt text-yellow-300"></i> {zapAmount}
</p> </p>
</div> </div>
</div> </div>

View File

@ -40,7 +40,7 @@ const ResourceTemplate = (resource) => {
return ( return (
<div <div
className="flex flex-col items-center mx-auto px-4 cursor-pointer mt-8 rounded-md" className="flex flex-col items-center mx-auto px-4 mt-8 rounded-md"
> >
{/* Wrap the image in a div with a relative class with a padding-bottom of 56.25% representing the aspect ratio of 16:9 */} {/* Wrap the image in a div with a relative class with a padding-bottom of 56.25% representing the aspect ratio of 16:9 */}
<div <div
@ -66,8 +66,8 @@ const ResourceTemplate = (resource) => {
<p className="text-xs text-gray-400"> <p className="text-xs text-gray-400">
{formatTimestampToHowLongAgo(resource.published_at)} {formatTimestampToHowLongAgo(resource.published_at)}
</p> </p>
<p className="text-xs"> <p className="text-xs cursor-pointer">
<i className="pi pi-bolt"></i> {zapAmount} <i className="pi pi-bolt text-yellow-300"></i> {zapAmount}
</p> </p>
</div> </div>
</div> </div>

View File

@ -40,7 +40,7 @@ const WorkshopTemplate = (workshop) => {
return ( return (
<div <div
className="flex flex-col items-center mx-auto px-4 cursor-pointer mt-8 rounded-md" className="flex flex-col items-center mx-auto px-4 mt-8 rounded-md"
> >
<div <div
onClick={() => router.push(`/details/${workshop.id}`)} onClick={() => router.push(`/details/${workshop.id}`)}
@ -65,8 +65,8 @@ const WorkshopTemplate = (workshop) => {
<p className="text-xs text-gray-400"> <p className="text-xs text-gray-400">
{formatTimestampToHowLongAgo(workshop.published_at)} {formatTimestampToHowLongAgo(workshop.published_at)}
</p> </p>
<p className="text-xs"> <p className="text-xs cursor-pointer">
<i className="pi pi-bolt"></i> {zapAmount} <i className="pi pi-bolt text-yellow-300"></i> {zapAmount}
</p> </p>
</div> </div>
</div> </div>