From 1b72e7f499cd546d9fdcd3ba95af478d809d74af Mon Sep 17 00:00:00 2001 From: austinkelsay Date: Sun, 24 Mar 2024 14:16:55 -0500 Subject: [PATCH] Resource forms working and posting as expected --- src/components/forms/ResourceForm.js | 123 ++++++++++++++++++++--- src/components/navbar/user/UserAvatar.js | 5 + src/pages/create.js | 4 +- src/pages/profile.js | 4 +- 4 files changed, 118 insertions(+), 18 deletions(-) diff --git a/src/components/forms/ResourceForm.js b/src/components/forms/ResourceForm.js index a5e29bf..e47bc44 100644 --- a/src/components/forms/ResourceForm.js +++ b/src/components/forms/ResourceForm.js @@ -10,6 +10,7 @@ import { useNostr } from "@/hooks/useNostr"; import { v4 as uuidv4 } from 'uuid'; import { useLocalStorageWithEffect } from "@/hooks/useLocalStorage"; import EditorHeader from "./Editor/EditorHeader"; +import { useToast } from "@/hooks/useToast"; import 'primeicons/primeicons.css'; const ResourceForm = () => { @@ -22,6 +23,8 @@ const ResourceForm = () => { const [user] = useLocalStorageWithEffect('user', {}); + const { showToast } = useToast(); + const { publishAll } = useNostr(); const handleSubmit = (e) => { @@ -34,11 +37,87 @@ const ResourceForm = () => { content: text, topics: topics.map(topic => topic.trim().toLowerCase()) // Process topics as they are }; - buildPaidResource(payload); + + if (checked) { + broadcastPaidResource(payload); + } else { + broadcastFreeResource(payload); + } }; + const broadcastFreeResource = async (payload) => { + const newresourceId = uuidv4(); + const event = { + kind: 30023, + content: payload.content, + created_at: Math.floor(Date.now() / 1000), + tags: [ + ['d', newresourceId], + ['title', payload.title], + ['summary', payload.summary], + ['image', ''], + ['t', ...topics], + ['published_at', Math.floor(Date.now() / 1000).toString()], + ] + }; + + const signedEvent = await window.nostr.signEvent(event); + + const eventVerification = await verifyEvent(signedEvent); + + if (!eventVerification) { + showToast('error', 'Error', 'Event verification failed. Please try again.'); + return; + } + + const nAddress = nip19.naddrEncode({ + pubkey: signedEvent.pubkey, + kind: signedEvent.kind, + identifier: newresourceId, + }) + + console.log('nAddress:', nAddress); + + const userResponse = await axios.get(`/api/users/${user.pubkey}`) + + if (!userResponse.data) { + showToast('error', 'Error', 'User not found', 'Please try again.'); + return; + } + + const resourcePayload = { + id: newresourceId, + userId: userResponse.data.id, + price: 0, + noteId: nAddress, + } + const response = await axios.post(`/api/resources`, resourcePayload); + + console.log('response:', response); + + if (response.status !== 201) { + showToast('error', 'Error', 'Failed to create resource. Please try again.'); + return; + } + + const publishResponse = await publishAll(signedEvent); + + if (!publishResponse) { + showToast('error', 'Error', 'Failed to publish resource. Please try again.'); + return; + } else if (publishResponse?.failedRelays) { + publishResponse?.failedRelays.map(relay => { + showToast('warn', 'Warning', `Failed to publish to relay: ${relay}`); + }); + } + + publishResponse?.successfulRelays.map(relay => { + showToast('success', 'Success', `Published to relay: ${relay}`); + }) + } + // For images, whether included in the markdown content or not, clients SHOULD use image tags as described in NIP-58. This allows clients to display images in carousel format more easily. - const buildPaidResource = async (payload) => { + const broadcastPaidResource = async (payload) => { // encrypt the content with NEXT_PUBLIC_APP_PRIV_KEY to NEXT_PUBLIC_APP_PUBLIC_KEY const encryptedContent = await nip04.encrypt(process.env.NEXT_PUBLIC_APP_PRIV_KEY ,process.env.NEXT_PUBLIC_APP_PUBLIC_KEY, payload.content); const newresourceId = uuidv4(); @@ -58,19 +137,14 @@ const ResourceForm = () => { ] }; - console.log('event:', event); - - // Will need to add d tag from db id - // will need to add url/id as location tag - - // first sign the event const signedEvent = await window.nostr.signEvent(event); const eventVerification = await verifyEvent(signedEvent); - console.log('eventVerification:', eventVerification); - - console.log('signedEvent:', signedEvent); + if (!eventVerification) { + showToast('error', 'Error', 'Event verification failed. Please try again.'); + return; + } const nAddress = nip19.naddrEncode({ pubkey: signedEvent.pubkey, @@ -81,7 +155,12 @@ const ResourceForm = () => { console.log('nAddress:', nAddress); const userResponse = await axios.get(`/api/users/${user.pubkey}`) - console.log('userResponse:', userResponse); + + if (!userResponse.data) { + showToast('error', 'Error', 'User not found', 'Please try again.'); + return; + } + const resourcePayload = { id: newresourceId, userId: userResponse.data.id, @@ -89,10 +168,26 @@ const ResourceForm = () => { noteId: nAddress, } const response = await axios.post(`/api/resources`, resourcePayload); - console.log('response:', response); + + if (response.status !== 201) { + showToast('error', 'Error', 'Failed to create resource. Please try again.'); + return; + } const publishResponse = await publishAll(signedEvent); - console.log('publishResponse:', publishResponse); + + if (!publishResponse) { + showToast('error', 'Error', 'Failed to publish resource. Please try again.'); + return; + } else if (publishResponse?.failedRelays) { + publishResponse?.failedRelays.map(relay => { + showToast('warn', 'Warning', `Failed to publish to relay: ${relay}`); + }); + } + + publishResponse?.successfulRelays.map(relay => { + showToast('success', 'Success', `Published to relay: ${relay}`); + }) } const handleTopicChange = (index, value) => { diff --git a/src/components/navbar/user/UserAvatar.js b/src/components/navbar/user/UserAvatar.js index ff90cdc..1452672 100644 --- a/src/components/navbar/user/UserAvatar.js +++ b/src/components/navbar/user/UserAvatar.js @@ -47,6 +47,11 @@ const UserAvatar = () => { icon: 'pi pi-user', command: () => router.push('/profile') }, + { + label: 'Create', + icon: 'pi pi-book', + command: () => router.push('/create') + }, { label: 'Logout', icon: 'pi pi-power-off', diff --git a/src/pages/create.js b/src/pages/create.js index 7e03e47..bd79f94 100644 --- a/src/pages/create.js +++ b/src/pages/create.js @@ -7,9 +7,9 @@ import CourseForm from "@/components/forms/CourseForm"; const Create = () => { const [activeIndex, setActiveIndex] = useState(0); // State to track the active tab index const homeItems = [ - { label: 'Course', icon: 'pi pi-desktop' }, - { label: 'Workshop', icon: 'pi pi-video' }, { label: 'Resource', icon: 'pi pi-book' }, + { label: 'Workshop', icon: 'pi pi-video' }, + { label: 'Course', icon: 'pi pi-desktop' } ]; // Function to render the correct form based on the active tab diff --git a/src/pages/profile.js b/src/pages/profile.js index 0d4d6d7..00f15af 100644 --- a/src/pages/profile.js +++ b/src/pages/profile.js @@ -18,9 +18,9 @@ const Profile = () => { const homeItems = [ { label: 'All', icon: 'pi pi-star' }, - { label: 'Courses', icon: 'pi pi-desktop' }, - { label: 'Workshops', icon: 'pi pi-video' }, { label: 'Resources', icon: 'pi pi-book' }, + { label: 'Workshops', icon: 'pi pi-video' }, + { label: 'Courses', icon: 'pi pi-desktop' } ]; const purchases = [