mirror of
https://github.com/AustinKelsay/plebdevs.git
synced 2025-04-19 19:01:19 +00:00
Improvements to profile pge and all user content views on profile
This commit is contained in:
parent
aa60c23611
commit
b61e927c0c
@ -1,95 +1,104 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import { useRouter } from "next/router";
|
||||
import { Button } from "primereact/button";
|
||||
import MenuTab from "@/components/menutab/MenuTab";
|
||||
import { useLocalStorageWithEffect } from "@/hooks/useLocalStorage";
|
||||
import { useNostr } from "@/hooks/useNostr";
|
||||
import { useCoursesQuery } from "@/hooks/nostrQueries/content/useCoursesQuery";
|
||||
import { useResourcesQuery } from "@/hooks/nostrQueries/content/useResourcesQuery";
|
||||
import { useWorkshopsQuery } from "@/hooks/nostrQueries/content/useWorkshopsQuery";
|
||||
import { useDraftsQuery } from "@/hooks/apiQueries/useDraftsQuery";
|
||||
import { useContentIdsQuery } from "@/hooks/apiQueries/useContentIdsQuery";
|
||||
import { useToast } from "@/hooks/useToast";
|
||||
import ContentList from "@/components/content/lists/ContentList";
|
||||
import { parseEvent } from "@/utils/nostr";
|
||||
import { useToast } from "@/hooks/useToast";
|
||||
import { useNDKContext } from "@/context/NDKContext";
|
||||
|
||||
const AUTHOR_PUBKEY = process.env.NEXT_PUBLIC_AUTHOR_PUBKEY;
|
||||
|
||||
const UserContent = () => {
|
||||
const [activeIndex, setActiveIndex] = useState(0);
|
||||
const [drafts, setDrafts] = useState([]);
|
||||
const [user, setUser] = useLocalStorageWithEffect('user', {});
|
||||
const [courses, setCourses] = useState([]);
|
||||
const [resources, setResources] = useState([]);
|
||||
const [workshops, setWorkshops] = useState([]);
|
||||
const { fetchCourses, fetchResources, fetchWorkshops } = useNostr();
|
||||
const [isClient, setIsClient] = useState(false);
|
||||
const [content, setContent] = useState([]);
|
||||
const [publishedContent, setPublishedContent] = useState([]);
|
||||
|
||||
const [user] = useLocalStorageWithEffect("user", {});
|
||||
const router = useRouter();
|
||||
const { showToast } = useToast();
|
||||
const ndk = useNDKContext();
|
||||
const { courses, coursesLoading, coursesError } = useCoursesQuery();
|
||||
const { resources, resourcesLoading, resourcesError } = useResourcesQuery();
|
||||
const { workshops, workshopsLoading, workshopsError } = useWorkshopsQuery();
|
||||
const { drafts, draftsLoading, draftsError } = useDraftsQuery();
|
||||
const { contentIds, contentIdsLoading, contentIdsError, refetchContentIds } = useContentIdsQuery();
|
||||
|
||||
useEffect(() => {
|
||||
setIsClient(true);
|
||||
}, []);
|
||||
|
||||
const contentItems = [
|
||||
{ label: 'Published', icon: 'pi pi-verified' },
|
||||
{ label: 'Drafts', icon: 'pi pi-file-edit' },
|
||||
{ label: 'Resources', icon: 'pi pi-book' },
|
||||
{ label: 'Workshops', icon: 'pi pi-video' },
|
||||
{ label: 'Courses', icon: 'pi pi-desktop' }
|
||||
{ label: "Published", icon: "pi pi-verified" },
|
||||
{ label: "Drafts", icon: "pi pi-file-edit" },
|
||||
{ label: "Resources", icon: "pi pi-book" },
|
||||
{ label: "Workshops", icon: "pi pi-video" },
|
||||
{ label: "Courses", icon: "pi pi-desktop" },
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
if (user && user.id) {
|
||||
fetchAllContent();
|
||||
}
|
||||
}, [user]);
|
||||
const fetchAllContentFromNDK = async (ids) => {
|
||||
try {
|
||||
await ndk.connect();
|
||||
const filter = { "#d": ids, authors: [AUTHOR_PUBKEY] };
|
||||
|
||||
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);
|
||||
const uniqueEvents = new Set();
|
||||
|
||||
// Fetch resources, workshops, and courses from Nostr
|
||||
const resources = await fetchResources();
|
||||
const workshops = await fetchWorkshops();
|
||||
const courses = await fetchCourses();
|
||||
|
||||
if (drafts.length > 0) {
|
||||
setDrafts(drafts);
|
||||
}
|
||||
if (resources.length > 0) {
|
||||
setResources(resources);
|
||||
}
|
||||
if (workshops.length > 0) {
|
||||
setWorkshops(workshops);
|
||||
}
|
||||
if (courses.length > 0) {
|
||||
setCourses(courses);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
showToast('error', 'Error', 'Failed to fetch content');
|
||||
}
|
||||
};
|
||||
|
||||
const getContentByIndex = (index) => {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return []
|
||||
case 1:
|
||||
return drafts;
|
||||
case 2:
|
||||
return resources.map(resource => {
|
||||
const { id, content, title, summary, image, published_at } = parseEvent(resource);
|
||||
return { id, content, title, summary, image, published_at };
|
||||
const events = await ndk.fetchEvents(filter);
|
||||
|
||||
events.forEach(event => {
|
||||
uniqueEvents.add(event);
|
||||
});
|
||||
case 3:
|
||||
return workshops.map(workshop => {
|
||||
const { id, content, title, summary, image, published_at } = parseEvent(workshop);
|
||||
return { id, content, title, summary, image, published_at };
|
||||
})
|
||||
case 4:
|
||||
return courses.map(course => {
|
||||
const { id, content, title, summary, image, published_at } = parseEvent(course);
|
||||
return { id, content, title, summary, image, published_at };
|
||||
})
|
||||
default:
|
||||
|
||||
console.log('uniqueEvents', uniqueEvents)
|
||||
return Array.from(uniqueEvents);
|
||||
} catch (error) {
|
||||
console.error('Error fetching workshops from NDK:', error);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const fetchContent = async () => {
|
||||
if (contentIds && isClient) {
|
||||
const content = await fetchAllContentFromNDK(contentIds);
|
||||
setPublishedContent(content);
|
||||
}
|
||||
}
|
||||
};
|
||||
fetchContent();
|
||||
}, [contentIds, isClient, ndk]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isClient) {
|
||||
const getContentByIndex = (index) => {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return publishedContent.map(parseEvent) || [];
|
||||
case 1:
|
||||
return drafts || [];
|
||||
case 2:
|
||||
return resources?.map(parseEvent) || [];
|
||||
case 3:
|
||||
return workshops?.map(parseEvent) || [];
|
||||
case 4:
|
||||
return courses?.map(parseEvent) || [];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
setContent(getContentByIndex(activeIndex));
|
||||
}
|
||||
}, [activeIndex, isClient, drafts, resources, workshops, courses, publishedContent])
|
||||
|
||||
const isLoading = coursesLoading || resourcesLoading || workshopsLoading || draftsLoading || contentIdsLoading;
|
||||
const isError = coursesError || resourcesError || workshopsError || draftsError || contentIdsError;
|
||||
|
||||
return (
|
||||
<div className="w-[90vw] mx-auto max-tab:w-[100vw] max-mob:w-[100vw]">
|
||||
@ -97,13 +106,29 @@ const UserContent = () => {
|
||||
<h2 className="text-center my-4">Your Content</h2>
|
||||
</div>
|
||||
<div className="flex flex-row w-full justify-between px-4">
|
||||
<MenuTab items={contentItems} activeIndex={activeIndex} onTabChange={setActiveIndex} />
|
||||
<Button onClick={() => router.push('/create')} label="Create" severity="success" outlined className="mt-2" />
|
||||
<MenuTab
|
||||
items={contentItems}
|
||||
activeIndex={activeIndex}
|
||||
onTabChange={setActiveIndex}
|
||||
/>
|
||||
<Button
|
||||
onClick={() => router.push("/create")}
|
||||
label="Create"
|
||||
severity="success"
|
||||
outlined
|
||||
className="mt-2"
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full mx-auto my-8">
|
||||
<div className="w-full mx-auto my-8">
|
||||
{getContentByIndex(activeIndex).length > 0 && (
|
||||
<ContentList content={getContentByIndex(activeIndex)} />
|
||||
{isLoading ? (
|
||||
<p>Loading...</p>
|
||||
) : isError ? (
|
||||
<p>Error loading content.</p>
|
||||
) : content.length > 0 ? (
|
||||
<ContentList content={content} />
|
||||
) : (
|
||||
<p>No content available.</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
32
src/hooks/apiQueries/useContentIdsQuery.js
Normal file
32
src/hooks/apiQueries/useContentIdsQuery.js
Normal file
@ -0,0 +1,32 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import axios from 'axios';
|
||||
|
||||
export function useContentIdsQuery() {
|
||||
const [isClient, setIsClient] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setIsClient(true);
|
||||
}, []);
|
||||
|
||||
const fetchContentIdsDB = async () => {
|
||||
try {
|
||||
const response = await axios.get(`/api/content/all`);
|
||||
const contentIds = response.data;
|
||||
return contentIds;
|
||||
} catch (error) {
|
||||
console.error('Error fetching contentIds from DB:', error);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const { data: contentIds, isLoading: contentIdsLoading, error: contentIdsError, refetch: refetchContentIds } = useQuery({
|
||||
queryKey: ['contentIds', isClient],
|
||||
queryFn: fetchContentIdsDB,
|
||||
staleTime: 1000 * 60 * 30, // 30 minutes
|
||||
refetchInterval: 1000 * 60 * 30, // 30 minutes
|
||||
enabled: isClient
|
||||
});
|
||||
|
||||
return { contentIds, contentIdsLoading, contentIdsError, refetchContentIds };
|
||||
}
|
38
src/hooks/apiQueries/useDraftsQuery.js
Normal file
38
src/hooks/apiQueries/useDraftsQuery.js
Normal file
@ -0,0 +1,38 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import axios from 'axios';
|
||||
import { useLocalStorageWithEffect } from '@/hooks/useLocalStorage';
|
||||
|
||||
export function useDraftsQuery() {
|
||||
const [isClient, setIsClient] = useState(false);
|
||||
const [user] = useLocalStorageWithEffect('user', {});
|
||||
|
||||
useEffect(() => {
|
||||
setIsClient(true);
|
||||
}, []);
|
||||
|
||||
const fetchDraftsDB = async () => {
|
||||
try {
|
||||
if (!user.id) {
|
||||
return [];
|
||||
}
|
||||
const response = await axios.get(`/api/drafts/all/${user.id}`);
|
||||
const drafts = response.data;
|
||||
console.log('drafts:', drafts);
|
||||
return drafts;
|
||||
} catch (error) {
|
||||
console.error('Error fetching drafts from DB:', error);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const { data: drafts, isLoading: draftsLoading, error: draftsError, refetch: refetchDrafts } = useQuery({
|
||||
queryKey: ['drafts', isClient],
|
||||
queryFn: fetchDraftsDB,
|
||||
staleTime: 1000 * 60 * 30, // 30 minutes
|
||||
refetchInterval: 1000 * 60 * 30, // 30 minutes
|
||||
enabled: isClient && !!user.id, // Only enable if client-side and user ID is available
|
||||
});
|
||||
|
||||
return { drafts, draftsLoading, draftsError, refetchDrafts };
|
||||
}
|
41
src/hooks/nostrQueries/content/useAllContentQuery.js
Normal file
41
src/hooks/nostrQueries/content/useAllContentQuery.js
Normal file
@ -0,0 +1,41 @@
|
||||
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 = useNDKContext();
|
||||
|
||||
useEffect(() => {
|
||||
setIsClient(true);
|
||||
}, []);
|
||||
|
||||
const fetchAllContentFromNDK = async (ids) => {
|
||||
try {
|
||||
console.log('Fetching all content from NDK');
|
||||
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 workshops 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,5 +1,6 @@
|
||||
import { useCallback, useContext, useRef } from 'react';
|
||||
import { useState, useEffect, useCallback, useContext, useRef } from 'react';
|
||||
import axios from 'axios';
|
||||
import { nip57, nip19 } from 'nostr-tools';
|
||||
import { NostrContext } from '@/context/NostrContext';
|
||||
import { lnurlEncode } from '@/utils/lnurl';
|
||||
import { parseEvent } from '@/utils/nostr';
|
||||
@ -15,6 +16,8 @@ const defaultRelays = [
|
||||
"wss://relay.primal.net/"
|
||||
];
|
||||
|
||||
const AUTHOR_PUBKEY = process.env.NEXT_PUBLIC_AUTHOR_PUBKEY;
|
||||
|
||||
export function useNostr() {
|
||||
const pool = useContext(NostrContext);
|
||||
const subscriptionQueue = useRef([]);
|
||||
@ -363,6 +366,117 @@ export function useNostr() {
|
||||
[fetchKind0]
|
||||
);
|
||||
|
||||
const fetchResources = useCallback(async () => {
|
||||
const filter = [{ kinds: [30023, 30402], authors: [AUTHOR_PUBKEY] }];
|
||||
const hasRequiredTags = (tags) => {
|
||||
const hasPlebDevs = tags.some(([tag, value]) => tag === "t" && value === "plebdevs");
|
||||
const hasResource = tags.some(([tag, value]) => tag === "t" && value === "resource");
|
||||
return hasPlebDevs && hasResource;
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let resources = [];
|
||||
const subscription = subscribe(
|
||||
filter,
|
||||
{
|
||||
onevent: (event) => {
|
||||
if (hasRequiredTags(event.tags)) {
|
||||
resources.push(event);
|
||||
}
|
||||
},
|
||||
onerror: (error) => {
|
||||
console.error('Error fetching resources:', error);
|
||||
// Don't resolve here, just log the error
|
||||
},
|
||||
onclose: () => {
|
||||
// Don't resolve here either
|
||||
},
|
||||
},
|
||||
2000 // Adjust the timeout value as needed
|
||||
);
|
||||
|
||||
// Set a timeout to resolve the promise after collecting events
|
||||
setTimeout(() => {
|
||||
subscription?.close();
|
||||
resolve(resources);
|
||||
}, 2000); // Adjust the timeout value as needed
|
||||
});
|
||||
}, [subscribe]);
|
||||
|
||||
const fetchWorkshops = useCallback(async () => {
|
||||
const filter = [{ kinds: [30023, 30402], authors: [AUTHOR_PUBKEY] }];
|
||||
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;
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let workshops = [];
|
||||
const subscription = subscribe(
|
||||
filter,
|
||||
{
|
||||
onevent: (event) => {
|
||||
if (hasRequiredTags(event.tags)) {
|
||||
workshops.push(event);
|
||||
}
|
||||
},
|
||||
onerror: (error) => {
|
||||
console.error('Error fetching workshops:', error);
|
||||
// Don't resolve here, just log the error
|
||||
},
|
||||
onclose: () => {
|
||||
// Don't resolve here either
|
||||
},
|
||||
},
|
||||
2000 // Adjust the timeout value as needed
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
subscription?.close();
|
||||
resolve(workshops);
|
||||
}, 2000); // Adjust the timeout value as needed
|
||||
});
|
||||
}, [subscribe]);
|
||||
|
||||
const fetchCourses = useCallback(async () => {
|
||||
const filter = [{ kinds: [30004], authors: [AUTHOR_PUBKEY] }];
|
||||
// Do we need required tags for courses? community instead?
|
||||
// const hasRequiredTags = (tags) => {
|
||||
// const hasPlebDevs = tags.some(([tag, value]) => tag === "t" && value === "plebdevs");
|
||||
// const hasCourse = tags.some(([tag, value]) => tag === "t" && value === "course");
|
||||
// return hasPlebDevs && hasCourse;
|
||||
// };
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let courses = [];
|
||||
const subscription = subscribe(
|
||||
filter,
|
||||
{
|
||||
onevent: (event) => {
|
||||
// if (hasRequiredTags(event.tags)) {
|
||||
// courses.push(event);
|
||||
// }
|
||||
courses.push(event);
|
||||
},
|
||||
onerror: (error) => {
|
||||
console.error('Error fetching courses:', error);
|
||||
// Don't resolve here, just log the error
|
||||
},
|
||||
onclose: () => {
|
||||
// Don't resolve here either
|
||||
},
|
||||
},
|
||||
2000 // Adjust the timeout value as needed
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
subscription?.close();
|
||||
resolve(courses);
|
||||
}, 2000); // Adjust the timeout value as needed
|
||||
});
|
||||
}, [subscribe]);
|
||||
|
||||
const publishResource = useCallback(
|
||||
async (resourceEvent) => {
|
||||
const published = await publish(resourceEvent);
|
||||
@ -447,5 +561,5 @@ export function useNostr() {
|
||||
[publish]
|
||||
);
|
||||
|
||||
return { subscribe, publish, fetchSingleEvent, fetchSingleNaddrEvent, fetchZapsForEvent, fetchKind0, zapEvent, fetchZapsForEvents, publishResource, publishCourse };
|
||||
return { subscribe, publish, fetchSingleEvent, fetchSingleNaddrEvent, fetchZapsForEvent, fetchKind0, fetchResources, fetchWorkshops, fetchCourses, zapEvent, fetchZapsForEvents, publishResource, publishCourse };
|
||||
}
|
@ -5,7 +5,7 @@ import { useImageProxy } from '@/hooks/useImageProxy';
|
||||
import { getSatAmountFromInvoice } from '@/utils/lightning';
|
||||
import ZapDisplay from '@/components/zaps/ZapDisplay';
|
||||
import { Tag } from 'primereact/tag';
|
||||
import { nip19 } from 'nostr-tools';
|
||||
import { nip19, nip04 } from 'nostr-tools';
|
||||
import { useLocalStorageWithEffect } from '@/hooks/useLocalStorage';
|
||||
import Image from 'next/image';
|
||||
import dynamic from 'next/dynamic';
|
||||
@ -27,6 +27,8 @@ const BitcoinConnectPayButton = dynamic(
|
||||
}
|
||||
);
|
||||
|
||||
const privkey = process.env.NEXT_PUBLIC_APP_PRIV_KEY;
|
||||
|
||||
export default function Details() {
|
||||
const [event, setEvent] = useState(null);
|
||||
const [processedEvent, setProcessedEvent] = useState({});
|
||||
@ -34,6 +36,9 @@ export default function Details() {
|
||||
const [bitcoinConnect, setBitcoinConnect] = useState(false);
|
||||
const [nAddress, setNAddress] = useState(null);
|
||||
const [zapAmount, setZapAmount] = useState(null);
|
||||
const [paidResource, setPaidResource] = useState(false);
|
||||
const [decryptedContent, setDecryptedContent] = useState(null);
|
||||
// const [user, setUser] = useState(null);
|
||||
|
||||
const ndk = useNDKContext();
|
||||
const [user] = useLocalStorageWithEffect('user', {});
|
||||
@ -42,6 +47,12 @@ export default function Details() {
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
if (processedEvent.price) {
|
||||
setPaidResource(true);
|
||||
}
|
||||
}, [processedEvent]);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined') return;
|
||||
|
||||
@ -52,6 +63,23 @@ export default function Details() {
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const decryptContent = async () => {
|
||||
if (user && paidResource) {
|
||||
if (!user.purchased.includes(processedEvent.id)) {
|
||||
// decrypt the content
|
||||
console.log('privkey', privkey);
|
||||
console.log('user.pubkey', user.pubkey);
|
||||
console.log('processedEvent.content', processedEvent.content);
|
||||
const decryptedContent = await nip04.decrypt(privkey, user.pubkey, processedEvent.content);
|
||||
console.log('decryptedContent', decryptedContent);
|
||||
setDecryptedContent(decryptedContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
decryptContent();
|
||||
}, [user, paidResource]);
|
||||
|
||||
useEffect(() => {
|
||||
if (router.isReady) {
|
||||
const { slug } = router.query;
|
||||
|
@ -6,7 +6,6 @@ import HeroBanner from '@/components/banner/HeroBanner';
|
||||
import ResourcesCarousel from '@/components/content/carousels/ResourcesCarousel';
|
||||
|
||||
export default function Home() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
|
@ -1,111 +1,98 @@
|
||||
import React, { useRef, useState, useEffect } from 'react';
|
||||
import React, { useRef, useState, useEffect } from "react";
|
||||
import { Button } from "primereact/button";
|
||||
import { DataTable } from 'primereact/datatable';
|
||||
import { Menu } from 'primereact/menu';
|
||||
import { Column } from 'primereact/column';
|
||||
import { DataTable } from "primereact/datatable";
|
||||
import { Menu } from "primereact/menu";
|
||||
import { Column } from "primereact/column";
|
||||
import { useImageProxy } from "@/hooks/useImageProxy";
|
||||
import { useRouter } from "next/router";
|
||||
import { useLocalStorageWithEffect } from "@/hooks/useLocalStorage";
|
||||
import UserContent from '@/components/profile/UserContent';
|
||||
import UserContent from "@/components/profile/UserContent";
|
||||
import Image from "next/image";
|
||||
import BitcoinConnectButton from '@/components/profile/BitcoinConnect';
|
||||
import BitcoinConnectButton from "@/components/profile/BitcoinConnect";
|
||||
|
||||
const Profile = () => {
|
||||
const [user, setUser] = useLocalStorageWithEffect('user', {});
|
||||
const { returnImageProxy } = useImageProxy();
|
||||
const menu = useRef(null);
|
||||
const [user] = useLocalStorageWithEffect("user", {});
|
||||
const { returnImageProxy } = useImageProxy();
|
||||
const menu = useRef(null);
|
||||
|
||||
const purchases = [
|
||||
// {
|
||||
// cost: 100,
|
||||
// name: 'Course 1',
|
||||
// category: 'Education',
|
||||
// date: '2021-09-01'
|
||||
// },
|
||||
// {
|
||||
// cost: 200,
|
||||
// name: 'Course 2',
|
||||
// category: 'Education',
|
||||
// date: '2021-09-01'
|
||||
// },
|
||||
// {
|
||||
// cost: 300,
|
||||
// name: 'Course 3',
|
||||
// category: 'Education',
|
||||
// date: '2021-09-01'
|
||||
// },
|
||||
// {
|
||||
// cost: 400,
|
||||
// name: 'Course 4',
|
||||
// category: 'Education',
|
||||
// date: '2021-09-01'
|
||||
// }
|
||||
];
|
||||
const 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
|
||||
}
|
||||
}
|
||||
];
|
||||
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 header = (
|
||||
<div className="flex flex-wrap align-items-center justify-content-between gap-2">
|
||||
<span className="text-xl text-900 font-bold text-white">Purchases</span>
|
||||
const header = (
|
||||
<div className="flex flex-wrap align-items-center justify-content-between gap-2">
|
||||
<span className="text-xl text-900 font-bold text-white">Purchases</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
user && (
|
||||
<div className="w-[90vw] mx-auto max-tab:w-[100vw] max-mob:w-[100vw]">
|
||||
<div className="w-[85vw] flex flex-col justify-center mx-auto max-tab:w-[100vw] max-mob:w-[100vw]">
|
||||
<div className="relative flex w-full items-center justify-center">
|
||||
<Image
|
||||
alt="user's avatar"
|
||||
src={returnImageProxy(user.avatar, user.pubkey)}
|
||||
width={100}
|
||||
height={100}
|
||||
className="rounded-full my-4"
|
||||
/>
|
||||
<i
|
||||
className="pi pi-ellipsis-h absolute right-24 text-2xl my-4 cursor-pointer hover:opacity-75"
|
||||
onClick={(e) => menu.current.toggle(e)}
|
||||
></i>
|
||||
<Menu model={menuItems} popup ref={menu} />
|
||||
</div>
|
||||
|
||||
<h1 className="text-center text-2xl my-2">
|
||||
{user.username || "Anon"}
|
||||
</h1>
|
||||
<h2 className="text-center text-xl my-2 truncate max-tab:px-4 max-mob:px-4">
|
||||
{user.pubkey}
|
||||
</h2>
|
||||
<div className="flex flex-col w-1/2 mx-auto my-4 justify-between items-center">
|
||||
<h2>Connect Your Lightning Wallet</h2>
|
||||
<BitcoinConnectButton />
|
||||
</div>
|
||||
<div className="flex flex-col w-1/2 mx-auto my-4 justify-between items-center">
|
||||
<h2>Subscription</h2>
|
||||
<p className="text-center">You currently have no active subscription</p>
|
||||
<Button
|
||||
label="Subscribe"
|
||||
className="p-button-raised p-button-success w-auto my-2 text-[#f8f8ff]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
user && (
|
||||
<div className="w-[90vw] mx-auto max-tab:w-[100vw] max-mob:w-[100vw]">
|
||||
<div className="w-[85vw] flex flex-col justify-center mx-auto max-tab:w-[100vw] max-mob:w-[100vw]">
|
||||
<div className="relative flex w-full items-center justify-center">
|
||||
<Image
|
||||
alt="user's avatar"
|
||||
src={returnImageProxy(user.avatar, user.pubkey)}
|
||||
width={100}
|
||||
height={100}
|
||||
className="rounded-full my-4"
|
||||
/>
|
||||
<i className="pi pi-ellipsis-h absolute right-24 text-2xl my-4 cursor-pointer hover:opacity-75"
|
||||
onClick={(e) => menu.current.toggle(e)}></i>
|
||||
<Menu model={menuItems} popup ref={menu} />
|
||||
</div>
|
||||
|
||||
|
||||
<h1 className="text-center text-2xl my-2">{user.username || "Anon"}</h1>
|
||||
<h2 className="text-center text-xl my-2 truncate max-tab:px-4 max-mob:px-4">{user.pubkey}</h2>
|
||||
<div className="flex flex-col w-1/2 mx-auto my-4 justify-between items-center">
|
||||
<h2>Connect Your Lightning Wallet</h2>
|
||||
<BitcoinConnectButton />
|
||||
</div>
|
||||
<div className="flex flex-col w-1/2 mx-auto my-4 justify-between items-center">
|
||||
<h2>Subscription</h2>
|
||||
<p className="text-center">You currently have no active subscription</p>
|
||||
<Button label="Subscribe" className="p-button-raised p-button-success w-auto my-2 text-[#f8f8ff]" />
|
||||
</div>
|
||||
</div>
|
||||
<DataTable emptyMessage="No purchases" value={purchases} tableStyle={{ minWidth: '100%' }} header={header}>
|
||||
<Column field="cost" header="Cost"></Column>
|
||||
<Column field="name" header="Name"></Column>
|
||||
<Column field="category" header="Category"></Column>
|
||||
<Column field="date" header="Date"></Column>
|
||||
</DataTable>
|
||||
<UserContent />
|
||||
</div>
|
||||
)
|
||||
<DataTable
|
||||
emptyMessage="No purchases"
|
||||
value={purchases}
|
||||
tableStyle={{ minWidth: "100%" }}
|
||||
header={header}
|
||||
>
|
||||
<Column field="cost" header="Cost"></Column>
|
||||
<Column field="name" header="Name"></Column>
|
||||
<Column field="category" header="Category"></Column>
|
||||
<Column field="date" header="Date"></Column>
|
||||
</DataTable>
|
||||
<UserContent />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default Profile
|
||||
export default Profile;
|
||||
|
@ -67,6 +67,9 @@ export const parseEvent = (event) => {
|
||||
case 'author':
|
||||
eventData.author = tag[1];
|
||||
break;
|
||||
case 'price':
|
||||
eventData.price = tag[1];
|
||||
break;
|
||||
// How do we get topics / tags?
|
||||
case 'l':
|
||||
// Grab index 1 and any subsequent elements in the array
|
||||
|
Loading…
x
Reference in New Issue
Block a user