mirror of
https://github.com/AustinKelsay/plebdevs.git
synced 2025-06-23 16:05:24 +00:00
Remove unused course and content hooks
This commit is contained in:
parent
892ac6c7be
commit
d31e28f49b
@ -1,206 +0,0 @@
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import { Tag } from 'primereact/tag';
|
||||
import Image from 'next/image';
|
||||
import { useImageProxy } from '@/hooks/useImageProxy';
|
||||
import { getTotalFromZaps } from '@/utils/lightning';
|
||||
import ZapDisplay from '@/components/zaps/ZapDisplay';
|
||||
import { useZapsQuery } from '@/hooks/nostrQueries/zaps/useZapsQuery';
|
||||
import { Toast } from 'primereact/toast';
|
||||
import useTrackDocumentLesson from '@/hooks/tracking/useTrackDocumentLesson';
|
||||
import useWindowWidth from '@/hooks/useWindowWidth';
|
||||
import { nip19 } from 'nostr-tools';
|
||||
import appConfig from '@/config/appConfig';
|
||||
import MoreOptionsMenu from '@/components/ui/MoreOptionsMenu';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import MarkdownDisplay from '@/components/markdown/MarkdownDisplay';
|
||||
|
||||
const CourseLesson = ({ lesson, course, decryptionPerformed, isPaid, setCompleted }) => {
|
||||
const [zapAmount, setZapAmount] = useState(0);
|
||||
const { zaps, zapsLoading, zapsError } = useZapsQuery({ event: lesson, type: 'lesson' });
|
||||
const { returnImageProxy } = useImageProxy();
|
||||
const menuRef = useRef(null);
|
||||
const toastRef = useRef(null);
|
||||
const windowWidth = useWindowWidth();
|
||||
const isMobileView = windowWidth <= 768;
|
||||
const { data: session } = useSession();
|
||||
|
||||
const readTime = lesson?.content ? Math.max(30, Math.ceil(lesson.content.length / 20)) : 60;
|
||||
|
||||
const { isCompleted, isTracking, markLessonAsCompleted } = useTrackDocumentLesson({
|
||||
lessonId: lesson?.d,
|
||||
courseId: course?.d,
|
||||
readTime,
|
||||
paidCourse: isPaid,
|
||||
decryptionPerformed,
|
||||
});
|
||||
|
||||
const buildMenuItems = () => {
|
||||
const items = [];
|
||||
|
||||
const hasAccess =
|
||||
session?.user && (!isPaid || decryptionPerformed || session.user.role?.subscribed);
|
||||
|
||||
if (hasAccess) {
|
||||
items.push({
|
||||
label: 'Mark as completed',
|
||||
icon: 'pi pi-check-circle',
|
||||
command: async () => {
|
||||
try {
|
||||
await markLessonAsCompleted();
|
||||
setCompleted && setCompleted(lesson.id);
|
||||
toastRef.current.show({
|
||||
severity: 'success',
|
||||
summary: 'Success',
|
||||
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',
|
||||
icon: 'pi pi-arrow-up-right',
|
||||
command: () => {
|
||||
window.open(`/details/${lesson.id}`, '_blank');
|
||||
},
|
||||
});
|
||||
|
||||
items.push({
|
||||
label: 'View Nostr note',
|
||||
icon: 'pi pi-globe',
|
||||
command: () => {
|
||||
if (lesson?.d) {
|
||||
const addr = nip19.naddrEncode({
|
||||
pubkey: lesson.pubkey,
|
||||
kind: lesson.kind,
|
||||
identifier: lesson.d,
|
||||
relays: appConfig.defaultRelayUrls || [],
|
||||
});
|
||||
window.open(`https://habla.news/a/${addr}`, '_blank');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return items;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!zaps || zapsLoading || zapsError) return;
|
||||
|
||||
const total = getTotalFromZaps(zaps, lesson);
|
||||
|
||||
setZapAmount(total);
|
||||
}, [zaps, zapsLoading, zapsError, lesson]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isCompleted && !isTracking && setCompleted) {
|
||||
setCompleted(lesson.id);
|
||||
}
|
||||
}, [isCompleted, isTracking, lesson.id, setCompleted]);
|
||||
|
||||
const renderContent = () => {
|
||||
if (isPaid && decryptionPerformed) {
|
||||
return <MarkdownDisplay content={lesson.content} className="p-4 rounded-lg w-full" />;
|
||||
}
|
||||
if (isPaid && !decryptionPerformed) {
|
||||
return (
|
||||
<p className="text-center text-xl text-red-500">
|
||||
This content is paid and needs to be purchased before viewing.
|
||||
</p>
|
||||
);
|
||||
}
|
||||
if (lesson?.content) {
|
||||
return <MarkdownDisplay content={lesson.content} className="p-4 rounded-lg w-full" />;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full px-24 pt-12 mx-auto mt-4 max-tab:px-0 max-mob:px-0 max-tab:pt-2 max-mob:pt-2">
|
||||
<Toast ref={toastRef} />
|
||||
<div className="w-full flex flex-row justify-between max-tab:flex-col max-mob:flex-col">
|
||||
<div className="w-[75vw] mx-auto flex flex-row items-start justify-between max-tab:flex-col max-mob:flex-col max-tab:w-[95vw] max-mob:w-[95vw]">
|
||||
<div className="flex flex-col items-start max-w-[45vw] max-tab:max-w-[100vw] max-mob:max-w-[100vw]">
|
||||
<div className="flex flex-row items-center justify-between w-full">
|
||||
<h1 className="text-4xl">{lesson?.title}</h1>
|
||||
<ZapDisplay zapAmount={zapAmount} event={lesson} zapsLoading={zapsLoading} />
|
||||
</div>
|
||||
<div className="pt-2 flex flex-row justify-start w-full mt-2 mb-4">
|
||||
{lesson &&
|
||||
lesson.topics &&
|
||||
lesson.topics.length > 0 &&
|
||||
lesson.topics.map((topic, index) => (
|
||||
<Tag className="mr-2 text-white" key={index} value={topic}></Tag>
|
||||
))}
|
||||
</div>
|
||||
<div className="text-xl mt-6">
|
||||
{lesson?.summary && (
|
||||
<div className="text-xl mt-4">
|
||||
{lesson.summary.split('\n').map((line, index) => (
|
||||
<p key={index}>{line}</p>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center justify-between w-full mt-8">
|
||||
<div className="flex flex-row w-fit items-center">
|
||||
<Image
|
||||
alt="avatar thumbnail"
|
||||
src={returnImageProxy(lesson.author?.avatar, lesson.author?.pubkey)}
|
||||
width={50}
|
||||
height={50}
|
||||
className="rounded-full mr-4"
|
||||
/>
|
||||
<p className="text-lg">
|
||||
Created by{' '}
|
||||
<a
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
className="text-blue-500 hover:underline"
|
||||
>
|
||||
{lesson.author?.username || lesson.author?.pubkey}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<MoreOptionsMenu
|
||||
menuItems={buildMenuItems()}
|
||||
additionalLinks={lesson?.additionalLinks || []}
|
||||
isMobileView={isMobileView}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col max-tab:mt-12 max-mob:mt-12">
|
||||
{lesson && (
|
||||
<div className="flex flex-col items-center justify-between rounded-lg h-72 p-4 bg-gray-700 drop-shadow-md">
|
||||
<Image
|
||||
alt="course thumbnail"
|
||||
src={returnImageProxy(lesson.image)}
|
||||
width={344}
|
||||
height={194}
|
||||
className="w-[344px] h-[194px] object-cover object-top rounded-lg"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-[75vw] mx-auto mt-12 p-12 border-t-2 border-gray-300 max-tab:p-0 max-mob:p-0 max-tab:max-w-[100vw] max-mob:max-w-[100vw]">
|
||||
{renderContent()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CourseLesson;
|
@ -1,30 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import { cva } from 'class-variance-authority';
|
||||
|
||||
import { cn } from '@/utils/tw';
|
||||
|
||||
const badgeVariants = cva(
|
||||
'inline-flex items-center rounded-full border border-neutral-200 px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-neutral-950 focus:ring-offset-2 dark:border-neutral-800 dark:focus:ring-neutral-300',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
'border-transparent bg-neutral-900 text-neutral-50 hover:bg-neutral-900/80 dark:bg-neutral-50 dark:text-neutral-900 dark:hover:bg-neutral-50/80',
|
||||
secondary:
|
||||
'border-transparent bg-neutral-100 text-neutral-900 hover:bg-neutral-100/80 dark:bg-neutral-800 dark:text-neutral-50 dark:hover:bg-neutral-800/80',
|
||||
destructive:
|
||||
'border-transparent bg-red-500 text-neutral-50 hover:bg-red-500/80 dark:bg-red-900 dark:text-neutral-50 dark:hover:bg-red-900/80',
|
||||
outline: 'text-neutral-950 dark:text-neutral-50',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
function Badge({ className, variant, ...props }) {
|
||||
return <div className={cn(badgeVariants({ variant }), className)} {...props} />;
|
||||
}
|
||||
|
||||
export { Badge, badgeVariants };
|
@ -1,44 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import { Slot } from '@radix-ui/react-slot';
|
||||
import { cva } from 'class-variance-authority';
|
||||
|
||||
import { cn } from '@/utils/tw';
|
||||
|
||||
const buttonVariants = cva(
|
||||
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-neutral-950 dark:focus-visible:ring-neutral-300',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
'bg-neutral-900 text-neutral-50 hover:bg-neutral-900/90 dark:bg-neutral-50 dark:text-neutral-900 dark:hover:bg-neutral-50/90',
|
||||
destructive:
|
||||
'bg-red-500 text-neutral-50 hover:bg-red-500/90 dark:bg-red-900 dark:text-neutral-50 dark:hover:bg-red-900/90',
|
||||
outline:
|
||||
'border border-neutral-200 bg-white hover:bg-neutral-100 hover:text-neutral-900 dark:border-neutral-800 dark:bg-neutral-950 dark:hover:bg-neutral-800 dark:hover:text-neutral-50',
|
||||
secondary:
|
||||
'bg-neutral-100 text-neutral-900 hover:bg-neutral-100/80 dark:bg-neutral-800 dark:text-neutral-50 dark:hover:bg-neutral-800/80',
|
||||
ghost:
|
||||
'hover:bg-neutral-100 hover:text-neutral-900 dark:hover:bg-neutral-800 dark:hover:text-neutral-50',
|
||||
link: 'text-neutral-900 underline-offset-4 hover:underline dark:text-neutral-50',
|
||||
},
|
||||
size: {
|
||||
default: 'h-10 px-4 py-2',
|
||||
sm: 'h-9 rounded-md px-3',
|
||||
lg: 'h-11 rounded-md px-8',
|
||||
icon: 'h-10 w-10',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
size: 'default',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const Button = React.forwardRef(({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : 'button';
|
||||
return <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />;
|
||||
});
|
||||
Button.displayName = 'Button';
|
||||
|
||||
export { Button, buttonVariants };
|
@ -1,17 +0,0 @@
|
||||
import useCourseDecryption from '../encryption/useCourseDecryption';
|
||||
import useCourseTabs from './useCourseTabs';
|
||||
import useCoursePayment from './useCoursePayment';
|
||||
import useCourseData from './useCourseData';
|
||||
import useLessons from './useLessons';
|
||||
import useCourseNavigation from './useCourseNavigation';
|
||||
import useCourseTabsState from './useCourseTabsState';
|
||||
|
||||
export {
|
||||
useCourseDecryption,
|
||||
useCourseTabs,
|
||||
useCoursePayment,
|
||||
useCourseData,
|
||||
useLessons,
|
||||
useCourseNavigation,
|
||||
useCourseTabsState
|
||||
};
|
@ -1,79 +0,0 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useToast } from '../useToast';
|
||||
import { useSession } from 'next-auth/react';
|
||||
|
||||
/**
|
||||
* Hook to handle course payment processing and authorization
|
||||
* @param {Object} course - The course object
|
||||
* @returns {Object} Payment handling utilities and authorization state
|
||||
*/
|
||||
const useCoursePayment = (course) => {
|
||||
const { data: session, update } = useSession();
|
||||
const { showToast } = useToast();
|
||||
|
||||
// Determine if course requires payment
|
||||
const isPaidCourse = useMemo(() => {
|
||||
return course?.price && course.price > 0;
|
||||
}, [course]);
|
||||
|
||||
// Check if user is authorized to access the course
|
||||
const isAuthorized = useMemo(() => {
|
||||
if (!session?.user || !course) return !isPaidCourse; // Free courses are always authorized
|
||||
|
||||
return (
|
||||
// User is subscribed
|
||||
session.user.role?.subscribed ||
|
||||
// User is the creator of the course
|
||||
session.user.pubkey === course.pubkey ||
|
||||
// Course is free
|
||||
!isPaidCourse ||
|
||||
// User has purchased this specific course
|
||||
session.user.purchased?.some(purchase => purchase.courseId === course.d)
|
||||
);
|
||||
}, [session, course, isPaidCourse]);
|
||||
|
||||
// Handler for successful payment
|
||||
const handlePaymentSuccess = useCallback(async (response) => {
|
||||
if (response?.preimage) {
|
||||
try {
|
||||
await update(); // refresh session
|
||||
showToast(
|
||||
'success',
|
||||
'Payment Success',
|
||||
'You have successfully purchased this course'
|
||||
);
|
||||
return true;
|
||||
} catch (err) {
|
||||
showToast(
|
||||
'warn',
|
||||
'Session Refresh Failed',
|
||||
'Purchase succeeded but we could not refresh your session automatically. Please reload the page.'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
showToast('error', 'Error', 'Failed to purchase course. Please try again.');
|
||||
return false;
|
||||
}
|
||||
}, [update, showToast]);
|
||||
|
||||
// Handler for payment errors
|
||||
const handlePaymentError = useCallback((error) => {
|
||||
showToast(
|
||||
'error',
|
||||
'Payment Error',
|
||||
`Failed to purchase course. Please try again. Error: ${error}`
|
||||
);
|
||||
return false;
|
||||
}, [showToast]);
|
||||
|
||||
return {
|
||||
isPaidCourse,
|
||||
isAuthorized,
|
||||
handlePaymentSuccess,
|
||||
handlePaymentError,
|
||||
session
|
||||
};
|
||||
};
|
||||
|
||||
export default useCoursePayment;
|
@ -1,92 +0,0 @@
|
||||
import { useEffect, useCallback } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import useWindowWidth from '../useWindowWidth';
|
||||
import useCourseTabsState from './useCourseTabsState';
|
||||
|
||||
/**
|
||||
* @deprecated Use useCourseTabsState for pure state or useCourseNavigation for router integration
|
||||
* Hook to manage course tabs, navigation, and sidebar visibility
|
||||
* @param {Object} options - Configuration options
|
||||
* @param {Array} options.tabMap - Optional custom tab map to use
|
||||
* @param {boolean} options.initialSidebarVisible - Initial sidebar visibility state
|
||||
* @returns {Object} Tab management utilities and state
|
||||
*/
|
||||
const useCourseTabs = (options = {}) => {
|
||||
const router = useRouter();
|
||||
const windowWidth = useWindowWidth();
|
||||
const isMobileView = typeof windowWidth === 'number' ? windowWidth <= 968 : false;
|
||||
|
||||
// Use the base hook for core tab state functionality
|
||||
const {
|
||||
activeTab,
|
||||
setActiveTab,
|
||||
sidebarVisible,
|
||||
setSidebarVisible,
|
||||
tabMap,
|
||||
getActiveTabIndex,
|
||||
getTabItems,
|
||||
toggleSidebar
|
||||
} = useCourseTabsState({
|
||||
tabMap: options.tabMap,
|
||||
initialSidebarVisible: options.initialSidebarVisible,
|
||||
isMobileView
|
||||
});
|
||||
|
||||
// Update tabs and sidebar based on router query
|
||||
useEffect(() => {
|
||||
if (router.isReady) {
|
||||
const { active, tab } = router.query;
|
||||
|
||||
// If tab is specified in the URL, use that
|
||||
if (tab && tabMap.includes(tab)) {
|
||||
setActiveTab(tab);
|
||||
} else if (active !== undefined) {
|
||||
// If we have an active lesson, switch to content tab
|
||||
setActiveTab('content');
|
||||
} else {
|
||||
// Default to overview tab when no parameters
|
||||
setActiveTab('overview');
|
||||
}
|
||||
}
|
||||
}, [router.isReady, router.query, tabMap, setActiveTab]);
|
||||
|
||||
// Toggle between tabs with router integration
|
||||
const toggleTab = useCallback((indexOrName) => {
|
||||
const tabName = typeof indexOrName === 'number'
|
||||
? tabMap[indexOrName]
|
||||
: indexOrName;
|
||||
|
||||
setActiveTab(tabName);
|
||||
|
||||
// Only show/hide sidebar on mobile - desktop keeps sidebar visible
|
||||
if (isMobileView) {
|
||||
setSidebarVisible(tabName === 'lessons');
|
||||
}
|
||||
|
||||
// Sync URL with tab change using shallow routing
|
||||
const newQuery = {
|
||||
...router.query,
|
||||
tab: tabName === 'overview' ? undefined : tabName
|
||||
};
|
||||
router.push(
|
||||
{ pathname: router.pathname, query: newQuery },
|
||||
undefined,
|
||||
{ shallow: true }
|
||||
);
|
||||
}, [tabMap, isMobileView, router, setActiveTab, setSidebarVisible]);
|
||||
|
||||
return {
|
||||
activeTab,
|
||||
setActiveTab,
|
||||
sidebarVisible,
|
||||
setSidebarVisible,
|
||||
isMobileView,
|
||||
toggleTab,
|
||||
toggleSidebar,
|
||||
getActiveTabIndex,
|
||||
getTabItems,
|
||||
tabMap
|
||||
};
|
||||
};
|
||||
|
||||
export default useCourseTabs;
|
@ -1,45 +0,0 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useNDKContext } from '@/context/NDKContext';
|
||||
|
||||
export function useAllContentQuery({ ids }) {
|
||||
const [isClient, setIsClient] = useState(false);
|
||||
const { ndk, addSigner } = useNDKContext();
|
||||
|
||||
useEffect(() => {
|
||||
setIsClient(true);
|
||||
}, []);
|
||||
|
||||
const fetchAllContentFromNDK = async ids => {
|
||||
try {
|
||||
await ndk.connect();
|
||||
|
||||
const filter = { ids: ids };
|
||||
const events = await ndk.fetchEvents(filter);
|
||||
|
||||
if (events && events.size > 0) {
|
||||
const eventsArray = Array.from(events);
|
||||
return eventsArray;
|
||||
}
|
||||
return [];
|
||||
} catch (error) {
|
||||
console.error('Error fetching videos from NDK:', error);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const {
|
||||
data: allContent,
|
||||
isLoading: allContentLoading,
|
||||
error: allContentError,
|
||||
refetch: refetchAllContent,
|
||||
} = useQuery({
|
||||
queryKey: ['allContent', isClient],
|
||||
queryFn: () => fetchAllContentFromNDK(ids),
|
||||
staleTime: 1000 * 60 * 30, // 30 minutes
|
||||
refetchInterval: 1000 * 60 * 30, // 30 minutes
|
||||
enabled: isClient,
|
||||
});
|
||||
|
||||
return { allContent, allContentLoading, allContentError, refetchAllContent };
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useNDKContext } from '@/context/NDKContext';
|
||||
import axios from 'axios';
|
||||
import appConfig from '@/config/appConfig';
|
||||
|
||||
export function useCoursesQuery() {
|
||||
const [isClient, setIsClient] = useState(false);
|
||||
const { ndk, addSigner } = useNDKContext();
|
||||
|
||||
useEffect(() => {
|
||||
setIsClient(true);
|
||||
}, []);
|
||||
|
||||
const hasRequiredProperties = (event, contentIds) => {
|
||||
const hasId = event.tags.some(([tag, value]) => tag === 'd' && contentIds.includes(value));
|
||||
return hasId;
|
||||
};
|
||||
|
||||
const fetchCoursesFromNDK = async () => {
|
||||
try {
|
||||
const response = await axios.get(`/api/content/all`);
|
||||
const contentIds = response.data;
|
||||
|
||||
if (!contentIds || contentIds.length === 0) {
|
||||
return []; // Return early if no content IDs are found
|
||||
}
|
||||
|
||||
await ndk.connect();
|
||||
|
||||
const filter = { kinds: [30004], authors: appConfig.authorPubkeys };
|
||||
const events = await ndk.fetchEvents(filter);
|
||||
|
||||
if (events && events.size > 0) {
|
||||
const eventsArray = Array.from(events);
|
||||
const courses = eventsArray.filter(event => hasRequiredProperties(event, contentIds));
|
||||
return courses;
|
||||
}
|
||||
return [];
|
||||
} catch (error) {
|
||||
console.error('Error fetching courses from NDK:', error);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const {
|
||||
data: courses,
|
||||
isLoading: coursesLoading,
|
||||
error: coursesError,
|
||||
refetch: refetchCourses,
|
||||
} = useQuery({
|
||||
queryKey: ['courses', isClient],
|
||||
queryFn: fetchCoursesFromNDK,
|
||||
// staleTime: 1000 * 60 * 30, // 30 minutes
|
||||
// refetchInterval: 1000 * 60 * 30, // 30 minutes
|
||||
enabled: isClient,
|
||||
});
|
||||
|
||||
return { courses, coursesLoading, coursesError, refetchCourses };
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useNDKContext } from '@/context/NDKContext';
|
||||
import axios from 'axios';
|
||||
import appConfig from '@/config/appConfig';
|
||||
|
||||
export function useDocumentsQuery() {
|
||||
const [isClient, setIsClient] = useState(false);
|
||||
const { ndk, addSigner } = useNDKContext();
|
||||
|
||||
useEffect(() => {
|
||||
setIsClient(true);
|
||||
}, []);
|
||||
|
||||
const hasRequiredProperties = (event, contentIds) => {
|
||||
const hasDocument = event.tags.some(([tag, value]) => tag === 't' && value === 'document');
|
||||
const hasId = event.tags.some(([tag, value]) => tag === 'd' && contentIds.includes(value));
|
||||
return hasDocument && hasId;
|
||||
};
|
||||
|
||||
const fetchDocumentsFromNDK = async () => {
|
||||
try {
|
||||
const response = await axios.get(`/api/content/all`);
|
||||
const contentIds = response.data;
|
||||
|
||||
if (!contentIds || contentIds.length === 0) {
|
||||
return []; // Return early if no content IDs are found
|
||||
}
|
||||
|
||||
await ndk.connect();
|
||||
|
||||
const filter = { kinds: [30023, 30402], authors: appConfig.authorPubkeys };
|
||||
const events = await ndk.fetchEvents(filter);
|
||||
|
||||
if (events && events.size > 0) {
|
||||
const eventsArray = Array.from(events);
|
||||
const documents = eventsArray.filter(event => hasRequiredProperties(event, contentIds));
|
||||
return documents;
|
||||
}
|
||||
return [];
|
||||
} catch (error) {
|
||||
console.error('Error fetching documents from NDK:', error);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const {
|
||||
data: documents,
|
||||
isLoading: documentsLoading,
|
||||
error: documentsError,
|
||||
refetch: refetchDocuments,
|
||||
} = useQuery({
|
||||
queryKey: ['documents', isClient],
|
||||
queryFn: fetchDocumentsFromNDK,
|
||||
// staleTime: 1000 * 60 * 30, // 30 minutes
|
||||
// refetchInterval: 1000 * 60 * 30, // 30 minutes
|
||||
enabled: isClient,
|
||||
});
|
||||
|
||||
return { documents, documentsLoading, documentsError, refetchDocuments };
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useNDKContext } from '@/context/NDKContext';
|
||||
import axios from 'axios';
|
||||
import appConfig from '@/config/appConfig';
|
||||
|
||||
export function useVideosQuery() {
|
||||
const [isClient, setIsClient] = useState(false);
|
||||
const { ndk, addSigner } = useNDKContext();
|
||||
|
||||
useEffect(() => {
|
||||
setIsClient(true);
|
||||
}, []);
|
||||
|
||||
const hasRequiredProperties = (event, contentIds) => {
|
||||
const hasVideo = event.tags.some(([tag, value]) => tag === 't' && value === 'video');
|
||||
const hasId = event.tags.some(([tag, value]) => tag === 'd' && contentIds.includes(value));
|
||||
return hasVideo && hasId;
|
||||
};
|
||||
|
||||
const fetchVideosFromNDK = async () => {
|
||||
try {
|
||||
const response = await axios.get(`/api/content/all`);
|
||||
const contentIds = response.data;
|
||||
|
||||
if (!contentIds || contentIds.length === 0) {
|
||||
return []; // Return early if no content IDs are found
|
||||
}
|
||||
|
||||
await ndk.connect();
|
||||
|
||||
const filter = { kinds: [30023, 30402], authors: appConfig.authorPubkeys };
|
||||
const events = await ndk.fetchEvents(filter);
|
||||
|
||||
if (events && events.size > 0) {
|
||||
const eventsArray = Array.from(events);
|
||||
const videos = eventsArray.filter(event => hasRequiredProperties(event, contentIds));
|
||||
return videos;
|
||||
}
|
||||
return [];
|
||||
} catch (error) {
|
||||
console.error('Error fetching videos from NDK:', error);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const {
|
||||
data: videos,
|
||||
isLoading: videosLoading,
|
||||
error: videosError,
|
||||
refetch: refetchVideos,
|
||||
} = useQuery({
|
||||
queryKey: ['videos', isClient],
|
||||
queryFn: fetchVideosFromNDK,
|
||||
// staleTime: 1000 * 60 * 30, // 30 minutes
|
||||
// refetchInterval: 1000 * 60 * 30, // 30 minutes
|
||||
enabled: isClient,
|
||||
});
|
||||
|
||||
return { videos, videosLoading, videosError, refetchVideos };
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user