Styling fixes, add video to promotional carousel

This commit is contained in:
austinkelsay 2024-09-30 12:59:19 -05:00
parent 3ce3a2f037
commit 17ca8fcf59
10 changed files with 86 additions and 77 deletions

Binary file not shown.

View File

@ -1,5 +1,5 @@
import Image from "next/image"
import { useState } from "react"
import { useState, useRef, useEffect } from "react"
import { useImageProxy } from "@/hooks/useImageProxy"
import GenericButton from "@/components/buttons/GenericButton"
import { useRouter } from "next/router"
@ -13,68 +13,69 @@ const promotions = [
title: "Developer education & community platform",
description: "PlebDevs is your gateway to mastering Bitcoin, Lightning, and Nostr technologies. Join our community of aspiring developers and start your journey today!",
icon: "pi pi-code",
image: "https://media.istockphoto.com/id/537331500/photo/programming-code-abstract-technology-background-of-software-deve.jpg?s=612x612&w=0&k=20&c=jlYes8ZfnCmD0lLn-vKvzQoKXrWaEcVypHnB5MuO-g8=",
video: "/videos/plebdevs-montage.mp4",
},
{
id: 2,
category: "COURSES",
title: "Structured learning paths for new devs",
description: "Dive into our comprehensive courses covering Bitcoin protocol, Lightning Network, and Nostr. From basics to advanced topics, we've got you covered.",
category: "CONTENT",
title: "Comprehensive Learning Resources",
description: "Access our extensive library of courses, videos, and documents. From structured learning paths to hands-on workshops, we've got everything you need to master Bitcoin, Lightning, and Nostr development.",
icon: "pi pi-book",
image: "https://media.istockphoto.com/id/1224500457/photo/programming-code-abstract-technology-background-of-software-developer-and-computer-script.jpg?s=612x612&w=0&k=20&c=nHMypkMTU1HUUW85Zt0Ff7MDbq17n0eVeXaoM9Knt4Q=",
},
{
id: 3,
category: "VIDEOS",
title: "Hands-on workshops and devleloper video content",
description: "Watch and learn with our interactive video workshops. Get practical experience building real Bitcoin and Lightning applications.",
icon: "pi pi-video",
image: "https://newsroom.siliconslopes.com/content/images/2018/10/code.jpg",
},
{
id: 4,
category: "DOCUMENTS",
title: "In-depth Resources and Documentation",
description: "Access our extensive library of documents, including guides, resources, and best practices for Bitcoin development.",
icon: "pi pi-file",
image: "https://img.freepik.com/free-photo/programming-background-with-person-working-with-codes-computer_23-2150010125.jpg",
},
{
id: 5,
category: "COMMUNITY",
title: "Join Our Community",
title: "Join Our Community of learners, hackers, and plebs",
description: "Connect with other developers, share your projects, and get support from our community of Bitcoin enthusiasts.",
icon: "pi pi-users",
image: "https://pikwizard.com/pw/medium/50238b1cad4ff412fdafc1325efa1c9f.jpg",
},
{
id: 6,
id: 4,
category: "LIGHTNING / NOSTR",
title: "Lightning and Nostr integrated",
title: "Lightning and Nostr integrated platform",
description: "This platform is the first of its kind to integrate Lightning Network and Nostr protocols, allowing users to send and receive payments and interact with the Nostr network.",
icon: "pi pi-bolt",
image: "https://www.financemagnates.com/wp-content/uploads/2016/05/Bicoin-lightning.jpg",
},
]
// todo bigger ore simple CTA to get users into the content
const InteractivePromotionalCarousel = () => {
const [selectedPromotion, setSelectedPromotion] = useState(promotions[0])
const { returnImageProxy } = useImageProxy();
const windowWidth = useWindowWidth();
const isMobileView = windowWidth <= 1360;
const router = useRouter();
const videoRef = useRef(null);
useEffect(() => {
if (videoRef.current && selectedPromotion.video) {
videoRef.current.play();
}
}, [selectedPromotion]);
return (
<div className={`flex ${isMobileView ? 'flex-col' : 'flex-row'} bg-gray-900 text-white m-4 mx-14 rounded-lg ${isMobileView ? 'h-auto' : 'h-[620px]'} ${isMobileView ? 'w-full mx-0 ml-0 mt-0' : null}`}>
<div className={isMobileView ? 'w-full' : 'lg:w-2/3 relative'}>
<Image
src={returnImageProxy(selectedPromotion.image)}
alt={selectedPromotion.title}
width={800}
height={600}
className={`object-cover w-full ${isMobileView ? 'h-[300px]' : 'h-full'} rounded-lg opacity-75`}
/>
{selectedPromotion.video ? (
<video
ref={videoRef}
src={selectedPromotion.video}
className={`object-cover w-full ${isMobileView ? 'h-[300px]' : 'h-full'} rounded-lg`}
loop
muted
playsInline
/>
) : (
<Image
src={returnImageProxy(selectedPromotion.image)}
alt={selectedPromotion.title}
width={800}
height={600}
className={`object-cover w-full ${isMobileView ? 'h-[300px]' : 'h-full'} rounded-lg opacity-75`}
/>
)}
{isMobileView ? (
<div className="p-6 space-y-2">
<div className="uppercase text-sm font-bold text-[#f8f8ff]">{selectedPromotion.category}</div>
@ -93,17 +94,13 @@ const InteractivePromotionalCarousel = () => {
<GenericButton onClick={() => router.push('/content?tag=all')} severity="primary" icon={<i className="pi pi-eye pr-2" />} label="View all content" className="w-fit py-2 font-semibold" size="small" outlined />
</div>
);
case "COURSES":
case "CONTENT":
return (
<GenericButton onClick={() => router.push('/content?tag=courses')} icon={<i className="pi pi-book pr-2 pb-1" />} label="View All Courses" className="w-fit py-2 font-semibold" size="small" outlined />
);
case "VIDEOS":
return (
<GenericButton onClick={() => router.push('/content?tag=videos')} icon={<i className="pi pi-video pr-2" />} label="View All Videos" className="w-fit py-2 font-semibold" size="small" outlined />
);
case "DOCUMENTS":
return (
<GenericButton onClick={() => router.push('/content?tag=documents')} icon={<i className="pi pi-file pr-2 pb-1" />} label="View All Documents" className="w-fit py-2 font-semibold" size="small" outlined />
<>
<GenericButton onClick={() => router.push('/content?tag=courses')} icon={<i className="pi pi-book pr-2 pb-1" />} label="Courses" className="py-2 font-semibold" size="small" outlined />
<GenericButton onClick={() => router.push('/content?tag=videos')} icon={<i className="pi pi-video pr-2" />} label="Videos" className="py-2 font-semibold" size="small" outlined />
<GenericButton onClick={() => router.push('/content?tag=documents')} icon={<i className="pi pi-file pr-2 pb-1" />} label="Documents" className="py-2 font-semibold" size="small" outlined />
</>
);
case "COMMUNITY":
return (
@ -141,17 +138,13 @@ const InteractivePromotionalCarousel = () => {
<GenericButton onClick={() => router.push('/content?tag=all')} severity="primary" icon={<i className="pi pi-eye pr-2" />} label="View all content" className="py-2 font-semibold" size="small" outlined />
</>
);
case "COURSES":
case "CONTENT":
return (
<GenericButton onClick={() => router.push('/content?tag=courses')} icon={<i className="pi pi-book pr-2 pb-1" />} label="View All Courses" className="py-2 font-semibold" size="small" outlined />
);
case "VIDEOS":
return (
<GenericButton onClick={() => router.push('/content?tag=videos')} icon={<i className="pi pi-video pr-2" />} label="View All Videos" className="py-2 font-semibold" size="small" outlined />
);
case "DOCUMENTS":
return (
<GenericButton onClick={() => router.push('/content?tag=documents')} icon={<i className="pi pi-file pr-2 pb-1" />} label="View All Documents" className="py-2 font-semibold" size="small" outlined />
<>
<GenericButton onClick={() => router.push('/content?tag=courses')} icon={<i className="pi pi-book pr-2 pb-1" />} label="Courses" className="py-2 font-semibold" size="small" outlined />
<GenericButton onClick={() => router.push('/content?tag=videos')} icon={<i className="pi pi-video pr-2" />} label="Videos" className="py-2 font-semibold" size="small" outlined />
<GenericButton onClick={() => router.push('/content?tag=documents')} icon={<i className="pi pi-file pr-2 pb-1" />} label="Documents" className="py-2 font-semibold" size="small" outlined />
</>
);
case "COMMUNITY":
return (
@ -177,8 +170,8 @@ const InteractivePromotionalCarousel = () => {
{promotions.map((promo) => (
<div
key={promo.id}
className={`flex-shrink-0 w-48 space-y-2 cursor-pointer transition-colors duration-200 ${selectedPromotion.id === promo.id ? "bg-gray-800" : "hover:bg-gray-700"
} p-3 rounded-lg shadow-lg`}
className={`flex-shrink-0 w-64 space-y-4 cursor-pointer transition-colors duration-200 bg-gray-800 ${selectedPromotion.id === promo.id ? "bg-gray-800" : "hover:bg-gray-700"
} p-4 rounded-lg shadow-lg`}
onClick={() => setSelectedPromotion(promo)}>
<div className="flex items-center gap-2">
<i className={`${promo.icon} text-2xl text-[#f8f8ff]`}></i>
@ -192,14 +185,14 @@ const InteractivePromotionalCarousel = () => {
promotions.map((promo) => (
<div
key={promo.id}
className={`space-y-2 cursor-pointer transition-colors duration-200 ${selectedPromotion.id === promo.id ? "bg-gray-800" : "hover:bg-gray-700"
} p-3 rounded-lg shadow-lg`}
className={`space-y-8 cursor-pointer transition-colors duration-200 bg-gray-800 ${selectedPromotion.id === promo.id ? "bg-gray-800" : "hover:bg-gray-700"
} p-4 rounded-lg shadow-lg`}
onClick={() => setSelectedPromotion(promo)}>
<div className="flex items-center gap-2">
<i className={`${promo.icon} text-2xl text-[#f8f8ff]`}></i>
<div className="text-sm font-bold text-[#f8f8ff]">{promo.category}</div>
<div className="text-lg font-semibold text-[#f8f8ff]">{promo.category}</div>
</div>
<h4 className="text-white font-semibold">{promo.title}</h4>
<h4 className="text-white text-lg">{promo.title}</h4>
</div>
))
)}

View File

@ -103,7 +103,7 @@ export default function CourseDetailsNew({ processedEvent, paidCourse, lessons,
if (paidCourse && !decryptionPerformed) {
return (
<CoursePaymentButton
lnAddress={lnAddress}
lnAddress={author?.lnAddress}
amount={processedEvent.price}
onSuccess={handlePaymentSuccess}
onError={handlePaymentError}

View File

@ -21,8 +21,6 @@ const MDDisplay = dynamic(
}
);
const lnAddress = process.env.NEXT_PUBLIC_LIGHTNING_ADDRESS;
const DocumentDetails = ({ processedEvent, topics, title, summary, image, price, author, paidResource, decryptedContent, nAddress, handlePaymentSuccess, handlePaymentError, authorView }) => {
const [zapAmount, setZapAmount] = useState(0);
const router = useRouter();
@ -96,7 +94,7 @@ const DocumentDetails = ({ processedEvent, topics, title, summary, image, price,
</p>
<div className="flex flex-row items-center justify-center w-full mt-4">
<ResourcePaymentButton
lnAddress={lnAddress}
lnAddress={author?.lightningAddress}
amount={price}
onSuccess={handlePaymentSuccess}
onError={handlePaymentError}

View File

@ -21,8 +21,6 @@ const MDDisplay = dynamic(
}
);
const lnAddress = process.env.NEXT_PUBLIC_LIGHTNING_ADDRESS;
const VideoDetails = ({ processedEvent, topics, title, summary, image, price, author, paidResource, decryptedContent, nAddress, handlePaymentSuccess, handlePaymentError, authorView }) => {
const [zapAmount, setZapAmount] = useState(0);
const router = useRouter();
@ -99,7 +97,7 @@ const VideoDetails = ({ processedEvent, topics, title, summary, image, price, au
</p>
<div className="flex flex-row items-center justify-center w-full mt-4 z-10">
<ResourcePaymentButton
lnAddress={lnAddress}
lnAddress={author?.lnAddress}
amount={price}
onSuccess={handlePaymentSuccess}
onError={handlePaymentError}

View File

@ -23,7 +23,6 @@ import 'primeicons/primeicons.css';
import { Tooltip } from 'primereact/tooltip';
import 'primereact/resources/primereact.min.css';
// todo make the summarry save in a formatted way so we can keep this spaces and line breaks
const DocumentForm = ({ draft = null, isPublished = false }) => {
const [title, setTitle] = useState(draft?.title || '');
const [summary, setSummary] = useState(draft?.summary || '');

View File

@ -41,7 +41,7 @@ const allTasks = [
const UserProgress = () => {
const [progress, setProgress] = useState(0);
const [currentTier, setCurrentTier] = useState('Pleb');
const [expanded, setExpanded] = useState(null);
const [expandedItems, setExpandedItems] = useState({});
const [completedCourses, setCompletedCourses] = useState([]);
const [tasks, setTasks] = useState([]);
@ -91,6 +91,13 @@ const UserProgress = () => {
fetchProgress();
}, []);
const handleAccordionChange = (index, isExpanded) => {
setExpandedItems(prev => ({
...prev,
[index]: isExpanded
}));
};
return (
<div className="bg-gray-800 rounded-3xl p-6 w-[500px] max-mob:w-full max-tab:w-full mx-auto my-8">
<h1 className="text-3xl font-bold text-white mb-2">Dev Journey (Coming Soon)</h1>
@ -115,7 +122,11 @@ const UserProgress = () => {
{tasks.map((task, index) => (
<li key={index}>
{task.subTasks ? (
<Accordion className="border-none" onTabOpen={(e) => setExpanded(true)} onTabClose={(e) => setExpanded(false)}>
<Accordion
className="border-none"
onTabChange={(e) => handleAccordionChange(index, e.index === 0)}
activeIndex={expandedItems[index] ? 0 : null}
>
<AccordionTab
pt={{
root: { className: 'border-none p-0' },
@ -129,10 +140,12 @@ const UserProgress = () => {
<div className="bg-gray-800 flex items-center justify-between w-full font-normal">
<div className="flex items-center">
<div className="w-6 h-6 bg-gray-700 rounded-full flex items-center justify-center mr-3">
<i className="pi pi-info-circle text-white text-lg"></i>
<i className="pi pi-info-circle text-white text-2xl"></i>
</div>
<div className="flex items-center justify-between w-[160px] max-mob:w-[100px]">
<span className="text-lg text-gray-400">{task.status}</span>
<i className={`pi pi-chevron-down text-gray-400 ml-2 transition-transform duration-300 ${expandedItems[index] ? 'rotate-180' : ''}`} />
</div>
<span className="text-lg text-gray-400">{task.status}</span>
<i className={`pi pi-chevron-down text-gray-400 ml-2 transition-transform duration-300 ${expanded ? 'rotate-180' : ''}`} />
</div>
<span className="bg-blue-500 text-white text-xs px-2 py-1 rounded-full w-20 text-center">
{task.tier}

View File

@ -98,28 +98,30 @@ const AboutPage = () => {
severity="secondary"
outlined
icon="pi pi-github"
label="GitHub"
tooltip="Github"
className="text-gray-300"
onClick={() => window.open('https://github.com/pleb-devs', '_blank')}
/>
<GenericButton
severity="info"
outlined
icon="pi pi-twitter"
label="X.com"
tooltip="X"
onClick={() => window.open('https://x.com/pleb_devs', '_blank')}
/>
<GenericButton
severity="help"
outlined
icon={<Image src={NostrIcon} alt="Nostr" width={20} height={20} className="mr-2" />}
label="Nostr"
icon={<Image src={NostrIcon} alt="Nostr" width={20} height={20} className="mr-0" />}
tooltip="Nostr"
onClick={() => window.open('https://nostr.com/plebdevs@plebdevs.com', '_blank')}
/>
<GenericButton
severity="warning"
className="text-yellow-400"
outlined
icon="pi pi-bolt"
label="Donate"
tooltip="Donate"
onClick={() => copyToClipboard("austin@bitcoinpleb.dev")}
/>
</div>

View File

@ -5,7 +5,7 @@ import VideosCarousel from '@/components/content/carousels/VideosCarousel';
import DocumentsCarousel from '@/components/content/carousels/DocumentsCarousel';
import InteractivePromotionalCarousel from '@/components/content/carousels/InteractivePromotionalCarousel';
// todo: make paid course videos and documents not appear in carousels
// todo: make paid course video and document lessons not appear in carousels
export default function Home() {
return (
<>

View File

@ -34,6 +34,12 @@ export const findKind0Fields = async (kind0) => {
fields.pubkey = pubkey;
}
const lud16 = findTruthyPropertyValue(kind0, ['lud16', 'lightning', 'lnurl', 'lnurlp', 'lnurlw']);
if (lud16) {
fields.lightningAddress = lud16;
}
return fields;
}