Fixes to content fetching based on correct tags and presence of id in db, fixed forms

This commit is contained in:
austinkelsay 2024-03-27 14:44:54 -05:00
parent a06542c4a3
commit 6eb4edf617
13 changed files with 183 additions and 103 deletions

View File

@ -0,0 +1,56 @@
import React, { useState, useEffect } from 'react';
import { Carousel } from 'primereact/carousel';
import { useRouter } from 'next/router';
import { useNostr } from '@/hooks/useNostr';
import { useImageProxy } from '@/hooks/useImageProxy';
import { parseEvent } from '@/utils/nostr';
import ResourceTemplate from '@/components/content/carousels/templates/ResourceTemplate';
const responsiveOptions = [
{
breakpoint: '3000px',
numVisible: 3,
numScroll: 1
},
{
breakpoint: '1462px',
numVisible: 2,
numScroll: 1
},
{
breakpoint: '575px',
numVisible: 1,
numScroll: 1
}
];
export default function ResourcesCarousel() {
const [processedResources, setProcessedResources] = useState([]);
const [resources, setResources] = useState([]);
const router = useRouter();
const { fetchResources, events } = useNostr();
const { returnImageProxy } = useImageProxy();
useEffect(() => {
if (events && events.resources && events.resources.length > 0) {
setResources(events.resources);
} else {
fetchResources();
}
}, [events]);
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 (
<>
<h2 className="ml-[6%] mt-4">resources</h2>
<Carousel value={[...processedResources, ...processedResources]} numVisible={2} itemTemplate={ResourceTemplate} responsiveOptions={responsiveOptions} />
</>
);
}

View File

@ -26,7 +26,6 @@ const responsiveOptions = [
export default function WorkshopsCarousel() {
const [processedWorkshops, setProcessedWorkshops] = useState([]);
const [screenWidth, setScreenWidth] = useState(null);
const [workshops, setWorkshops] = useState([]);
const router = useRouter();
const { fetchWorkshops, events } = useNostr();
@ -40,35 +39,6 @@ export default function WorkshopsCarousel() {
}
}, [events]);
useEffect(() => {
// Update the state to the current window width
setScreenWidth(window.innerWidth);
const handleResize = () => {
// Update the state to the new window width when it changes
setScreenWidth(window.innerWidth);
};
window.addEventListener('resize', handleResize);
// Remove the event listener on cleanup
return () => window.removeEventListener('resize', handleResize);
}, []); // The empty array ensures this effect only runs once, similar to componentDidMount
const calculateImageDimensions = () => {
if (screenWidth >= 1200) {
// Large screens
return { width: 426, height: 240 };
} else if (screenWidth >= 768 && screenWidth < 1200) {
// Medium screens
return { width: 344, height: 194 };
} else {
// Small screens
return { width: screenWidth - 120, height: (screenWidth - 120) * (9 / 16) };
}
};
useEffect(() => {
const processWorkshops = workshops.map(workshop => {
const { id, content, title, summary, image, published_at } = parseEvent(workshop);

View File

@ -28,7 +28,7 @@ const CourseForm = () => {
price: checked ? price : null,
content: text,
topics: topics.map(topic => topic.trim().toLowerCase()),
resources: resources.map(resource => resource.trim())
topics: [...topics.map(topic => topic.trim().toLowerCase()), 'plebdevs', 'course']
};
console.log(payload);

View File

@ -21,7 +21,7 @@ const ResourceForm = () => {
const [price, setPrice] = useState(0);
const [text, setText] = useState('');
const [coverImage, setCoverImage] = useState('');
const [topics, setTopics] = useState(['']);
const [topics, setTopics] = useState([]);
const [user] = useLocalStorageWithEffect('user', {});
@ -49,7 +49,7 @@ const ResourceForm = () => {
content: text,
image: coverImage,
user: userResponse.data.id,
topics: topics.map(topic => topic.trim().toLowerCase())
topics: [...topics.map(topic => topic.trim().toLowerCase()), 'plebdevs', 'resource']
};
if (payload && payload.user) {

View File

@ -49,7 +49,7 @@ const WorkshopForm = () => {
isPaidResource: checked,
price: checked ? price : null,
embedCode,
topics: topics.map(topic => topic.trim().toLowerCase()) // Include topics in the payload
topics: [...topics.map(topic => topic.trim().toLowerCase()), 'plebdevs', 'workshop']
};
console.log(payload);

View File

@ -33,9 +33,11 @@ const UserContent = () => {
const fetchAllContent = async () => {
try {
console.log(user.id)
// Fetch drafts from the database
const draftsResponse = await axios.get(`/api/drafts/all/${user.id}`);
const drafts = draftsResponse.data;
console.log('drafts:', drafts);
// Fetch resources, workshops, and courses from Nostr
fetchResources();

View File

@ -1,5 +1,6 @@
import { useState, useEffect, useRef } from "react";
import { SimplePool, nip19, verifyEvent } from "nostr-tools";
import axios from "axios";
import { useToast } from "./useToast";
const initialRelays = [
@ -22,7 +23,7 @@ export const useNostr = () => {
streams: []
});
const {showToast} = useToast();
const { showToast } = useToast();
const pool = useRef(new SimplePool({ seenOnEnabled: true }));
const subscriptions = useRef([]);
@ -51,8 +52,9 @@ export const useNostr = () => {
const fetchEvents = async (filter, updateDataField, hasRequiredTags) => {
try {
const sub = pool.current.subscribeMany(relays, filter, {
onevent: (event) => {
if (hasRequiredTags(event.tags)) {
onevent: async (event) => {
const shouldInclude = await hasRequiredTags(event.tags);
if (shouldInclude) {
setEvents(prevData => ({
...prevData,
[updateDataField]: [...prevData[updateDataField], event]
@ -74,29 +76,78 @@ export const useNostr = () => {
};
// Fetch resources, workshops, courses, and streams with appropriate filters and update functions
const fetchResources = () => {
const filter = [{kinds: [30023], authors: ["f33c8a9617cb15f705fc70cd461cfd6eaf22f9e24c33eabad981648e5ec6f741"]}];
const hasRequiredTags = (eventData) => eventData.some(([tag, value]) => tag === "t" && value === "plebdevs") && eventData.some(([tag, value]) => tag === "t" && value === "resource");
const fetchResources = async () => {
const filter = [{ kinds: [30023], authors: ["f33c8a9617cb15f705fc70cd461cfd6eaf22f9e24c33eabad981648e5ec6f741"] }];
const hasRequiredTags = async (eventData) => {
const hasPlebDevs = eventData.some(([tag, value]) => tag === "t" && value === "plebdevs");
const hasResource = eventData.some(([tag, value]) => tag === "t" && value === "resource");
if (hasPlebDevs && hasResource) {
const resourceId = eventData.find(([tag]) => tag === "d")?.[1];
if (resourceId) {
try {
const response = await axios.get(`/api/resources/${resourceId}`);
return response.status === 200;
} catch (error) {
// Handle 404 or other errors gracefully
return false;
}
}
}
return false;
};
fetchEvents(filter, 'resources', hasRequiredTags);
};
const fetchWorkshops = () => {
const filter = [{kinds: [30023], authors: ["f33c8a9617cb15f705fc70cd461cfd6eaf22f9e24c33eabad981648e5ec6f741"]}];
const hasRequiredTags = (eventData) => eventData.some(([tag, value]) => tag === "t" && value === "plebdevs") && eventData.some(([tag, value]) => tag === "t" && value === "resource");
const fetchWorkshops = async () => {
const filter = [{ kinds: [30023], authors: ["f33c8a9617cb15f705fc70cd461cfd6eaf22f9e24c33eabad981648e5ec6f741"] }];
const hasRequiredTags = async (eventData) => {
const hasPlebDevs = eventData.some(([tag, value]) => tag === "t" && value === "plebdevs");
const hasWorkshop = eventData.some(([tag, value]) => tag === "t" && value === "workshop");
if (hasPlebDevs && hasWorkshop) {
const workshopId = eventData.find(([tag]) => tag === "d")?.[1];
if (workshopId) {
try {
const response = await axios.get(`/api/resources/${workshopId}`);
return response.status === 200;
} catch (error) {
// Handle 404 or other errors gracefully
return false;
}
}
}
return false;
};
fetchEvents(filter, 'workshops', hasRequiredTags);
}
};
const fetchCourses = () => {
const filter = [{kinds: [30023], authors: ["f33c8a9617cb15f705fc70cd461cfd6eaf22f9e24c33eabad981648e5ec6f741"]}];
const hasRequiredTags = (eventData) => eventData.some(([tag, value]) => tag === "t" && value === "plebdevs") && eventData.some(([tag, value]) => tag === "t" && value === "course");
const fetchCourses = async () => {
const filter = [{ kinds: [30023], authors: ["f33c8a9617cb15f705fc70cd461cfd6eaf22f9e24c33eabad981648e5ec6f741"] }];
const hasRequiredTags = async (eventData) => {
const hasPlebDevs = eventData.some(([tag, value]) => tag === "t" && value === "plebdevs");
const hasCourse = eventData.some(([tag, value]) => tag === "t" && value === "course");
if (hasPlebDevs && hasCourse) {
const courseId = eventData.find(([tag]) => tag === "d")?.[1];
if (courseId) {
// try {
// const response = await axios.get(`/api/resources/${courseId}`);
// return response.status === 200;
// } catch (error) {
// // Handle 404 or other errors gracefully
// return false;
// }
return true;
}
}
return false;
};
fetchEvents(filter, 'courses', hasRequiredTags);
}
};
const fetchStreams = () => {
const filter = [{kinds: [30311], authors: ["f33c8a9617cb15f705fc70cd461cfd6eaf22f9e24c33eabad981648e5ec6f741"]}];
const hasRequiredTags = (eventData) => eventData.some(([tag, value]) => tag === "t" && value === "plebdevs");
fetchEvents(filter, 'streams', hasRequiredTags);
}
// const fetchStreams = () => {
// const filter = [{kinds: [30311], authors: ["f33c8a9617cb15f705fc70cd461cfd6eaf22f9e24c33eabad981648e5ec6f741"]}];
// const hasRequiredTags = (eventData) => eventData.some(([tag, value]) => tag === "t" && value === "plebdevs");
// fetchEvents(filter, 'streams', hasRequiredTags);
// }
const fetchKind0 = async (criteria, params) => {
return new Promise((resolve, reject) => {
@ -150,45 +201,45 @@ export const useNostr = () => {
const wsRelay = new window.WebSocket(relay)
let timer
let isMessageSentSuccessfully = false
function timedout () {
clearTimeout(timer)
wsRelay.close()
reject(new Error(`relay timeout for ${relay}`))
}
timer = setTimeout(timedout, timeout)
wsRelay.onopen = function () {
clearTimeout(timer)
timer = setTimeout(timedout, timeout)
wsRelay.send(JSON.stringify(['EVENT', signedEvent]))
}
wsRelay.onmessage = function (msg) {
const m = JSON.parse(msg.data)
if (m[0] === 'OK') {
isMessageSentSuccessfully = true
function timedout() {
clearTimeout(timer)
wsRelay.close()
console.log('Successfully sent event to', relay)
resolve()
}
reject(new Error(`relay timeout for ${relay}`))
}
timer = setTimeout(timedout, timeout)
wsRelay.onopen = function () {
clearTimeout(timer)
timer = setTimeout(timedout, timeout)
wsRelay.send(JSON.stringify(['EVENT', signedEvent]))
}
wsRelay.onmessage = function (msg) {
const m = JSON.parse(msg.data)
if (m[0] === 'OK') {
isMessageSentSuccessfully = true
clearTimeout(timer)
wsRelay.close()
console.log('Successfully sent event to', relay)
resolve()
}
}
wsRelay.onerror = function (error) {
clearTimeout(timer)
console.log(error)
reject(new Error(`relay error: Failed to send to ${relay}`))
}
wsRelay.onclose = function () {
clearTimeout(timer)
if (!isMessageSentSuccessfully) {
clearTimeout(timer)
console.log(error)
reject(new Error(`relay error: Failed to send to ${relay}`))
}
}
})
wsRelay.onclose = function () {
clearTimeout(timer)
if (!isMessageSentSuccessfully) {
reject(new Error(`relay error: Failed to send to ${relay}`))
}
}
})
};
@ -214,7 +265,7 @@ export const useNostr = () => {
console.error('Error publishing event:', error);
}
};
useEffect(() => {
getRelayStatuses(); // Get initial statuses on mount
@ -236,7 +287,7 @@ export const useNostr = () => {
fetchResources,
fetchCourses,
fetchWorkshops,
fetchStreams,
// fetchStreams,
getRelayStatuses,
events
};

View File

@ -1,11 +1,11 @@
import { getCourseById, updateCourse, deleteCourse } from "@/db/models/courseModels";
export default async function handler(req, res) {
const { id } = req.query;
const { slug } = req.query;
if (req.method === 'GET') {
try {
const course = await getCourseById(parseInt(id));
const course = await getCourseById(slug);
if (course) {
res.status(200).json(course);
} else {
@ -16,14 +16,14 @@ export default async function handler(req, res) {
}
} else if (req.method === 'PUT') {
try {
const course = await updateCourse(parseInt(id), req.body);
const course = await updateCourse(slug, req.body);
res.status(200).json(course);
} catch (error) {
res.status(400).json({ error: error.message });
}
} else if (req.method === 'DELETE') {
try {
await deleteCourse(parseInt(id));
await deleteCourse(slug);
res.status(204).end();
} catch (error) {
res.status(500).json({ error: error.message });

View File

@ -1,15 +1,15 @@
import { getAllDraftsByUserId } from "@/db/models/draftModels";
export default async function handler(req, res) {
const { id } = req.query;
const { slug } = req.query;
if (req.method === 'GET') {
try {
const resource = await getAllDraftsByUserId(id);
if (resource) {
res.status(200).json(resource);
const drafts = await getAllDraftsByUserId(slug);
if (drafts) {
res.status(200).json(drafts);
} else {
res.status(404).json({ error: 'Resource not found' });
res.status(404).json({ error: 'Drafts not found' });
}
} catch (error) {
res.status(400).json({ error: error.message });

View File

@ -1,11 +1,11 @@
import { getResourceById, updateResource, deleteResource } from "@/db/models/resourceModels";
export default async function handler(req, res) {
const { id } = req.query;
const { slug } = req.query;
if (req.method === 'GET') {
try {
const resource = await getResourceById(parseInt(id));
const resource = await getResourceById(slug);
if (resource) {
res.status(200).json(resource);
} else {
@ -16,14 +16,14 @@ export default async function handler(req, res) {
}
} else if (req.method === 'PUT') {
try {
const resource = await updateResource(parseInt(id), req.body);
const resource = await updateResource(slug, req.body);
res.status(200).json(resource);
} catch (error) {
res.status(400).json({ error: error.message });
}
} else if (req.method === 'DELETE') {
try {
await deleteResource(parseInt(id));
await deleteResource(slug);
res.status(204).end();
} catch (error) {
res.status(500).json({ error: error.message });

View File

@ -27,7 +27,7 @@ const Create = () => {
};
return (
<div className="w-fit mx-auto my-8 flex flex-col justify-center">
<div className="w-[80vw] max-w-[80vw] mx-auto my-8 flex flex-col justify-center">
<h2 className="text-center mb-8">Create a {homeItems[activeIndex].label}</h2>
<MenuTab items={homeItems} activeIndex={activeIndex} onTabChange={setActiveIndex} />
{renderForm()}

View File

@ -50,7 +50,6 @@ export default function Details() {
const fetchAuthor = async (pubkey) => {
const author = await fetchKind0([{ authors: [pubkey], kinds: [0] }], {});
const fields = await findKind0Fields(author);
console.log('fields:', fields);
if (fields) {
setAuthor(fields);
}

View File

@ -3,6 +3,7 @@ import React from 'react';
import CoursesCarousel from '@/components/content/carousels/CoursesCarousel'
import WorkshopsCarousel from '@/components/content/carousels/WorkshopsCarousel'
import HeroBanner from '@/components/banner/HeroBanner';
import ResourcesCarousel from '@/components/content/carousels/ResourcesCarousel';
export default function Home() {
return (
@ -17,6 +18,7 @@ export default function Home() {
<HeroBanner />
<CoursesCarousel />
<WorkshopsCarousel />
<ResourcesCarousel />
</main>
</>
)