From 80edbf09053b90f5fc43dcfd85ac20cee99402b5 Mon Sep 17 00:00:00 2001 From: austinkelsay Date: Thu, 8 Aug 2024 16:29:16 -0500 Subject: [PATCH] A bunch of good stuff --- src/components/forms/ResourceForm.js | 75 +++++++- src/components/navbar/user/UserAvatar.js | 2 +- src/db/models/resourceModels.js | 13 ++ .../nostrQueries/content/useCoursesQuery.js | 63 +++++-- .../nostrQueries/content/useResourcesQuery.js | 62 ++++--- .../nostrQueries/content/useWorkshopsQuery.js | 89 +++++---- src/pages/api/auth/[...nextauth].js | 4 +- src/pages/api/resources/[slug].js | 12 +- src/pages/details/[slug]/edit.js | 47 +++++ .../details/{[slug].js => [slug]/index.js} | 39 +++- src/pages/profile.js | 170 +++++++++--------- src/utils/nostr.js | 3 +- 12 files changed, 410 insertions(+), 169 deletions(-) create mode 100644 src/pages/details/[slug]/edit.js rename src/pages/details/{[slug].js => [slug]/index.js} (83%) diff --git a/src/components/forms/ResourceForm.js b/src/components/forms/ResourceForm.js index 50bbff0..3967a4d 100644 --- a/src/components/forms/ResourceForm.js +++ b/src/components/forms/ResourceForm.js @@ -7,6 +7,8 @@ import { Button } from "primereact/button"; import { useRouter } from "next/router";; import { useSession } from "next-auth/react"; import { useToast } from "@/hooks/useToast"; +import { useNDKContext } from "@/context/NDKContext"; +import { NDKEvent } from "@nostr-dev-kit/ndk"; import dynamic from 'next/dynamic'; const MDEditor = dynamic( () => import("@uiw/react-md-editor"), @@ -16,7 +18,7 @@ const MDEditor = dynamic( ); import 'primeicons/primeicons.css'; -const ResourceForm = ({ draft = null }) => { +const ResourceForm = ({ draft = null, isPublished = false }) => { const [title, setTitle] = useState(draft?.title || ''); const [summary, setSummary] = useState(draft?.summary || ''); const [isPaidResource, setIsPaidResource] = useState(draft?.price ? true : false); @@ -29,6 +31,12 @@ const ResourceForm = ({ draft = null }) => { const { data: session, status } = useSession(); const { showToast } = useToast(); const router = useRouter(); + const ndk = useNDKContext(); + + useEffect(() => { + console.log('isPublished', isPublished); + console.log('draft', draft); + }, [isPublished, draft]); useEffect(() => { if (session) { @@ -52,6 +60,69 @@ const ResourceForm = ({ draft = null }) => { } }, [draft]); + const buildEvent = async (draft) => { + const dTag = draft.d + const event = new NDKEvent(ndk); + let encryptedContent; + + if (draft?.price) { + // encrypt the content with NEXT_PUBLIC_APP_PRIV_KEY to NEXT_PUBLIC_APP_PUBLIC_KEY + encryptedContent = await nip04.encrypt(process.env.NEXT_PUBLIC_APP_PRIV_KEY, process.env.NEXT_PUBLIC_APP_PUBLIC_KEY, draft.content); + } + + event.kind = draft?.price ? 30402 : 30023; // Determine kind based on if price is present + event.content = draft?.price ? encryptedContent : draft.content; + event.created_at = Math.floor(Date.now() / 1000); + event.pubkey = user.pubkey; + event.tags = [ + ['d', dTag], + ['title', draft.title], + ['summary', draft.summary], + ['image', draft.image], + ...draft.topics.map(topic => ['t', topic]), + ['published_at', Math.floor(Date.now() / 1000).toString()], + ...(draft?.price ? [['price', draft.price.toString()], ['location', `https://plebdevs.com/details/${draft.id}`]] : []), + ]; + + return event; + }; + + const handlePublishedResource = async (e) => { + e.preventDefault(); + + // create new object with state fields + const updatedDraft = { + title, + summary, + price, + content, + image: coverImage, + topics: [...topics.map(topic => topic.trim().toLowerCase()), 'plebdevs', 'resource'] + } + + console.log('handlePublishedResource', updatedDraft); + + const event = await buildEvent(updatedDraft); + + console.log('event', event); + + try { + await ndk.connect(); + + const published = await ndk.publish(event); + + if (published) { + showToast('success', 'Success', 'Resource published successfully.'); + router.push(`/resource/${event.id}`); + } else { + showToast('error', 'Error', 'Failed to publish resource. Please try again.'); + } + } catch (error) { + console.error(error); + showToast('error', 'Error', 'Failed to publish resource. Please try again.'); + } + } + const handleSubmit = async (e) => { e.preventDefault(); @@ -115,7 +186,7 @@ const ResourceForm = ({ draft = null }) => { }; return ( -
+
setTitle(e.target.value)} placeholder="Title" />
diff --git a/src/components/navbar/user/UserAvatar.js b/src/components/navbar/user/UserAvatar.js index 93fb9ce..ac314cb 100644 --- a/src/components/navbar/user/UserAvatar.js +++ b/src/components/navbar/user/UserAvatar.js @@ -89,7 +89,7 @@ const UserAvatar = () => { icon="pi pi-user" className="text-[#f8f8ff]" rounded - onClick={() => router.push('/login')} + onClick={() => router.push('/auth/signin')} size={windowWidth < 768 ? 'small' : 'normal'} /> ); diff --git a/src/db/models/resourceModels.js b/src/db/models/resourceModels.js index 818af61..78900a2 100644 --- a/src/db/models/resourceModels.js +++ b/src/db/models/resourceModels.js @@ -19,6 +19,19 @@ export const getResourceById = async (id) => { }); }; +export async function isResourcePartOfAnyCourse(resourceId) { + const courses = await prisma.course.findMany({ + where: { + resources: { + some: { + id: resourceId + } + } + } + }); + return courses.length > 0; +} + export const createResource = async (data) => { return await prisma.resource.create({ data, diff --git a/src/hooks/nostrQueries/content/useCoursesQuery.js b/src/hooks/nostrQueries/content/useCoursesQuery.js index 44f2228..80b626c 100644 --- a/src/hooks/nostrQueries/content/useCoursesQuery.js +++ b/src/hooks/nostrQueries/content/useCoursesQuery.js @@ -1,46 +1,71 @@ import { useState, useEffect } from 'react'; import { useQuery } from '@tanstack/react-query'; import { useNDKContext } from '@/context/NDKContext'; +import { useContentIdsQuery } from '@/hooks/apiQueries/useContentIdsQuery'; -const AUTHOR_PUBKEY = process.env.NEXT_PUBLIC_AUTHOR_PUBKEY +const AUTHOR_PUBKEY = process.env.NEXT_PUBLIC_AUTHOR_PUBKEY; export function useCoursesQuery() { const [isClient, setIsClient] = useState(false); + + const { contentIds, contentIdsLoading, contentIdsError, refetchContentIds } = useContentIdsQuery(); const ndk = useNDKContext(); useEffect(() => { setIsClient(true); }, []); + useEffect(() => { + refetchContentIds(); + }, [refetchContentIds]); + + const hasRequiredProperties = (event) => { + if (contentIdsLoading) { + return false; + } + + const hasCourseTag = event.tags.some(([tag, value]) => tag === "t" && value === "course"); + const hasId = contentIds.includes(event.id); + return hasCourseTag && hasId; + }; + const fetchCoursesFromNDK = async () => { try { - console.log('Fetching courses from NDK'); + if (contentIdsLoading) { + return []; // or a loading state indication + } + if (contentIdsError) { + console.error('Error fetching content IDs:', contentIdsError); + return []; + } + if (!contentIds) { + return []; + } + await ndk.connect(); - + const filter = { kinds: [30004], authors: [AUTHOR_PUBKEY] }; const events = await ndk.fetchEvents(filter); - + if (events && events.size > 0) { const eventsArray = Array.from(events); - console.log('eventsArray', eventsArray) - // const resources = eventsArray.filter(event => hasRequiredTags(event.tags)); - // return resources; - return eventsArray; + const courses = eventsArray.filter(event => hasRequiredProperties(event)); + return courses; } return []; } catch (error) { - console.error('Error fetching workshops from NDK:', 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, - }) + 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 } -} \ No newline at end of file + return { courses, coursesLoading, coursesError, refetchCourses }; +} diff --git a/src/hooks/nostrQueries/content/useResourcesQuery.js b/src/hooks/nostrQueries/content/useResourcesQuery.js index 3884626..bfaff7b 100644 --- a/src/hooks/nostrQueries/content/useResourcesQuery.js +++ b/src/hooks/nostrQueries/content/useResourcesQuery.js @@ -1,25 +1,47 @@ import { useState, useEffect } from 'react'; import { useQuery } from '@tanstack/react-query'; import { useNDKContext } from '@/context/NDKContext'; +import { useContentIdsQuery } from '@/hooks/apiQueries/useContentIdsQuery'; const AUTHOR_PUBKEY = process.env.NEXT_PUBLIC_AUTHOR_PUBKEY export function useResourcesQuery() { - const [isClient, setIsClient] = useState(false); - const ndk = useNDKContext(); + const [isClient, setIsClient] = useState(false); - useEffect(() => { - setIsClient(true); - }, []); + const { contentIds, contentIdsLoading, contentIdsError, refetchContentIds } = useContentIdsQuery(); + const ndk = useNDKContext(); -const hasRequiredTags = (tags) => { - const hasPlebDevs = tags.some(([tag, value]) => tag === "t" && value === "plebdevs"); - const hasWorkshop = tags.some(([tag, value]) => tag === "t" && value === "resource"); - return hasPlebDevs && hasWorkshop; -}; + useEffect(() => { + setIsClient(true); + }, []); -const fetchResourcesFromNDK = async () => { - try { + useEffect(() => { + refetchContentIds(); + }, [refetchContentIds]); + + const hasRequiredProperties = (event) => { + if (!contentIds) { + return false; + } + + const hasPlebDevs = event.tags.some(([tag, value]) => tag === "t" && value === "plebdevs"); + const hasWorkshop = event.tags.some(([tag, value]) => tag === "t" && value === "resource"); + const hasId = contentIds.includes(event.id); + return hasPlebDevs && hasWorkshop && hasId; + }; + + const fetchResourcesFromNDK = async () => { + try { + if (contentIdsLoading) { + return []; // or a loading state indication + } + if (contentIdsError) { + console.error('Error fetching content IDs:', contentIdsError); + return []; + } + if (!contentIds) { + return []; + } console.log('Fetching workshops from NDK'); await ndk.connect(); @@ -27,19 +49,19 @@ const fetchResourcesFromNDK = async () => { const events = await ndk.fetchEvents(filter); if (events && events.size > 0) { - const eventsArray = Array.from(events); - console.log('eventsArray', eventsArray) - const resources = eventsArray.filter(event => hasRequiredTags(event.tags)); - return resources; + const eventsArray = Array.from(events); + console.log('eventsArray', eventsArray) + const resources = eventsArray.filter(event => hasRequiredProperties(event)); + return resources; } return []; - } catch (error) { + } catch (error) { console.error('Error fetching workshops from NDK:', error); return []; - } -}; + } + }; -const { data: resources, isLoading: resourcesLoading, error: resourcesError, refetch: refetchResources } = useQuery({ + const { data: resources, isLoading: resourcesLoading, error: resourcesError, refetch: refetchResources } = useQuery({ queryKey: ['resources', isClient], queryFn: fetchResourcesFromNDK, staleTime: 1000 * 60 * 30, // 30 minutes diff --git a/src/hooks/nostrQueries/content/useWorkshopsQuery.js b/src/hooks/nostrQueries/content/useWorkshopsQuery.js index e2d2cd9..d1c07cd 100644 --- a/src/hooks/nostrQueries/content/useWorkshopsQuery.js +++ b/src/hooks/nostrQueries/content/useWorkshopsQuery.js @@ -1,51 +1,72 @@ import { useState, useEffect } from 'react'; import { useQuery } from '@tanstack/react-query'; import { useNDKContext } from '@/context/NDKContext'; +import { useContentIdsQuery } from '@/hooks/apiQueries/useContentIdsQuery'; -const AUTHOR_PUBKEY = process.env.NEXT_PUBLIC_AUTHOR_PUBKEY +const AUTHOR_PUBKEY = process.env.NEXT_PUBLIC_AUTHOR_PUBKEY; export function useWorkshopsQuery() { const [isClient, setIsClient] = useState(false); + + const { contentIds, contentIdsLoading, contentIdsError, refetchContentIds } = useContentIdsQuery(); const ndk = useNDKContext(); useEffect(() => { setIsClient(true); }, []); -const hasRequiredTags = (tags) => { - const hasPlebDevs = tags.some(([tag, value]) => tag === "t" && value === "plebdevs"); - const hasWorkshop = tags.some(([tag, value]) => tag === "t" && value === "workshop"); - return hasPlebDevs && hasWorkshop; -}; + useEffect(() => { + refetchContentIds(); + }, [refetchContentIds]); -const fetchWorkshopsFromNDK = async () => { - try { - console.log('Fetching workshops from NDK'); - await ndk.connect(); - - const filter = { kinds: [30023, 30402], authors: [AUTHOR_PUBKEY] }; - const events = await ndk.fetchEvents(filter); - - if (events && events.size > 0) { - const eventsArray = Array.from(events); - console.log('eventsArray', eventsArray) - const resources = eventsArray.filter(event => hasRequiredTags(event.tags)); - return resources; + const hasRequiredProperties = (event) => { + if (contentIdsLoading) { + return false; } - return []; - } catch (error) { - console.error('Error fetching workshops from NDK:', error); - return []; - } -}; + const hasPlebDevs = event.tags.some(([tag, value]) => tag === "t" && value === "plebdevs"); + const hasWorkshop = event.tags.some(([tag, value]) => tag === "t" && value === "workshop"); + const hasId = contentIds.includes(event.id); + return hasPlebDevs && hasWorkshop && hasId; + }; -const { data: workshops, isLoading: workshopsLoading, error: workshopsError, refetch: refetchWorkshops } = useQuery({ - queryKey: ['workshops', isClient], - queryFn: fetchWorkshopsFromNDK, - staleTime: 1000 * 60 * 30, // 30 minutes - refetchInterval: 1000 * 60 * 30, // 30 minutes - enabled: isClient, - }) + const fetchWorkshopsFromNDK = async () => { + try { + if (contentIdsLoading) { + return []; // or a loading state indication + } + if (contentIdsError) { + console.error('Error fetching content IDs:', contentIdsError); + return []; + } + if (!contentIds) { + return []; + } + console.log('Fetching workshops from NDK'); + await ndk.connect(); - return { workshops, workshopsLoading, workshopsError, refetchWorkshops } -} \ No newline at end of file + const filter = { kinds: [30023, 30402], authors: [AUTHOR_PUBKEY] }; + const events = await ndk.fetchEvents(filter); + + if (events && events.size > 0) { + const eventsArray = Array.from(events); + console.log('eventsArray', eventsArray); + const workshops = eventsArray.filter(event => hasRequiredProperties(event)); + return workshops; + } + return []; + } catch (error) { + console.error('Error fetching workshops from NDK:', error); + return []; + } + }; + + const { data: workshops, isLoading: workshopsLoading, error: workshopsError, refetch: refetchWorkshops } = useQuery({ + queryKey: ['workshops', isClient], + queryFn: fetchWorkshopsFromNDK, + staleTime: 1000 * 60 * 30, // 30 minutes + refetchInterval: 1000 * 60 * 30, // 30 minutes + enabled: isClient, + }); + + return { workshops, workshopsLoading, workshopsError, refetchWorkshops }; +} diff --git a/src/pages/api/auth/[...nextauth].js b/src/pages/api/auth/[...nextauth].js index ad44590..9eee582 100644 --- a/src/pages/api/auth/[...nextauth].js +++ b/src/pages/api/auth/[...nextauth].js @@ -40,11 +40,13 @@ export default NextAuth({ // Check if user exists, create if not const response = await axios.get(`${BASE_URL}/api/users/${credentials.pubkey}`); if (response.status === 200 && response.data) { - return response.data; + const fields = await findKind0Fields(profile); + return { pubkey: credentials.pubkey, ...fields }; } else if (response.status === 204) { // Create user if (profile) { const fields = await findKind0Fields(profile); + console.log('FEEEEELDS', fields); const payload = { pubkey: credentials.pubkey, ...fields }; const createUserResponse = await axios.post(`${BASE_URL}/api/users`, payload); diff --git a/src/pages/api/resources/[slug].js b/src/pages/api/resources/[slug].js index e8f4d66..b217adb 100644 --- a/src/pages/api/resources/[slug].js +++ b/src/pages/api/resources/[slug].js @@ -1,4 +1,4 @@ -import { getResourceById, updateResource, deleteResource } from "@/db/models/resourceModels"; +import { getResourceById, updateResource, deleteResource, isResourcePartOfAnyCourse } from "@/db/models/resourceModels"; export default async function handler(req, res) { const { slug } = req.query; @@ -23,13 +23,17 @@ export default async function handler(req, res) { } } else if (req.method === 'DELETE') { try { - await deleteResource(slug); - res.status(204).end(); + const isPartOfAnyCourse = await isResourcePartOfAnyCourse(slug); + if (isPartOfAnyCourse) { + res.status(400).json({ error: 'Resource is part of one or more courses' }); + } else { + await deleteResource(slug); + res.status(204).end(); + } } catch (error) { res.status(500).json({ error: error.message }); } } else { - // Handle any other HTTP method res.setHeader('Allow', ['GET', 'PUT', 'DELETE']); res.status(405).end(`Method ${req.method} Not Allowed`); } diff --git a/src/pages/details/[slug]/edit.js b/src/pages/details/[slug]/edit.js new file mode 100644 index 0000000..e265b7f --- /dev/null +++ b/src/pages/details/[slug]/edit.js @@ -0,0 +1,47 @@ +import React, { useState, useEffect } from "react"; +import { useRouter } from "next/router"; +import { parseEvent } from "@/utils/nostr"; +import ResourceForm from "@/components/forms/ResourceForm"; +import WorkshopForm from "@/components/forms/WorkshopForm"; +import CourseForm from "@/components/forms/CourseForm"; +import { useNDKContext } from "@/context/NDKContext"; +import { useToast } from "@/hooks/useToast"; + +export default function Edit() { + const [event, setEvent] = useState(null); + + const ndk = useNDKContext(); + const router = useRouter(); + const { showToast } = useToast(); + + useEffect(() => { + if (router.isReady) { + const { slug } = router.query; + + const fetchEvent = async () => { + await ndk.connect(); + + const fetchedEvent = await ndk.fetchEvent(slug); + + if (fetchedEvent) { + const parsedEvent = parseEvent(fetchedEvent); + console.log('parsedEvent:', parsedEvent); + setEvent(parsedEvent); + } else { + showToast('error', 'Error', 'Event not found.'); + } + } + + fetchEvent(); + } + }, [router.isReady, router.query, ndk, showToast]); + + return ( +
+

Edit Published Event

+ {event?.topics.includes('course') && } + {event?.topics.includes('workshop') && } + {event?.topics.includes('resource') && } +
+ ); +} diff --git a/src/pages/details/[slug].js b/src/pages/details/[slug]/index.js similarity index 83% rename from src/pages/details/[slug].js rename to src/pages/details/[slug]/index.js index 092b2c1..935db59 100644 --- a/src/pages/details/[slug].js +++ b/src/pages/details/[slug]/index.js @@ -1,15 +1,18 @@ import React, { useEffect, useState } from 'react'; +import axios from 'axios'; import { useRouter } from 'next/router'; -import { parseEvent, findKind0Fields, hexToNpub } from '@/utils/nostr'; +import { parseEvent, findKind0Fields } from '@/utils/nostr'; import { useImageProxy } from '@/hooks/useImageProxy'; import { getSatAmountFromInvoice } from '@/utils/lightning'; import ZapDisplay from '@/components/zaps/ZapDisplay'; import { Tag } from 'primereact/tag'; +import { Button } from 'primereact/button'; import { nip19, nip04 } from 'nostr-tools'; import { useSession } from 'next-auth/react'; import Image from 'next/image'; import dynamic from 'next/dynamic'; import ZapThreadsWrapper from '@/components/ZapThreadsWrapper'; +import { useToast } from '@/hooks/useToast'; import { useNDKContext } from '@/context/NDKContext'; import { useZapsSubscription } from '@/hooks/nostrQueries/zaps/useZapsSubscription'; import 'primeicons/primeicons.css'; @@ -38,11 +41,13 @@ export default function Details() { const [zapAmount, setZapAmount] = useState(null); const [paidResource, setPaidResource] = useState(false); const [decryptedContent, setDecryptedContent] = useState(null); + const [authorView, setAuthorView] = useState(false); const ndk = useNDKContext(); const { data: session, status } = useSession(); const [user, setUser] = useState(null); const { returnImageProxy } = useImageProxy(); + const { showToast } = useToast(); const { zaps, zapsLoading, zapsError } = useZapsSubscription({ event: processedEvent }); const router = useRouter(); @@ -102,6 +107,9 @@ export default function Details() { if (event) { setEvent(event); + if (user && user.pubkey === event.pubkey) { + setAuthorView(true); + } } } catch (error) { console.error('Error fetching event:', error); @@ -111,7 +119,7 @@ export default function Details() { fetchEvent(slug); } } - }, [router.isReady, router.query, ndk]); + }, [router.isReady, router.query, ndk, user]); useEffect(() => { const fetchAuthor = async (pubkey) => { @@ -171,6 +179,25 @@ export default function Details() { setZapAmount(total); }, [zaps]); + const handleDelete = async () => { + try { + const response = await axios.delete(`/api/resources/${processedEvent.id}`); + if (response.status === 204) { + showToast('success', 'Success', 'Resource deleted successfully.'); + router.push('/'); + } + } catch (error) { + if (error.response && error.response.data && error.response.data.error.includes("Invalid `prisma.resource.delete()`")) { + showToast('error', 'Error', 'Resource cannot be deleted because it is part of a course, delete the course first.'); + } + else if (error.response && error.response.data && error.response.data.error) { + showToast('error', 'Error', error.response.data.error); + } else { + showToast('error', 'Error', 'Failed to delete resource. Please try again.'); + } + } + } + return (
@@ -227,6 +254,14 @@ export default function Details() {
+ {authorView && ( +
+
+
+
+ )} {typeof window !== 'undefined' && nAddress !== null && (
{ - const { data: session, status } = useSession(); - const [user, setUser] = useState(null); - const { returnImageProxy } = useImageProxy(); - const menu = useRef(null); + const [user, setUser] = useState(null); - useEffect(() => { - if (session) { - setUser(session.user); - } - }, [session]); + const { data: session, status } = useSession(); + const { returnImageProxy } = useImageProxy(); + const menu = useRef(null); - const purchases = []; + useEffect(() => { + if (session) { + setUser(session.user); + } + }, [session]); - const menuItems = [ - { - label: "Edit", - icon: "pi pi-pencil", - command: () => { - // Add your edit functionality here - }, - }, - { - label: "Delete", - icon: "pi pi-trash", - command: () => { - // Add your delete functionality here - }, - }, - ]; + const purchases = []; - const header = ( -
- Purchases -
- ); + const menuItems = [ + { + label: "Edit", + icon: "pi pi-pencil", + command: () => { + // Add your edit functionality here + }, + }, + { + label: "Delete", + icon: "pi pi-trash", + command: () => { + // Add your delete functionality here + }, + }, + ]; - return ( - user && ( -
-
-
- user's avatar - menu.current.toggle(e)} - > - -
- -

- {user.username || "Anon"} -

-

- {user.pubkey} -

-
-

Connect Your Lightning Wallet

- -
-
-

Subscription

-

You currently have no active subscription

-
+ const header = ( +
+ Purchases
- - - - - - - -
- ) - ); + ); + + return ( + user && ( +
+
+
+ user's avatar + menu.current.toggle(e)} + > + +
+ +

+ {user.username || "Anon"} +

+

+ {user.pubkey} +

+
+

Connect Your Lightning Wallet

+ +
+
+

Subscription

+

You currently have no active subscription

+
+
+ + + + + + + +
+ ) + ); }; export default Profile; diff --git a/src/utils/nostr.js b/src/utils/nostr.js index 3344289..5613d25 100644 --- a/src/utils/nostr.js +++ b/src/utils/nostr.js @@ -1,6 +1,7 @@ import { nip19 } from "nostr-tools"; export const findKind0Fields = async (kind0) => { + console.log('kind0', kind0); let fields = {} const usernameProperties = ['name', 'displayName', 'display_name', 'username', 'handle', 'alias']; @@ -20,7 +21,7 @@ export const findKind0Fields = async (kind0) => { fields.username = username; } - const avatar = findTruthyPropertyValue(kind0, ['picture', 'avatar', 'profilePicture', 'profile_picture']); + const avatar = findTruthyPropertyValue(kind0, ['picture', 'avatar', 'profilePicture', 'profile_picture', 'image']); if (avatar) { fields.avatar = avatar;