From 79a05599954caccfefbf9784ecaabcb33d8c49d5 Mon Sep 17 00:00:00 2001 From: austinkelsay Date: Tue, 18 Mar 2025 13:05:27 -0500 Subject: [PATCH] Updated carousels layout with menutab of tags on home page --- src/components/banner/HeroBanner.js | 37 +-- .../content/carousels/CoursesCarousel.js | 8 +- .../content/carousels/DocumentsCarousel.js | 8 +- .../content/carousels/VideosCarousel.js | 8 +- src/config/appConfig.js | 2 +- src/hooks/nostr/useCourses.js | 1 + src/hooks/nostr/useDocuments.js | 1 + src/hooks/nostr/useVideos.js | 1 + src/pages/_app.js | 2 +- src/pages/index.js | 227 ++++++++++++++++-- 10 files changed, 239 insertions(+), 56 deletions(-) diff --git a/src/components/banner/HeroBanner.js b/src/components/banner/HeroBanner.js index 694ae3a..ac25a14 100644 --- a/src/components/banner/HeroBanner.js +++ b/src/components/banner/HeroBanner.js @@ -2,7 +2,6 @@ import React, { useEffect, useState, useRef } from 'react'; import useWindowWidth from '@/hooks/useWindowWidth'; import Image from 'next/image'; import { getSession, signIn, useSession } from 'next-auth/react'; -import { useImageProxy } from '@/hooks/useImageProxy'; import { useRouter } from 'next/router'; import { Avatar } from 'primereact/avatar'; import { AvatarGroup } from 'primereact/avatargroup'; @@ -15,7 +14,6 @@ const HeroBanner = () => { const techs = ['Bitcoin', 'Lightning', 'Nostr']; const windowWidth = useWindowWidth(); const router = useRouter(); - const { returnImageProxy } = useImageProxy(); const { data: session } = useSession(); const isTabView = windowWidth <= 1360; @@ -48,10 +46,10 @@ const HeroBanner = () => { }; const getHeroHeight = () => { - if (isSuperWideScreen) return 'h-[900px]'; - if (isWideScreen) return 'h-[700px]'; - if (isMobile) return 'h-[450px]'; - return 'h-[600px]'; + if (isSuperWideScreen) return 'h-[700px]'; + if (isWideScreen) return 'h-[550px]'; + if (isMobile) return 'h-[400px]'; + return 'h-[500px]'; }; const handleLearnToCode = async () => { @@ -105,7 +103,7 @@ const HeroBanner = () => { }; return ( -
+
Banner {
{!isTabView && ( -
+
)} -
-

+
+

Learn to code Build on{' '} @@ -144,16 +142,16 @@ const HeroBanner = () => { Become a dev

{isMobile ? ( -

+

A one of a kind developer education, content, and community platform built on Nostr and fully Lightning integrated.

) : ( -

+

A one of a kind developer education, content, and community platform built on Nostr and fully Lightning integrated.

)}
!isMobile && window.open('https://www.udemy.com/user/austin-james-kelsay/', '_blank')} style={{ cursor: isMobile ? 'default' : 'pointer' }} > @@ -180,21 +178,10 @@ const HeroBanner = () => { label="Learn To Code" icon={} rounded - severity="info" - className="border-2" - size={isMobile ? null : "large"} - outlined - onClick={handleLearnToCode} - /> - } - rounded - size={isMobile ? null : "large"} severity="success" className="border-2" outlined - onClick={() => router.push('/content?tag=all')} + onClick={handleLearnToCode} />
diff --git a/src/components/content/carousels/CoursesCarousel.js b/src/components/content/carousels/CoursesCarousel.js index 093003d..542c553 100644 --- a/src/components/content/carousels/CoursesCarousel.js +++ b/src/components/content/carousels/CoursesCarousel.js @@ -56,18 +56,18 @@ export default function CoursesCarousel() { return ( <> -

Courses

- +

Courses

+
diff --git a/src/components/content/carousels/DocumentsCarousel.js b/src/components/content/carousels/DocumentsCarousel.js index deac41d..43caf2d 100644 --- a/src/components/content/carousels/DocumentsCarousel.js +++ b/src/components/content/carousels/DocumentsCarousel.js @@ -82,17 +82,17 @@ export default function DocumentsCarousel() { return ( <> -

Documents

- +

Documents

+ diff --git a/src/components/content/carousels/VideosCarousel.js b/src/components/content/carousels/VideosCarousel.js index f3efc9e..4164066 100644 --- a/src/components/content/carousels/VideosCarousel.js +++ b/src/components/content/carousels/VideosCarousel.js @@ -83,17 +83,17 @@ export default function VideosCarousel() { return ( <> -

Videos

- +

Videos

+ diff --git a/src/config/appConfig.js b/src/config/appConfig.js index 784cb30..98cefb8 100644 --- a/src/config/appConfig.js +++ b/src/config/appConfig.js @@ -9,7 +9,7 @@ const appConfig = { "wss://purplerelay.com/", "wss://relay.devs.tools/" ], - authorPubkeys: ["f33c8a9617cb15f705fc70cd461cfd6eaf22f9e24c33eabad981648e5ec6f741", "c67cd3e1a83daa56cff16f635db2fdb9ed9619300298d4701a58e68e84098345"], + authorPubkeys: ["f33c8a9617cb15f705fc70cd461cfd6eaf22f9e24c33eabad981648e5ec6f741", "c67cd3e1a83daa56cff16f635db2fdb9ed9619300298d4701a58e68e84098345", "6260f29fa75c91aaa292f082e5e87b438d2ab4fdf96af398567b01802ee2fcd4"], customLightningAddresses: [ { // todo remove need for lowercase diff --git a/src/hooks/nostr/useCourses.js b/src/hooks/nostr/useCourses.js index 90c1cad..94a4d96 100644 --- a/src/hooks/nostr/useCourses.js +++ b/src/hooks/nostr/useCourses.js @@ -11,6 +11,7 @@ export function useCourses() { const [coursesError, setCoursesError] = useState(null); const { contentIds } = useContentIdsQuery() + // const contentIds = ["f6825391-831c-44da-904a-9ac3d149b7be","f73c37f4-df2e-4f7d-a838-dce568c76136","f538f5c5-1a72-4804-8eb1-3f05cea64874","e58a42c0-c7c6-4b0e-8206-ace7df59a2b8","751ba534-e13a-4ed6-8f8b-452bf482f944","711924b4-3efe-4603-8a23-0ed42c88a63c","42f19d87-3a40-46bb-96b9-e7d31ded89bb","d084d45e-05fa-48fc-afa7-b54499f9e21e","ef588bcb-79a3-4ce9-a370-28c1dc7b604e","5bb34e83-599b-4494-9790-db2ac087baed","67d24075-7cda-4d38-93ad-cd0ef32f13f1","91c15fc0-bb11-43b4-83e6-4b8fd3a826ac","e25f3d3b-f28b-4edd-a325-380564e6db7d","558f88a6-fd1e-4482-9ae7-0f52d7fb82f0","181ed1a5-1f6b-4f68-bb5f-3c2f993f3c72","a0a7d5d2-d416-4bff-9a7f-836b51891fed","38180242-dd5c-4bae-ace4-d46b854d5b7d","5aeb5c26-0d6e-48d7-9436-11ff490a2adc","8b08d474-2ee9-4468-9976-19284c562e13","7d1c7b9b-0ad4-4199-a462-eeb936ea30c9","d607625a-81e1-4098-ba17-068ed29b05ed","e0b93cb8-ba23-4a6b-b7c9-efda9c886b7c","26fb6c60-3655-4193-998d-6e920caead68","4372a797-3565-44ab-a801-042ed5087873","18fd6bfd-fec1-4496-9871-2630b4f3d3b0","f3a61a3e-7523-43ff-9df3-7581c1436c36","b4deaa04-0c78-4526-80ec-362944fe35b6","dbc8672a-d09e-413b-a076-7b86bcc75ca6","5bba9ee9-a37b-496d-b524-e70ebd7140cc","c14bbbde-63b1-4341-a848-f67e07ee8618","e3d7ccfc-0374-46b9-98ff-5526247e13e5","164a7d28-3677-4f68-9fbb-ce2ff5cc4684","89da8522-f8dd-44e2-a49d-7be58328c9ff","03586c4c-5c4a-4bd6-b5e9-bfad02bb22c5","2c79dca4-334b-4805-9e97-fb1a27351580","93f52ce2-ebeb-445b-b698-757815ddb450","ee31bfb2-a2cd-4a31-87e1-da4f3e3c70fa","8cb1d556-df1b-4fef-a625-69c1fe1c397c","1f889e07-0fe2-4944-976d-7e602d3c1fb7","2553ed1a-d8ab-4fa5-a160-4a668a672a5e","80aac9d4-8bef-4a92-9ee9-dea1c2d66c3a","6fe3cb4b-2571-4e3b-9159-db78325ee5cc","a3083ab5-0187-4b77-83d1-29ae1f644559","e5399c72-9b95-46d6-a594-498e673b6c58","f93827ed-68ad-4b5e-af33-f7424b37f0d6","16a65e26-e5d9-450f-9b98-79d539b8acb0","8ce74cf3-bc67-4e95-83b6-a01983a1b586","6d8260b3-c902-46ec-8aed-f3b8c8f1229b"] const {ndk, addSigner} = useNDKContext(); useEffect(() => { diff --git a/src/hooks/nostr/useDocuments.js b/src/hooks/nostr/useDocuments.js index 09652d7..8457c04 100644 --- a/src/hooks/nostr/useDocuments.js +++ b/src/hooks/nostr/useDocuments.js @@ -11,6 +11,7 @@ export function useDocuments() { const [documentsError, setDocumentsError] = useState(null); const { contentIds } = useContentIdsQuery() + // const contentIds = ["f6825391-831c-44da-904a-9ac3d149b7be","f73c37f4-df2e-4f7d-a838-dce568c76136","f538f5c5-1a72-4804-8eb1-3f05cea64874","e58a42c0-c7c6-4b0e-8206-ace7df59a2b8","751ba534-e13a-4ed6-8f8b-452bf482f944","711924b4-3efe-4603-8a23-0ed42c88a63c","42f19d87-3a40-46bb-96b9-e7d31ded89bb","d084d45e-05fa-48fc-afa7-b54499f9e21e","ef588bcb-79a3-4ce9-a370-28c1dc7b604e","5bb34e83-599b-4494-9790-db2ac087baed","67d24075-7cda-4d38-93ad-cd0ef32f13f1","91c15fc0-bb11-43b4-83e6-4b8fd3a826ac","e25f3d3b-f28b-4edd-a325-380564e6db7d","558f88a6-fd1e-4482-9ae7-0f52d7fb82f0","181ed1a5-1f6b-4f68-bb5f-3c2f993f3c72","a0a7d5d2-d416-4bff-9a7f-836b51891fed","38180242-dd5c-4bae-ace4-d46b854d5b7d","5aeb5c26-0d6e-48d7-9436-11ff490a2adc","8b08d474-2ee9-4468-9976-19284c562e13","7d1c7b9b-0ad4-4199-a462-eeb936ea30c9","d607625a-81e1-4098-ba17-068ed29b05ed","e0b93cb8-ba23-4a6b-b7c9-efda9c886b7c","26fb6c60-3655-4193-998d-6e920caead68","4372a797-3565-44ab-a801-042ed5087873","18fd6bfd-fec1-4496-9871-2630b4f3d3b0","f3a61a3e-7523-43ff-9df3-7581c1436c36","b4deaa04-0c78-4526-80ec-362944fe35b6","dbc8672a-d09e-413b-a076-7b86bcc75ca6","5bba9ee9-a37b-496d-b524-e70ebd7140cc","c14bbbde-63b1-4341-a848-f67e07ee8618","e3d7ccfc-0374-46b9-98ff-5526247e13e5","164a7d28-3677-4f68-9fbb-ce2ff5cc4684","89da8522-f8dd-44e2-a49d-7be58328c9ff","03586c4c-5c4a-4bd6-b5e9-bfad02bb22c5","2c79dca4-334b-4805-9e97-fb1a27351580","93f52ce2-ebeb-445b-b698-757815ddb450","ee31bfb2-a2cd-4a31-87e1-da4f3e3c70fa","8cb1d556-df1b-4fef-a625-69c1fe1c397c","1f889e07-0fe2-4944-976d-7e602d3c1fb7","2553ed1a-d8ab-4fa5-a160-4a668a672a5e","80aac9d4-8bef-4a92-9ee9-dea1c2d66c3a","6fe3cb4b-2571-4e3b-9159-db78325ee5cc","a3083ab5-0187-4b77-83d1-29ae1f644559","e5399c72-9b95-46d6-a594-498e673b6c58","f93827ed-68ad-4b5e-af33-f7424b37f0d6","16a65e26-e5d9-450f-9b98-79d539b8acb0","8ce74cf3-bc67-4e95-83b6-a01983a1b586","6d8260b3-c902-46ec-8aed-f3b8c8f1229b"] const {ndk, addSigner} = useNDKContext(); useEffect(() => { diff --git a/src/hooks/nostr/useVideos.js b/src/hooks/nostr/useVideos.js index 5b13762..3da6735 100644 --- a/src/hooks/nostr/useVideos.js +++ b/src/hooks/nostr/useVideos.js @@ -11,6 +11,7 @@ export function useVideos() { const [videosError, setVideosError] = useState(null); const { contentIds } = useContentIdsQuery() + // const contentIds = ["f6825391-831c-44da-904a-9ac3d149b7be","f73c37f4-df2e-4f7d-a838-dce568c76136","f538f5c5-1a72-4804-8eb1-3f05cea64874","e58a42c0-c7c6-4b0e-8206-ace7df59a2b8","751ba534-e13a-4ed6-8f8b-452bf482f944","711924b4-3efe-4603-8a23-0ed42c88a63c","42f19d87-3a40-46bb-96b9-e7d31ded89bb","d084d45e-05fa-48fc-afa7-b54499f9e21e","ef588bcb-79a3-4ce9-a370-28c1dc7b604e","5bb34e83-599b-4494-9790-db2ac087baed","67d24075-7cda-4d38-93ad-cd0ef32f13f1","91c15fc0-bb11-43b4-83e6-4b8fd3a826ac","e25f3d3b-f28b-4edd-a325-380564e6db7d","558f88a6-fd1e-4482-9ae7-0f52d7fb82f0","181ed1a5-1f6b-4f68-bb5f-3c2f993f3c72","a0a7d5d2-d416-4bff-9a7f-836b51891fed","38180242-dd5c-4bae-ace4-d46b854d5b7d","5aeb5c26-0d6e-48d7-9436-11ff490a2adc","8b08d474-2ee9-4468-9976-19284c562e13","7d1c7b9b-0ad4-4199-a462-eeb936ea30c9","d607625a-81e1-4098-ba17-068ed29b05ed","e0b93cb8-ba23-4a6b-b7c9-efda9c886b7c","26fb6c60-3655-4193-998d-6e920caead68","4372a797-3565-44ab-a801-042ed5087873","18fd6bfd-fec1-4496-9871-2630b4f3d3b0","f3a61a3e-7523-43ff-9df3-7581c1436c36","b4deaa04-0c78-4526-80ec-362944fe35b6","dbc8672a-d09e-413b-a076-7b86bcc75ca6","5bba9ee9-a37b-496d-b524-e70ebd7140cc","c14bbbde-63b1-4341-a848-f67e07ee8618","e3d7ccfc-0374-46b9-98ff-5526247e13e5","164a7d28-3677-4f68-9fbb-ce2ff5cc4684","89da8522-f8dd-44e2-a49d-7be58328c9ff","03586c4c-5c4a-4bd6-b5e9-bfad02bb22c5","2c79dca4-334b-4805-9e97-fb1a27351580","93f52ce2-ebeb-445b-b698-757815ddb450","ee31bfb2-a2cd-4a31-87e1-da4f3e3c70fa","8cb1d556-df1b-4fef-a625-69c1fe1c397c","1f889e07-0fe2-4944-976d-7e602d3c1fb7","2553ed1a-d8ab-4fa5-a160-4a668a672a5e","80aac9d4-8bef-4a92-9ee9-dea1c2d66c3a","6fe3cb4b-2571-4e3b-9159-db78325ee5cc","a3083ab5-0187-4b77-83d1-29ae1f644559","e5399c72-9b95-46d6-a594-498e673b6c58","f93827ed-68ad-4b5e-af33-f7424b37f0d6","16a65e26-e5d9-450f-9b98-79d539b8acb0","8ce74cf3-bc67-4e95-83b6-a01983a1b586","6d8260b3-c902-46ec-8aed-f3b8c8f1229b"] const {ndk, addSigner} = useNDKContext(); useEffect(() => { diff --git a/src/pages/_app.js b/src/pages/_app.js index 1c7dcb9..71cfd61 100644 --- a/src/pages/_app.js +++ b/src/pages/_app.js @@ -36,7 +36,7 @@ export default function MyApp({
-
+
diff --git a/src/pages/index.js b/src/pages/index.js index 573f1e2..60ca6c7 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -1,25 +1,218 @@ import Head from 'next/head'; -import React from 'react'; +import React, { useEffect, useState, useMemo } from 'react'; +// import GenericCarousel from '@/components/content/carousels/GenericCarousel'; import CoursesCarousel from '@/components/content/carousels/CoursesCarousel'; import VideosCarousel from '@/components/content/carousels/VideosCarousel'; import DocumentsCarousel from '@/components/content/carousels/DocumentsCarousel'; +import { parseEvent, parseCourseEvent } from '@/utils/nostr'; +import { useDocuments } from '@/hooks/nostr/useDocuments'; +import { useVideos } from '@/hooks/nostr/useVideos'; +import { useCourses } from '@/hooks/nostr/useCourses'; +import { TabMenu } from 'primereact/tabmenu'; +import 'primeicons/primeicons.css'; +import GenericButton from '@/components/buttons/GenericButton'; +import { useRouter } from 'next/router'; import HeroBanner from '@/components/banner/HeroBanner'; +const TOP_CONTENT_IDS = [ + "72aa9f48-a0d1-4e0e-ba6b-de3e8ef8e9ad", + "d493fc22-20ab-47fc-ab55-b017b92bb0fe", + "936a024a-719c-455c-8c9d-2657ebd8a5f0", + "dfa1e4ed-1387-418d-a166-84dbdb9701df", + "f806c5c6-7b88-4be6-911e-0f7e08a0dac2", + "ad217a77-7422-45eb-979e-fe8cefd89a1f", + "3dcc434a-7b65-4cf4-883a-db117fe97b77", + "e0495afa-d879-47d8-a8be-843785e47611", + "8670318e-91d0-443f-9ceb-8324cd1bdabc", + "5d67444d-0b91-423b-b927-3def8535adc8" +]; + +const MenuTab = ({ selectedTopic, onTabChange, allTopics }) => { + const router = useRouter(); + + // Define the hardcoded priority items that always appear first + const priorityItems = ['Top', 'Courses', 'Videos', 'Documents', 'Free', 'Paid']; + // Items that should be filtered out from topics + const blacklistedItems = ['document', 'video', 'course']; + + // Get dynamic topics, excluding hardcoded and blacklisted items + const otherItems = allTopics.filter(item => + !priorityItems.includes(item) && + !blacklistedItems.includes(item) + ); + + // Only take the first 4 dynamic topics to keep the menu clean + // Additional topics will be accessible through the "More" page + const limitedOtherItems = otherItems.slice(0, 8); + + // Combine all menu items: priority items + up to 4 dynamic topics + More + const allItems = [...priorityItems, ...limitedOtherItems, 'More']; + + const menuItems = allItems.map((item) => { + let icon = 'pi pi-tag'; + if (item === 'Top') icon = 'pi pi-star'; + else if (item === 'Documents') icon = 'pi pi-file'; + else if (item === 'Videos') icon = 'pi pi-video'; + else if (item === 'Courses') icon = 'pi pi-desktop'; + else if (item === 'Free') icon = 'pi pi-lock-open'; + else if (item === 'Paid') icon = 'pi pi-lock'; + else if (item === 'More') icon = 'pi pi-ellipsis-h'; + + const isMore = item === 'More'; + const path = isMore ? '/content?tag=all' : item === 'Top' ? '/' : `/content?tag=${item.toLowerCase()}`; + + return { + label: ( + { + onTabChange(item); + router.push(path); + }} + outlined={selectedTopic !== item} + rounded + size='small' + label={item} + icon={icon} + /> + ), + command: () => { + onTabChange(item); + router.push(path); + } + }; + }); + + return ( +
+ onTabChange(allItems[e.index])} + pt={{ + menu: { className: 'bg-transparent border-none my-2 mb-4' }, + action: ({ context, parent }) => ({ + className: 'cursor-pointer select-none flex items-center relative no-underline overflow-hidden border-b-2 p-2 font-bold rounded-t-lg', + style: { top: '2px' } + }), + menuitem: { className: 'mr-0' } + }} + /> +
+ ); +} + export default function Home() { - return ( - <> - - PlebDevs - - - - -
- - - - -
- - ); + const router = useRouter(); + const { documents, documentsLoading } = useDocuments(); + const { videos, videosLoading } = useVideos(); + const { courses, coursesLoading } = useCourses(); + + const [processedDocuments, setProcessedDocuments] = useState([]); + const [processedVideos, setProcessedVideos] = useState([]); + const [processedCourses, setProcessedCourses] = useState([]); + const [allContent, setAllContent] = useState([]); + const [allTopics, setAllTopics] = useState([]); + const [selectedTopic, setSelectedTopic] = useState('Top'); + const [filteredContent, setFilteredContent] = useState([]); + + const memoizedFilteredContent = useMemo(() => filteredContent, [filteredContent]); + + useEffect(() => { + if (documents && !documentsLoading) { + const processedDocuments = documents.map(document => ({...parseEvent(document), type: 'document'})); + setProcessedDocuments(processedDocuments); + } + }, [documents, documentsLoading]); + + useEffect(() => { + if (videos && !videosLoading) { + const processedVideos = videos.map(video => ({...parseEvent(video), type: 'video'})); + setProcessedVideos(processedVideos); + } + }, [videos, videosLoading]); + + useEffect(() => { + if (courses && !coursesLoading) { + const processedCourses = courses.map(course => ({...parseCourseEvent(course), type: 'course'})); + setProcessedCourses(processedCourses); + } + }, [courses, coursesLoading]); + + useEffect(() => { + const allContent = [...processedDocuments, ...processedVideos, ...processedCourses]; + setAllContent(allContent); + + const uniqueTopics = new Set(allContent.map(item => item.topics).flat()); + const otherTopics = Array.from(uniqueTopics).filter(topic => + !['Top', 'Courses', 'Videos', 'Documents', 'Free', 'Paid', 'More'].includes(topic) && + !['document', 'video', 'course'].includes(topic) + ); + setAllTopics(otherTopics); + + if (selectedTopic === 'Top') { + const topContent = allContent.filter(item => TOP_CONTENT_IDS.includes(item.d)); + setFilteredContent(topContent); + } else { + filterContent(selectedTopic, allContent); + } + }, [processedDocuments, processedVideos, processedCourses, selectedTopic]); + + const filterContent = (topic, content) => { + let filtered = content; + if (topic !== 'Top' && topic !== 'More') { + const topicLower = topic.toLowerCase(); + if (['courses', 'videos', 'documents'].includes(topicLower)) { + filtered = content.filter(item => item.type === topicLower.slice(0, -1)); + } else if (topicLower === 'free') { + filtered = content.filter(item => !item.price || Number(item.price) === 0); + } else if (topicLower === 'paid') { + filtered = content.filter(item => item.price && Number(item.price) > 0); + } else { + filtered = content.filter(item => item.topics && item.topics.includes(topicLower)); + } + } + setFilteredContent(filtered); + }; + + const handleTopicChange = (newTopic) => { + setSelectedTopic(newTopic); + if (newTopic === 'Top') { + const topContent = allContent.filter(item => TOP_CONTENT_IDS.includes(item.d)); + setFilteredContent(topContent); + router.push('/'); + } else if (newTopic === 'More') { + router.push('/content?tag=all'); + } else { + filterContent(newTopic, allContent); + router.push(`/content?tag=${newTopic.toLowerCase()}`); + } + }; + + return ( + <> + + PlebDevs + + + + +
+ +
+ +
+
+ + + +
+
+ + ); } \ No newline at end of file