mirror of
https://github.com/AustinKelsay/plebdevs.git
synced 2025-06-06 18:31:00 +00:00
Added loading skeletons for carousels
This commit is contained in:
parent
f258866e07
commit
edf97c74fa
@ -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} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user