Show invoices
This commit is contained in:
5
src/element/Invoice.css
Normal file
5
src/element/Invoice.css
Normal file
@ -0,0 +1,5 @@
|
||||
.invoice {
|
||||
border-radius: 10px;
|
||||
border: 1px solid #444;
|
||||
padding: 10px;
|
||||
}
|
27
src/element/Invoice.js
Normal file
27
src/element/Invoice.js
Normal file
@ -0,0 +1,27 @@
|
||||
import "./Invoice.css";
|
||||
import { decode as invoiceDecode } from "light-bolt11-decoder";
|
||||
import { useMemo } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
export default function Invoice(props) {
|
||||
const invoice = props.invoice;
|
||||
const navigate = useNavigate();
|
||||
|
||||
const info = useMemo(() => {
|
||||
let parsed = invoiceDecode(invoice);
|
||||
|
||||
let amount = parseInt(parsed.sections.find(a => a.name === "amount")?.value);
|
||||
return {
|
||||
amount: !isNaN(amount) ? (amount / 1000) : null
|
||||
}
|
||||
}, [invoice]);
|
||||
|
||||
return (
|
||||
<div className="invoice flex">
|
||||
<div className="f-grow">
|
||||
⚡️ Invoice for {info?.amount?.toLocaleString()} sats
|
||||
</div>
|
||||
<div className="btn" onClick={() => window.open(`lightning:${invoice}`)}>Pay</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import "./Note.css";
|
||||
import { useState } from "react";
|
||||
import { useCallback, useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import moment from "moment";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
@ -10,10 +10,12 @@ import Event from "../nostr/Event";
|
||||
import ProfileImage from "./ProfileImage";
|
||||
import useEventPublisher from "../feed/EventPublisher";
|
||||
import { NoteCreator } from "./NoteCreator";
|
||||
import Invoice from "./Invoice";
|
||||
|
||||
const UrlRegex = /((?:http|ftp|https):\/\/(?:[\w+?\.\w+])+(?:[a-zA-Z0-9\~\!\@\#\$\%\^\&\*\(\)_\-\=\+\\\/\?\.\:\;\'\,]*)?)/i;
|
||||
const FileExtensionRegex = /\.([\w]+)$/i;
|
||||
const MentionRegex = /(#\[\d+\])/gi;
|
||||
const InvoiceRegex = /(lnbc\w+)/i;
|
||||
|
||||
export default function Note(props) {
|
||||
const navigate = useNavigate();
|
||||
@ -33,6 +35,14 @@ export default function Note(props) {
|
||||
...opt
|
||||
};
|
||||
|
||||
const transformBody = useCallback(() => {
|
||||
let body = ev?.Content ?? "";
|
||||
|
||||
let fragments = extractLinks([body]);
|
||||
fragments = extractMentions(fragments);
|
||||
return extractInvoices(fragments);
|
||||
}, [data, dataEvent]);
|
||||
|
||||
function goToEvent(e, id) {
|
||||
if (!window.location.pathname.startsWith("/e/")) {
|
||||
e.stopPropagation();
|
||||
@ -55,36 +65,25 @@ export default function Note(props) {
|
||||
)
|
||||
}
|
||||
|
||||
function transformBody() {
|
||||
let body = ev.Content;
|
||||
let urlBody = body.split(UrlRegex);
|
||||
|
||||
return urlBody.map(a => {
|
||||
if (a.startsWith("http")) {
|
||||
let url = new URL(a);
|
||||
let ext = url.pathname.toLowerCase().match(FileExtensionRegex);
|
||||
if (ext) {
|
||||
switch (ext[1]) {
|
||||
case "gif":
|
||||
case "jpg":
|
||||
case "jpeg":
|
||||
case "png":
|
||||
case "bmp":
|
||||
case "webp": {
|
||||
return <img key={url} src={url} />;
|
||||
}
|
||||
case "mp4":
|
||||
case "mkv":
|
||||
case "avi":
|
||||
case "m4v": {
|
||||
return <video key={url} src={url} controls />
|
||||
}
|
||||
function extractInvoices(fragments) {
|
||||
return fragments.map(f => {
|
||||
if (typeof f === "string") {
|
||||
return f.split(InvoiceRegex).map(i => {
|
||||
if (i.toLowerCase().startsWith("lnbc")) {
|
||||
return <Invoice key={i} invoice={i}/>
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
} else {
|
||||
return <a href={url}>{url.toString()}</a>
|
||||
}
|
||||
} else {
|
||||
let mentions = a.split(MentionRegex).map((match) => {
|
||||
});
|
||||
}
|
||||
return f;
|
||||
}).flat();
|
||||
}
|
||||
|
||||
function extractMentions(fragments) {
|
||||
return fragments.map(f => {
|
||||
if (typeof f === "string") {
|
||||
return f.split(MentionRegex).map((match) => {
|
||||
let matchTag = match.match(/#\[(\d+)\]/);
|
||||
if (matchTag && matchTag.length === 2) {
|
||||
let idx = parseInt(matchTag[1]);
|
||||
@ -106,10 +105,44 @@ export default function Note(props) {
|
||||
return match;
|
||||
}
|
||||
});
|
||||
return mentions;
|
||||
}
|
||||
return a;
|
||||
});
|
||||
return f;
|
||||
}).flat();
|
||||
}
|
||||
|
||||
function extractLinks(fragments) {
|
||||
return fragments.map(f => {
|
||||
if (typeof f === "string") {
|
||||
return f.split(UrlRegex).map(a => {
|
||||
if (a.startsWith("http")) {
|
||||
let url = new URL(a);
|
||||
let ext = url.pathname.toLowerCase().match(FileExtensionRegex);
|
||||
if (ext) {
|
||||
switch (ext[1]) {
|
||||
case "gif":
|
||||
case "jpg":
|
||||
case "jpeg":
|
||||
case "png":
|
||||
case "bmp":
|
||||
case "webp": {
|
||||
return <img key={url} src={url} />;
|
||||
}
|
||||
case "mp4":
|
||||
case "mkv":
|
||||
case "avi":
|
||||
case "m4v": {
|
||||
return <video key={url} src={url} controls />
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return <a href={url}>{url.toString()}</a>
|
||||
}
|
||||
}
|
||||
return a;
|
||||
});
|
||||
}
|
||||
return f;
|
||||
}).flat();
|
||||
}
|
||||
|
||||
async function like() {
|
||||
|
@ -18,6 +18,7 @@ const RecommendedFollows = [
|
||||
"3F770D65D3A764A9C5CB503AE123E62EC7598AD035D836E2A810F3877A745B24", // DerekRoss
|
||||
"472F440F29EF996E92A186B8D320FF180C855903882E59D50DE1B8BD5669301E", // MartyBent
|
||||
"1577e4599dd10c863498fe3c20bd82aafaf829a595ce83c5cf8ac3463531b09b", // yegorpetrov
|
||||
"04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9", // ODELL
|
||||
];
|
||||
|
||||
export default function NewUserPage(props) {
|
||||
|
@ -124,6 +124,7 @@ const LoginSlice = createSlice({
|
||||
state.publicKey = null;
|
||||
state.follows = [];
|
||||
state.notifications = [];
|
||||
state.loggedOut = true;
|
||||
},
|
||||
markNotificationsRead: (state) => {
|
||||
state.readNotifications = new Date().getTime();
|
||||
|
Reference in New Issue
Block a user