mirror of
https://github.com/AustinKelsay/plebdevs.git
synced 2025-06-21 15:05:04 +00:00
improvements to details page ui
This commit is contained in:
parent
be6bb02db0
commit
0ab56b009c
@ -1,7 +1,7 @@
|
|||||||
import { useState, useEffect, useRef } from "react";
|
import { useState, useEffect, useRef } from "react";
|
||||||
import { SimplePool, relayInit, nip19 } from "nostr-tools";
|
import { SimplePool, relayInit, nip19 } from "nostr-tools";
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
import { addResource, addCourse } from "@/redux/reducers/eventsReducer";
|
import { addResource, addCourse, addWorkshop, addStream } from "@/redux/reducers/eventsReducer";
|
||||||
import { initialRelays } from "@/redux/reducers/userReducer";
|
import { initialRelays } from "@/redux/reducers/userReducer";
|
||||||
|
|
||||||
export const useNostr = () => {
|
export const useNostr = () => {
|
||||||
@ -88,6 +88,32 @@ export const useNostr = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fetchWorkshops = async () => {
|
||||||
|
const filter = [{kinds: [30023], authors: ["f33c8a9617cb15f705fc70cd461cfd6eaf22f9e24c33eabad981648e5ec6f741"]}];
|
||||||
|
|
||||||
|
const params = {seenOnEnabled: true};
|
||||||
|
|
||||||
|
const hasRequiredTags = (eventData) => {
|
||||||
|
return eventData.some(([tag, value]) => tag === "t" && value === "plebdevs") && eventData.some(([tag, value]) => tag === "t" && value === "workshop");
|
||||||
|
};
|
||||||
|
|
||||||
|
const sub = pool.current.subscribeMany(relays, filter, {
|
||||||
|
...params,
|
||||||
|
onevent: (event) => {
|
||||||
|
if (hasRequiredTags(event.tags)) {
|
||||||
|
dispatch(addWorkshop(event));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onerror: (error) => {
|
||||||
|
console.error("Error fetching workshops:", error);
|
||||||
|
},
|
||||||
|
oneose: () => {
|
||||||
|
console.log("Subscription closed");
|
||||||
|
sub.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const fetchCourses = async () => {
|
const fetchCourses = async () => {
|
||||||
const filter = [{kinds: [30023], authors: ["f33c8a9617cb15f705fc70cd461cfd6eaf22f9e24c33eabad981648e5ec6f741"]}];
|
const filter = [{kinds: [30023], authors: ["f33c8a9617cb15f705fc70cd461cfd6eaf22f9e24c33eabad981648e5ec6f741"]}];
|
||||||
|
|
||||||
@ -114,6 +140,32 @@ export const useNostr = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fetchStreams = async () => {
|
||||||
|
const filter = [{kinds: [30311], authors: ["f33c8a9617cb15f705fc70cd461cfd6eaf22f9e24c33eabad981648e5ec6f741"]}];
|
||||||
|
|
||||||
|
const params = {seenOnEnabled: true};
|
||||||
|
|
||||||
|
const hasRequiredTags = (eventData) => {
|
||||||
|
return eventData.some(([tag, value]) => tag === "t" && value === "plebdevs");
|
||||||
|
};
|
||||||
|
|
||||||
|
const sub = pool.current.subscribeMany(relays, filter, {
|
||||||
|
...params,
|
||||||
|
onevent: (event) => {
|
||||||
|
if (hasRequiredTags(event.tags)) {
|
||||||
|
dispatch(addStream(event));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onerror: (error) => {
|
||||||
|
console.error("Error fetching streams:", error);
|
||||||
|
},
|
||||||
|
oneose: () => {
|
||||||
|
console.log("Subscription closed");
|
||||||
|
sub.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const fetchSingleEvent = async (id) => {
|
const fetchSingleEvent = async (id) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const sub = pool.current.subscribeMany(relays, [{ ids: [id] }], {
|
const sub = pool.current.subscribeMany(relays, [{ ids: [id] }], {
|
||||||
@ -159,6 +211,7 @@ export const useNostr = () => {
|
|||||||
fetchKind0,
|
fetchKind0,
|
||||||
fetchResources,
|
fetchResources,
|
||||||
fetchCourses,
|
fetchCourses,
|
||||||
|
fetchWorkshops,
|
||||||
getRelayStatuses,
|
getRelayStatuses,
|
||||||
};
|
};
|
||||||
};
|
};
|
@ -1,17 +1,20 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useNostr } from '@/hooks/useNostr';
|
import { useNostr } from '@/hooks/useNostr';
|
||||||
import { parseEvent } from '@/utils/nostr';
|
import { parseEvent, findKind0Fields, hexToNpub } from '@/utils/nostr';
|
||||||
import { useImageProxy } from '@/hooks/useImageProxy';
|
import { useImageProxy } from '@/hooks/useImageProxy';
|
||||||
|
import { Button } from 'primereact/button';
|
||||||
|
import { Tag } from 'primereact/tag';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import 'primeicons/primeicons.css';
|
import 'primeicons/primeicons.css';
|
||||||
|
|
||||||
export default function Details() {
|
export default function Details() {
|
||||||
const [event, setEvent] = useState(null);
|
const [event, setEvent] = useState(null);
|
||||||
const [processedEvent, setProcessedEvent] = useState({});
|
const [processedEvent, setProcessedEvent] = useState({});
|
||||||
|
const [author, setAuthor] = useState(null);
|
||||||
|
|
||||||
const { returnImageProxy } = useImageProxy();
|
const { returnImageProxy } = useImageProxy();
|
||||||
const { fetchSingleEvent } = useNostr();
|
const { fetchSingleEvent, fetchKind0 } = useNostr();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
@ -31,31 +34,80 @@ export default function Details() {
|
|||||||
}, [router.isReady, router.query]);
|
}, [router.isReady, router.query]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (event) {
|
if (event) {
|
||||||
const { id, content, title, summary, image, published_at } = parseEvent(event);
|
fetchAuthor(event.pubkey);
|
||||||
setProcessedEvent({ id, content, title, summary, image, published_at });
|
}
|
||||||
|
}, [event]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (event) {
|
||||||
|
const { id, pubkey, content, title, summary, image, published_at } = parseEvent(event);
|
||||||
|
setProcessedEvent({ id, pubkey, content, title, summary, image, published_at });
|
||||||
}
|
}
|
||||||
}, [event]);
|
}, [event]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-row justify-between m-4'>
|
<div className='flex flex-row justify-between m-4'>
|
||||||
<i className='pi pi-arrow-left cursor-pointer hover:opacity-75' onClick={() => router.push('/')} />
|
<i className='pi pi-arrow-left cursor-pointer hover:opacity-75' onClick={() => router.push('/')} />
|
||||||
|
<div className='flex flex-col items-start'>
|
||||||
|
<div className='flex flex-row justify-start w-full'>
|
||||||
|
<Tag className='mr-2' value="Primary"></Tag>
|
||||||
|
<Tag className='mr-2' severity="success" value="Success"></Tag>
|
||||||
|
<Tag className='mr-2' severity="info" value="Info"></Tag>
|
||||||
|
<Tag className='mr-2' severity="warning" value="Warning"></Tag>
|
||||||
|
<Tag className='mr-2' severity="danger" value="Danger"></Tag>
|
||||||
|
</div>
|
||||||
|
<h1 className='text-4xl mt-6'>{processedEvent?.title}</h1>
|
||||||
|
<p className='text-xl mt-6'>{processedEvent?.summary}</p>
|
||||||
|
<div className='flex flex-row w-full mt-6 items-center'>
|
||||||
|
<Image
|
||||||
|
alt="resource thumbnail"
|
||||||
|
src={returnImageProxy(author?.avatar)}
|
||||||
|
width={50}
|
||||||
|
height={50}
|
||||||
|
className="rounded-full mr-4"
|
||||||
|
/>
|
||||||
|
<p className='text-lg'>
|
||||||
|
Created by{' '}
|
||||||
|
<a rel='noreferrer noopener' target='_blank' className='text-blue-500 hover:underline'>
|
||||||
|
{author?.username}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className='flex flex-col'>
|
<div className='flex flex-col'>
|
||||||
{
|
{processedEvent && (
|
||||||
processedEvent && (
|
<div className='flex flex-col items-center justify-between rounded-lg h-72 p-4 bg-gray-700 drop-shadow-md'>
|
||||||
<>
|
<Image
|
||||||
<Image
|
alt="resource thumbnail"
|
||||||
alt="resource thumbnail"
|
src={returnImageProxy(processedEvent.image)}
|
||||||
src={returnImageProxy(processedEvent.image)}
|
width={344}
|
||||||
width={344}
|
height={194}
|
||||||
height={194}
|
className="object-cover object-center rounded-lg"
|
||||||
className="w-full h-full object-cover object-center rounded-lg"
|
/>
|
||||||
/>
|
<Button
|
||||||
<h2>{processedEvent.title}</h2>
|
icon="pi pi-bolt"
|
||||||
<p>{processedEvent.summary}</p>
|
label="100 sats"
|
||||||
</>
|
severity="success"
|
||||||
)
|
outlined
|
||||||
}
|
pt={{
|
||||||
|
button: {
|
||||||
|
icon: ({ context }) => ({
|
||||||
|
className: 'bg-yellow-500'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -23,6 +23,8 @@ export const eventsSlice = createSlice({
|
|||||||
initialState: {
|
initialState: {
|
||||||
resources: [],
|
resources: [],
|
||||||
courses: [],
|
courses: [],
|
||||||
|
workshops: [],
|
||||||
|
streams: [],
|
||||||
},
|
},
|
||||||
reducers: {
|
reducers: {
|
||||||
addResource: (state, action) => {
|
addResource: (state, action) => {
|
||||||
@ -31,6 +33,12 @@ export const eventsSlice = createSlice({
|
|||||||
addCourse: (state, action) => {
|
addCourse: (state, action) => {
|
||||||
addItems(state, action, 'courses');
|
addItems(state, action, 'courses');
|
||||||
},
|
},
|
||||||
|
addWorkshop: (state, action) => {
|
||||||
|
addItems(state, action, 'workshops');
|
||||||
|
},
|
||||||
|
addStream: (state, action) => {
|
||||||
|
addItems(state, action, 'streams');
|
||||||
|
},
|
||||||
setResources: (state, action) => {
|
setResources: (state, action) => {
|
||||||
state.resources = action.payload;
|
state.resources = action.payload;
|
||||||
},
|
},
|
||||||
@ -40,5 +48,5 @@ export const eventsSlice = createSlice({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const { addResource, addCourse, setResources, setCourses } = eventsSlice.actions;
|
export const { addResource, addCourse, setResources, setCourses, addWorkshop, addStream } = eventsSlice.actions;
|
||||||
export default eventsSlice.reducer;
|
export default eventsSlice.reducer;
|
||||||
|
@ -23,3 +23,6 @@
|
|||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.p-button .pi.pi-bolt {
|
||||||
|
color: yellow;
|
||||||
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { nip19 } from "nostr-tools";
|
||||||
|
|
||||||
export const findKind0Fields = async (kind0) => {
|
export const findKind0Fields = async (kind0) => {
|
||||||
let fields = {}
|
let fields = {}
|
||||||
|
|
||||||
@ -31,6 +33,7 @@ export const parseEvent = (event) => {
|
|||||||
// Initialize an object to store the extracted data
|
// Initialize an object to store the extracted data
|
||||||
const eventData = {
|
const eventData = {
|
||||||
id: event.id,
|
id: event.id,
|
||||||
|
pubkey: event.pubkey || '',
|
||||||
content: event.content || '',
|
content: event.content || '',
|
||||||
title: '',
|
title: '',
|
||||||
summary: '',
|
summary: '',
|
||||||
@ -53,9 +56,17 @@ export const parseEvent = (event) => {
|
|||||||
case 'published_at':
|
case 'published_at':
|
||||||
eventData.published_at = tag[1];
|
eventData.published_at = tag[1];
|
||||||
break;
|
break;
|
||||||
// Add cases for any other data you need to extract
|
case 'author':
|
||||||
|
eventData.author = tag[1];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return eventData;
|
return eventData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const hexToNpub = (hex) => {
|
||||||
|
return nip19.npubEncode(hex);
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user