mirror of
https://github.com/AustinKelsay/plebdevs.git
synced 2025-06-06 18:31:00 +00:00
naddress slugs for all published details pages, also view nostr note buttons on all content
This commit is contained in:
parent
3b077b542a
commit
8b782ffc60
@ -1,22 +1,34 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
|
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
|
||||||
import ZapDisplay from "@/components/zaps/ZapDisplay";
|
import ZapDisplay from "@/components/zaps/ZapDisplay";
|
||||||
import { FileText } from "lucide-react"
|
|
||||||
import Image from "next/image"
|
import Image from "next/image"
|
||||||
import { useZapsSubscription } from "@/hooks/nostrQueries/zaps/useZapsSubscription";
|
import { useZapsSubscription } from "@/hooks/nostrQueries/zaps/useZapsSubscription";
|
||||||
import { getTotalFromZaps } from "@/utils/lightning";
|
import { getTotalFromZaps } from "@/utils/lightning";
|
||||||
import { useImageProxy } from "@/hooks/useImageProxy";
|
import { useImageProxy } from "@/hooks/useImageProxy";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { formatTimestampToHowLongAgo } from "@/utils/time";
|
import { formatTimestampToHowLongAgo } from "@/utils/time";
|
||||||
|
import { nip19 } from "nostr-tools";
|
||||||
import { Tag } from "primereact/tag";
|
import { Tag } from "primereact/tag";
|
||||||
import GenericButton from "@/components/buttons/GenericButton";
|
import GenericButton from "@/components/buttons/GenericButton";
|
||||||
|
|
||||||
export function DocumentTemplate({ document }) {
|
export function DocumentTemplate({ document }) {
|
||||||
const { zaps, zapsLoading, zapsError } = useZapsSubscription({ event: document });
|
const { zaps, zapsLoading, zapsError } = useZapsSubscription({ event: document });
|
||||||
|
const [nAddress, setNAddress] = useState(null);
|
||||||
const [zapAmount, setZapAmount] = useState(0);
|
const [zapAmount, setZapAmount] = useState(0);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { returnImageProxy } = useImageProxy();
|
const { returnImageProxy } = useImageProxy();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (document && document?.id) {
|
||||||
|
const nAddress = nip19.naddrEncode({
|
||||||
|
pubkey: document.pubkey,
|
||||||
|
kind: document.kind,
|
||||||
|
identifier: document.id,
|
||||||
|
});
|
||||||
|
setNAddress(nAddress);
|
||||||
|
}
|
||||||
|
}, [document]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (zaps.length > 0) {
|
if (zaps.length > 0) {
|
||||||
const total = getTotalFromZaps(zaps, document);
|
const total = getTotalFromZaps(zaps, document);
|
||||||
@ -73,7 +85,7 @@ export function DocumentTemplate({ document }) {
|
|||||||
) : (
|
) : (
|
||||||
formatTimestampToHowLongAgo(document.created_at)
|
formatTimestampToHowLongAgo(document.created_at)
|
||||||
)}</p>
|
)}</p>
|
||||||
<GenericButton onClick={() => router.push(`/details/${document.id}`)} size="small" label="Read" icon="pi pi-chevron-right" iconPos="right" outlined className="items-center py-2" />
|
<GenericButton onClick={() => router.push(`/details/${nAddress}`)} size="small" label="Read" icon="pi pi-chevron-right" iconPos="right" outlined className="items-center py-2" />
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
)
|
||||||
|
@ -7,6 +7,7 @@ import { useZapsSubscription } from "@/hooks/nostrQueries/zaps/useZapsSubscripti
|
|||||||
import { getTotalFromZaps } from "@/utils/lightning";
|
import { getTotalFromZaps } from "@/utils/lightning";
|
||||||
import { useImageProxy } from "@/hooks/useImageProxy";
|
import { useImageProxy } from "@/hooks/useImageProxy";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import { nip19 } from "nostr-tools";
|
||||||
import { formatTimestampToHowLongAgo } from "@/utils/time";
|
import { formatTimestampToHowLongAgo } from "@/utils/time";
|
||||||
import { Tag } from "primereact/tag";
|
import { Tag } from "primereact/tag";
|
||||||
import GenericButton from "@/components/buttons/GenericButton";
|
import GenericButton from "@/components/buttons/GenericButton";
|
||||||
@ -14,9 +15,19 @@ import GenericButton from "@/components/buttons/GenericButton";
|
|||||||
export function VideoTemplate({ video }) {
|
export function VideoTemplate({ video }) {
|
||||||
const { zaps, zapsLoading, zapsError } = useZapsSubscription({ event: video });
|
const { zaps, zapsLoading, zapsError } = useZapsSubscription({ event: video });
|
||||||
const [zapAmount, setZapAmount] = useState(0);
|
const [zapAmount, setZapAmount] = useState(0);
|
||||||
|
const [nAddress, setNAddress] = useState(null);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { returnImageProxy } = useImageProxy();
|
const { returnImageProxy } = useImageProxy();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const addr = nip19.naddrEncode({
|
||||||
|
pubkey: video.pubkey,
|
||||||
|
kind: video.kind,
|
||||||
|
identifier: video.id
|
||||||
|
})
|
||||||
|
setNAddress(addr);
|
||||||
|
}, [video]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (zaps.length > 0) {
|
if (zaps.length > 0) {
|
||||||
const total = getTotalFromZaps(zaps, video);
|
const total = getTotalFromZaps(zaps, video);
|
||||||
@ -73,7 +84,7 @@ export function VideoTemplate({ video }) {
|
|||||||
) : (
|
) : (
|
||||||
formatTimestampToHowLongAgo(video.created_at)
|
formatTimestampToHowLongAgo(video.created_at)
|
||||||
)}</p>
|
)}</p>
|
||||||
<GenericButton onClick={() => router.push(`/details/${video.id}`)} size="small" label="Watch" icon="pi pi-chevron-right" iconPos="right" outlined className="items-center py-2" />
|
<GenericButton onClick={() => router.push(`/details/${nAddress}`)} size="small" label="Watch" icon="pi pi-chevron-right" iconPos="right" outlined className="items-center py-2" />
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
)
|
||||||
|
@ -164,9 +164,7 @@ export default function CourseDetailsNew({ processedEvent, paidCourse, lessons,
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{nAddress && (
|
{nAddress && (
|
||||||
<div className='w-full flex flex-row justify-end'>
|
<GenericButton outlined icon="pi pi-external-link" onClick={() => window.open(`https://nostr.band/${nAddress}`, '_blank')} tooltip="View Nostr Event" tooltipOptions={{ position: paidCourse ? 'left' : 'right' }} />
|
||||||
<GenericButton outlined icon="pi pi-external-link" onClick={() => window.open(`https://nostr.band/${nAddress}`, '_blank')} tooltip="View Nostr Event" tooltipOptions={{ position: 'left' }} />
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -86,7 +86,7 @@ const VideoLesson = ({ lesson, course, decryptionPerformed, isPaid }) => {
|
|||||||
<Divider />
|
<Divider />
|
||||||
<div className="bg-gray-800/90 rounded-lg p-4 m-4">
|
<div className="bg-gray-800/90 rounded-lg p-4 m-4">
|
||||||
<div className="w-full flex flex-col items-start justify-start mt-2 px-2">
|
<div className="w-full flex flex-col items-start justify-start mt-2 px-2">
|
||||||
<div className="flex flex-row items-center justify-between w-full">
|
<div className="flex flex-row items-center gap-2 w-full">
|
||||||
<h1 className='text-3xl text-white'>{lesson.title}</h1>
|
<h1 className='text-3xl text-white'>{lesson.title}</h1>
|
||||||
{lesson.topics && lesson.topics.length > 0 && (
|
{lesson.topics && lesson.topics.length > 0 && (
|
||||||
lesson.topics.map((topic, index) => (
|
lesson.topics.map((topic, index) => (
|
||||||
|
@ -23,7 +23,7 @@ const MDDisplay = dynamic(
|
|||||||
|
|
||||||
const lnAddress = process.env.NEXT_PUBLIC_LIGHTNING_ADDRESS;
|
const lnAddress = process.env.NEXT_PUBLIC_LIGHTNING_ADDRESS;
|
||||||
|
|
||||||
const DocumentDetails = ({ processedEvent, topics, title, summary, image, price, author, paidResource, decryptedContent, handlePaymentSuccess, handlePaymentError, authorView }) => {
|
const DocumentDetails = ({ processedEvent, topics, title, summary, image, price, author, paidResource, decryptedContent, nAddress, handlePaymentSuccess, handlePaymentError, authorView }) => {
|
||||||
const [zapAmount, setZapAmount] = useState(0);
|
const [zapAmount, setZapAmount] = useState(0);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { returnImageProxy } = useImageProxy();
|
const { returnImageProxy } = useImageProxy();
|
||||||
@ -151,6 +151,17 @@ const DocumentDetails = ({ processedEvent, topics, title, summary, image, price,
|
|||||||
zapsLoading={zapsLoading && zapAmount === 0}
|
zapsLoading={zapsLoading && zapAmount === 0}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="w-full flex flex-row justify-end">
|
||||||
|
<GenericButton
|
||||||
|
tooltip={`View Nostr Note`}
|
||||||
|
tooltipOptions={{ position: 'left' }}
|
||||||
|
icon="pi pi-external-link"
|
||||||
|
outlined
|
||||||
|
onClick={() => {
|
||||||
|
window.open(`https://nostr.com/${nAddress}`, '_blank');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{renderContent()}
|
{renderContent()}
|
||||||
|
@ -23,7 +23,7 @@ const MDDisplay = dynamic(
|
|||||||
|
|
||||||
const lnAddress = process.env.NEXT_PUBLIC_LIGHTNING_ADDRESS;
|
const lnAddress = process.env.NEXT_PUBLIC_LIGHTNING_ADDRESS;
|
||||||
|
|
||||||
const VideoDetails = ({ processedEvent, topics, title, summary, image, price, author, paidResource, decryptedContent, handlePaymentSuccess, handlePaymentError, authorView }) => {
|
const VideoDetails = ({ processedEvent, topics, title, summary, image, price, author, paidResource, decryptedContent, nAddress, handlePaymentSuccess, handlePaymentError, authorView }) => {
|
||||||
const [zapAmount, setZapAmount] = useState(0);
|
const [zapAmount, setZapAmount] = useState(0);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { returnImageProxy } = useImageProxy();
|
const { returnImageProxy } = useImageProxy();
|
||||||
@ -119,7 +119,7 @@ const VideoDetails = ({ processedEvent, topics, title, summary, image, price, au
|
|||||||
{renderContent()}
|
{renderContent()}
|
||||||
<div className="bg-gray-800/90 rounded-lg p-4 m-4">
|
<div className="bg-gray-800/90 rounded-lg p-4 m-4">
|
||||||
<div className="w-full flex flex-col items-start justify-start mt-2 px-2">
|
<div className="w-full flex flex-col items-start justify-start mt-2 px-2">
|
||||||
<div className="flex flex-row items-center justify-between w-full">
|
<div className="flex flex-row items-center gap-2 w-full">
|
||||||
<h1 className='text-4xl'>{title}</h1>
|
<h1 className='text-4xl'>{title}</h1>
|
||||||
{topics && topics.length > 0 && (
|
{topics && topics.length > 0 && (
|
||||||
topics.map((topic, index) => (
|
topics.map((topic, index) => (
|
||||||
@ -154,6 +154,15 @@ const VideoDetails = ({ processedEvent, topics, title, summary, image, price, au
|
|||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<GenericButton
|
||||||
|
tooltip={`View Nostr Note`}
|
||||||
|
tooltipOptions={{ position: 'left' }}
|
||||||
|
icon="pi pi-external-link"
|
||||||
|
outlined
|
||||||
|
onClick={() => {
|
||||||
|
window.open(`https://nostr.com/${nAddress}`, '_blank');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
{authorView && (
|
{authorView && (
|
||||||
<div className='flex flex-row justify-center items-center space-x-2'>
|
<div className='flex flex-row justify-center items-center space-x-2'>
|
||||||
<GenericButton onClick={() => router.push(`/details/${processedEvent.id}/edit`)} label="Edit" severity='warning' outlined />
|
<GenericButton onClick={() => router.push(`/details/${processedEvent.id}/edit`)} label="Edit" severity='warning' outlined />
|
||||||
|
@ -40,7 +40,7 @@ const Sidebar = ({ course = false }) => {
|
|||||||
if (router.isReady) {
|
if (router.isReady) {
|
||||||
const { slug } = router.query;
|
const { slug } = router.query;
|
||||||
|
|
||||||
if (slug) {
|
if (slug && course) {
|
||||||
const { data } = nip19.decode(slug)
|
const { data } = nip19.decode(slug)
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
|
@ -68,14 +68,27 @@ export default function Details() {
|
|||||||
if (router.isReady) {
|
if (router.isReady) {
|
||||||
const { slug } = router.query;
|
const { slug } = router.query;
|
||||||
|
|
||||||
const fetchEvent = async (slug, retryCount = 0) => {
|
if (!slug) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = nip19.decode(slug)
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
showToast('error', 'Error', 'Resource not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = data?.identifier;
|
||||||
|
|
||||||
|
const fetchEvent = async (id, retryCount = 0) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
try {
|
try {
|
||||||
await ndk.connect();
|
await ndk.connect();
|
||||||
|
|
||||||
const filter = {
|
const filter = {
|
||||||
ids: [slug]
|
ids: [id]
|
||||||
}
|
}
|
||||||
|
|
||||||
const event = await ndk.fetchEvent(filter);
|
const event = await ndk.fetchEvent(filter);
|
||||||
@ -93,7 +106,7 @@ export default function Details() {
|
|||||||
if (retryCount < 1) {
|
if (retryCount < 1) {
|
||||||
// Wait for 2 seconds before retrying
|
// Wait for 2 seconds before retrying
|
||||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||||
return fetchEvent(slug, retryCount + 1);
|
return fetchEvent(id, retryCount + 1);
|
||||||
} else {
|
} else {
|
||||||
setError("Event not found");
|
setError("Event not found");
|
||||||
}
|
}
|
||||||
@ -103,7 +116,7 @@ export default function Details() {
|
|||||||
if (retryCount < 1) {
|
if (retryCount < 1) {
|
||||||
// Wait for 2 seconds before retrying
|
// Wait for 2 seconds before retrying
|
||||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||||
return fetchEvent(slug, retryCount + 1);
|
return fetchEvent(id, retryCount + 1);
|
||||||
} else {
|
} else {
|
||||||
setError("Failed to fetch event. Please try again.");
|
setError("Failed to fetch event. Please try again.");
|
||||||
}
|
}
|
||||||
@ -112,8 +125,8 @@ export default function Details() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (ndk) {
|
if (ndk && id) {
|
||||||
fetchEvent(slug);
|
fetchEvent(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [router.isReady, router.query, ndk, user]);
|
}, [router.isReady, router.query, ndk, user]);
|
||||||
@ -200,6 +213,7 @@ export default function Details() {
|
|||||||
price={processedEvent.price}
|
price={processedEvent.price}
|
||||||
author={author}
|
author={author}
|
||||||
paidResource={paidResource}
|
paidResource={paidResource}
|
||||||
|
nAddress={nAddress}
|
||||||
decryptedContent={decryptedContent}
|
decryptedContent={decryptedContent}
|
||||||
handlePaymentSuccess={handlePaymentSuccess}
|
handlePaymentSuccess={handlePaymentSuccess}
|
||||||
handlePaymentError={handlePaymentError}
|
handlePaymentError={handlePaymentError}
|
||||||
@ -216,13 +230,14 @@ export default function Details() {
|
|||||||
author={author}
|
author={author}
|
||||||
paidResource={paidResource}
|
paidResource={paidResource}
|
||||||
decryptedContent={decryptedContent}
|
decryptedContent={decryptedContent}
|
||||||
|
nAddress={nAddress}
|
||||||
handlePaymentSuccess={handlePaymentSuccess}
|
handlePaymentSuccess={handlePaymentSuccess}
|
||||||
handlePaymentError={handlePaymentError}
|
handlePaymentError={handlePaymentError}
|
||||||
authorView={authorView}
|
authorView={authorView}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{typeof window !== 'undefined' && nAddress !== null && (
|
{typeof window !== 'undefined' && nAddress !== null && (
|
||||||
<div className='max-tab:px-4'>
|
<div className='px-4'>
|
||||||
<ZapThreadsWrapper
|
<ZapThreadsWrapper
|
||||||
anchor={nAddress}
|
anchor={nAddress}
|
||||||
user={user?.pubkey || null}
|
user={user?.pubkey || null}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user