From fabf714cdce74a52cb1e4b6368aff58d78c06b71 Mon Sep 17 00:00:00 2001 From: austinkelsay Date: Mon, 1 Apr 2024 18:13:38 -0500 Subject: [PATCH] Added zap amounts onto course template, implemented bitcoin connect connect button --- package-lock.json | 157 ++++++++++++++++++ package.json | 2 + .../carousels/templates/CourseTemplate.js | 26 ++- src/components/profile/BitcoinConnect.js | 35 ++++ src/hooks/useNostr.js | 11 +- src/pages/details/[slug].js | 53 ++++-- src/pages/profile.js | 5 + src/utils/lightning.js | 6 + 8 files changed, 268 insertions(+), 27 deletions(-) create mode 100644 src/components/profile/BitcoinConnect.js create mode 100644 src/utils/lightning.js diff --git a/package-lock.json b/package-lock.json index a9b7e3f..4c9c346 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,10 +8,12 @@ "name": "plebdevs-new", "version": "0.1.0", "dependencies": { + "@getalby/bitcoin-connect-react": "^3.2.2", "@prisma/client": "^5.9.1", "@reduxjs/toolkit": "^2.1.0", "axios": "^1.6.7", "classnames": "^2.5.1", + "light-bolt11-decoder": "^3.1.1", "next": "14.0.4", "next-auth": "^4.24.5", "nostr-tools": "^2.1.5", @@ -125,6 +127,102 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@getalby/bitcoin-connect": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@getalby/bitcoin-connect/-/bitcoin-connect-3.2.2.tgz", + "integrity": "sha512-hqsnTVNojTmLDXhhkJvs1RDFKZIFEQDD4UWNlB/hIV3Bx4INxRZzeOi8SOG7idjGF3ix7I8hpG3zeeJCZWNByQ==", + "dependencies": { + "@getalby/lightning-tools": "^5.0.1", + "@getalby/sdk": "^3.2.3", + "@lightninglabs/lnc-web": "^0.3.1-alpha", + "qrcode-generator": "^1.4.4", + "zustand": "^4.4.7" + } + }, + "node_modules/@getalby/bitcoin-connect-react": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@getalby/bitcoin-connect-react/-/bitcoin-connect-react-3.2.2.tgz", + "integrity": "sha512-JLJBJgYySr7LDIfycDum/pNdqKxKr81Rw+iidO6CW45N3XGyVLEHLMDZI1ZfwV1i/nsOiqL65BUICGXrBSBnHA==", + "dependencies": { + "@getalby/bitcoin-connect": "^3.2.2" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/@getalby/lightning-tools": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@getalby/lightning-tools/-/lightning-tools-5.0.2.tgz", + "integrity": "sha512-XS08p4JcUre4L1ROkIGQv2DidasPtFLlFVW6oJlqBi12ClRJnURnAV3h3yCi/r7sFixONjz+2IGwirTZUu5fPA==", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "lightning", + "url": "lightning:hello@getalby.com" + } + }, + "node_modules/@getalby/sdk": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@getalby/sdk/-/sdk-3.4.3.tgz", + "integrity": "sha512-K0F8Sj3aGmsBV87jfYbMBCAYbb8d9JrLA5jUYn+LuE59IF1flw4pQSb7irQBJYmiFHtvHA8+bpg38WRJr7hpeg==", + "dependencies": { + "eventemitter3": "^5.0.1", + "nostr-tools": "^1.17.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "lightning", + "url": "lightning:hello@getalby.com" + } + }, + "node_modules/@getalby/sdk/node_modules/@noble/ciphers": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.2.0.tgz", + "integrity": "sha512-6YBxJDAapHSdd3bLDv6x2wRPwq4QFMUaB3HvljNBUTThDd12eSm7/3F+2lnfzx2jvM+S6Nsy0jEt9QbPqSwqRw==", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@getalby/sdk/node_modules/@noble/curves": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", + "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", + "dependencies": { + "@noble/hashes": "1.3.1" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@getalby/sdk/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, + "node_modules/@getalby/sdk/node_modules/nostr-tools": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-1.17.0.tgz", + "integrity": "sha512-LZmR8GEWKZeElbFV5Xte75dOeE9EFUW/QLI1Ncn3JKn0kFddDKEfBbFN8Mu4TMs+L4HR/WTPha2l+PPuRnJcMw==", + "dependencies": { + "@noble/ciphers": "0.2.0", + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@scure/base": "1.1.1", + "@scure/bip32": "1.3.1", + "@scure/bip39": "1.2.1" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -250,6 +348,20 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@lightninglabs/lnc-core": { + "version": "0.3.1-alpha", + "resolved": "https://registry.npmjs.org/@lightninglabs/lnc-core/-/lnc-core-0.3.1-alpha.tgz", + "integrity": "sha512-I/hThdItLWJ6RU8Z27ZIXhpBS2JJuD3+TjtaQXX2CabaUYXlcN4sk+Kx8N/zG/fk8qZvjlRWum4vHu4ZX554Fg==" + }, + "node_modules/@lightninglabs/lnc-web": { + "version": "0.3.1-alpha", + "resolved": "https://registry.npmjs.org/@lightninglabs/lnc-web/-/lnc-web-0.3.1-alpha.tgz", + "integrity": "sha512-yL5SgBkl6kd6ISzJHGlSN7TXbiDoo1pfGvTOIdVWYVyXtEeW8PT+x6YGOmyQXGFT2OOf7fC7PfP9VnskDPuFaA==", + "dependencies": { + "@lightninglabs/lnc-core": "0.3.1-alpha", + "crypto-js": "4.2.0" + } + }, "node_modules/@next/env": { "version": "14.0.4", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.0.4.tgz", @@ -1585,6 +1697,11 @@ "node": ">= 8" } }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -3683,6 +3800,14 @@ "node": ">= 0.8.0" } }, + "node_modules/light-bolt11-decoder": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/light-bolt11-decoder/-/light-bolt11-decoder-3.1.1.tgz", + "integrity": "sha512-sLg/KCwYkgsHWkefWd6KqpCHrLFWWaXTOX3cf6yD2hAzL0SLpX+lFcaFK2spkjbgzG6hhijKfORDc9WoUHwX0A==", + "dependencies": { + "@scure/base": "1.1.1" + } + }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -5233,6 +5358,11 @@ "node": ">=6" } }, + "node_modules/qrcode-generator": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-1.4.4.tgz", + "integrity": "sha512-HM7yY8O2ilqhmULxGMpcHSF1EhJJ9yBj8gvDEuZ6M+KGJ0YY2hKpnXvRD+hZPLrDVck3ExIGhmPtSdcjC+guuw==" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -6787,6 +6917,33 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zustand": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.2.tgz", + "integrity": "sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==", + "dependencies": { + "use-sync-external-store": "1.2.0" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/package.json b/package.json index 523ce21..b71b5df 100644 --- a/package.json +++ b/package.json @@ -9,10 +9,12 @@ "lint": "next lint" }, "dependencies": { + "@getalby/bitcoin-connect-react": "^3.2.2", "@prisma/client": "^5.9.1", "@reduxjs/toolkit": "^2.1.0", "axios": "^1.6.7", "classnames": "^2.5.1", + "light-bolt11-decoder": "^3.1.1", "next": "14.0.4", "next-auth": "^4.24.5", "nostr-tools": "^2.1.5", diff --git a/src/components/content/carousels/templates/CourseTemplate.js b/src/components/content/carousels/templates/CourseTemplate.js index 3c92cd1..ca07305 100644 --- a/src/components/content/carousels/templates/CourseTemplate.js +++ b/src/components/content/carousels/templates/CourseTemplate.js @@ -1,27 +1,42 @@ -import React, {useEffect, useState} from "react"; +import React, {use, useEffect, useState} from "react"; import Image from "next/image"; import { useRouter } from "next/router"; import useResponsiveImageDimensions from "@/hooks/useResponsiveImageDimensions"; import { formatTimestampToHowLongAgo } from "@/utils/time"; import { useImageProxy } from "@/hooks/useImageProxy"; import { useNostr } from "@/hooks/useNostr"; +import {getSatAmountFromInvoice} from "@/utils/lightning"; const CourseTemplate = (course) => { const [zaps, setZaps] = useState([]); + const [zapAmount, setZapAmount] = useState(null); const router = useRouter(); const { returnImageProxy } = useImageProxy(); const { width, height } = useResponsiveImageDimensions(); const {events, fetchZapsForEvent} = useNostr(); useEffect(() => { - if (events && events.zaps) { - console.log('zaps:', events.zaps); + if (events && events.zaps.length > 0) { setZaps(events.zaps); } else { fetchZapsForEvent(course.id); } }, [events]); + useEffect(() => { + if (zaps.length > 0) { + zaps.map((zap) => { + const bolt11Tag = zap.tags.find(tag => tag[0] === 'bolt11'); + const invoice = bolt11Tag ? bolt11Tag[1] : null; + + if (invoice) { + const amount = getSatAmountFromInvoice(invoice); + setZapAmount(zapAmount + amount); + } + }) + } + }, [zaps]); + return (
router.push(`/details/${course.id}`)} className="flex flex-col items-center mx-auto px-4 cursor-pointer mt-8 rounded-md shadow-lg">
@@ -46,7 +61,10 @@ const CourseTemplate = (course) => { }}> {course.summary}

-

Published: {formatTimestampToHowLongAgo(course.published_at)}

+
+

Published: {formatTimestampToHowLongAgo(course.published_at)}

+

{zapAmount}

+
diff --git a/src/components/profile/BitcoinConnect.js b/src/components/profile/BitcoinConnect.js new file mode 100644 index 0000000..277eee1 --- /dev/null +++ b/src/components/profile/BitcoinConnect.js @@ -0,0 +1,35 @@ +"use client"; +import dynamic from 'next/dynamic'; +import { useEffect } from 'react'; + +const Button = dynamic( + () => import('@getalby/bitcoin-connect-react').then((mod) => mod.Button), + { + ssr: false, + } +); + +const BitcoinConnectButton = () => { + + useEffect(() => { + const initializeBitcoinConnect = async () => { + // Initialize Bitcoin Connect + const { init } = await import('@getalby/bitcoin-connect-react'); + init({ + appName: "PlebDevs", + filters: ["nwc"], + showBalance: false + }); + }; + + initializeBitcoinConnect(); + }, []); // Empty dependency array to run only once on component mount + + return ( +