split SendSats
This commit is contained in:
parent
649bab228b
commit
c8dae9fae6
@ -20,7 +20,16 @@ module.exports = {
|
||||
"simple-import-sort/imports": "error",
|
||||
"simple-import-sort/exports": "error",
|
||||
"@typescript-eslint/no-unused-vars": "error",
|
||||
"max-lines": ["warn", { max: 300, skipBlankLines: true, skipComments: true }],
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ["*.tsx"],
|
||||
rules: {
|
||||
"max-lines": ["warn", { max: 200, skipBlankLines: true, skipComments: true }],
|
||||
},
|
||||
},
|
||||
],
|
||||
root: true,
|
||||
ignorePatterns: ["build/", "*.test.ts", "*.js"],
|
||||
env: {
|
||||
|
@ -3,30 +3,18 @@ import "./SendSats.css";
|
||||
import { LNURLSuccessAction } from "@snort/shared";
|
||||
import { HexKey } from "@snort/system";
|
||||
import React, { ReactNode, useEffect, useState } from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import AsyncButton from "@/Components/Button/AsyncButton";
|
||||
import CloseButton from "@/Components/Button/CloseButton";
|
||||
import Copy from "@/Components/Copy/Copy";
|
||||
import Icon from "@/Components/Icons/Icon";
|
||||
import Modal from "@/Components/Modal/Modal";
|
||||
import QrCode from "@/Components/QrCode";
|
||||
import ProfileImage from "@/Components/User/ProfileImage";
|
||||
import { SendSatsInput, SendSatsInputSelection } from "@/Components/SendSats/SendSatsInput";
|
||||
import { SendSatsInvoice } from "@/Components/SendSats/SendSatsInvoice";
|
||||
import { SendSatsTitle } from "@/Components/SendSats/SendSatsTitle";
|
||||
import { SuccessAction } from "@/Components/SendSats/SuccessAction";
|
||||
import { ZapType } from "@/Components/SendSats/ZapType";
|
||||
import useEventPublisher from "@/Hooks/useEventPublisher";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import { debounce } from "@/Utils";
|
||||
import { formatShort } from "@/Utils/Number";
|
||||
import { Zapper, ZapTarget, ZapTargetResult } from "@/Utils/Zapper";
|
||||
import { LNWallet, useWallet } from "@/Wallet";
|
||||
|
||||
import messages from "../messages";
|
||||
|
||||
enum ZapType {
|
||||
PublicZap = 1,
|
||||
AnonZap = 2,
|
||||
PrivateZap = 3,
|
||||
NonZap = 4,
|
||||
}
|
||||
import { useWallet } from "@/Wallet";
|
||||
|
||||
export interface SendSatsProps {
|
||||
onClose?: () => void;
|
||||
@ -97,91 +85,14 @@ export default function SendSats(props: SendSatsProps) {
|
||||
}
|
||||
}, [props.targets, props.show]);
|
||||
|
||||
function successAction() {
|
||||
if (!success) return null;
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<p className="flex g12">
|
||||
<Icon name="check" className="success" />
|
||||
{success?.description ?? <FormattedMessage defaultMessage="Paid" id="u/vOPu" />}
|
||||
</p>
|
||||
{success.url && (
|
||||
<p>
|
||||
<a href={success.url} rel="noreferrer" target="_blank">
|
||||
{success.url}
|
||||
</a>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function title() {
|
||||
if (!props.targets) {
|
||||
return (
|
||||
<>
|
||||
<h2>
|
||||
{zapper?.canZap() ? (
|
||||
<FormattedMessage defaultMessage="Send zap" id="5ykRmX" />
|
||||
) : (
|
||||
<FormattedMessage defaultMessage="Send sats" id="DKnriN" />
|
||||
)}
|
||||
</h2>
|
||||
</>
|
||||
);
|
||||
}
|
||||
if (props.targets.length === 1 && props.targets[0].name) {
|
||||
const t = props.targets[0];
|
||||
const values = {
|
||||
name: t.name,
|
||||
};
|
||||
return (
|
||||
<>
|
||||
{t.zap?.pubkey && <ProfileImage pubkey={t.zap.pubkey} showUsername={false} />}
|
||||
<h2>
|
||||
{zapper?.canZap() ? (
|
||||
<FormattedMessage defaultMessage="Send zap to {name}" id="SMO+on" values={values} />
|
||||
) : (
|
||||
<FormattedMessage defaultMessage="Send sats to {name}" id="JGrt9q" values={values} />
|
||||
)}
|
||||
</h2>
|
||||
</>
|
||||
);
|
||||
}
|
||||
if (props.targets.length > 1) {
|
||||
const total = props.targets.reduce((acc, v) => (acc += v.weight), 0);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col g12">
|
||||
<h2>
|
||||
{zapper?.canZap() ? (
|
||||
<FormattedMessage defaultMessage="Send zap splits to" id="ZS+jRE" />
|
||||
) : (
|
||||
<FormattedMessage defaultMessage="Send sats splits to" id="uc0din" />
|
||||
)}
|
||||
</h2>
|
||||
<div className="flex g4 f-wrap">
|
||||
{props.targets.map(v => (
|
||||
<ProfileImage
|
||||
key={v.value}
|
||||
pubkey={v.value}
|
||||
showUsername={false}
|
||||
showFollowDistance={false}
|
||||
imageOverlay={formatShort(Math.floor((amount?.amount ?? 0) * (v.weight / total)))}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(props.show ?? false)) return null;
|
||||
return (
|
||||
<Modal id="send-sats" className="lnurl-modal" onClose={onClose}>
|
||||
<div className="p flex flex-col g12">
|
||||
<div className="flex g12">
|
||||
<div className="flex items-center grow">{props.title || title()}</div>
|
||||
<div className="flex items-center grow">
|
||||
{props.title || <SendSatsTitle amount={amount} targets={props.targets} zapper={zapper} />}
|
||||
</div>
|
||||
<CloseButton onClick={onClose} />
|
||||
</div>
|
||||
{zapper && !invoice && (
|
||||
@ -227,180 +138,8 @@ export default function SendSats(props: SendSatsProps) {
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{successAction()}
|
||||
{success && <SuccessAction success={success} />}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
interface SendSatsInputSelection {
|
||||
amount: number;
|
||||
comment?: string;
|
||||
type: ZapType;
|
||||
}
|
||||
|
||||
function SendSatsInput(props: {
|
||||
zapper: Zapper;
|
||||
onChange?: (v: SendSatsInputSelection) => void;
|
||||
onNextStage: (v: SendSatsInputSelection) => Promise<void>;
|
||||
}) {
|
||||
const { defaultZapAmount, readonly } = useLogin(s => ({
|
||||
defaultZapAmount: s.appData.item.preferences.defaultZapAmount,
|
||||
readonly: s.readonly,
|
||||
}));
|
||||
const { formatMessage } = useIntl();
|
||||
const amounts: Record<string, string> = {
|
||||
[defaultZapAmount.toString()]: "",
|
||||
"1000": "👍",
|
||||
"5000": "💜",
|
||||
"10000": "😍",
|
||||
"20000": "🤩",
|
||||
"50000": "🔥",
|
||||
"100000": "🚀",
|
||||
"1000000": "🤯",
|
||||
};
|
||||
const [comment, setComment] = useState<string>();
|
||||
const [amount, setAmount] = useState<number>(defaultZapAmount);
|
||||
const [customAmount, setCustomAmount] = useState<number>(defaultZapAmount);
|
||||
const [zapType, setZapType] = useState(readonly ? ZapType.AnonZap : ZapType.PublicZap);
|
||||
|
||||
function getValue() {
|
||||
return {
|
||||
amount,
|
||||
comment,
|
||||
type: zapType,
|
||||
} as SendSatsInputSelection;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (props.onChange) {
|
||||
props.onChange(getValue());
|
||||
}
|
||||
}, [amount, comment, zapType]);
|
||||
|
||||
function renderAmounts() {
|
||||
const min = props.zapper.minAmount() / 1000;
|
||||
const max = props.zapper.maxAmount() / 1000;
|
||||
const filteredAmounts = Object.entries(amounts).filter(([k]) => Number(k) >= min && Number(k) <= max);
|
||||
|
||||
return (
|
||||
<div className="amounts">
|
||||
{filteredAmounts.map(([k, v]) => (
|
||||
<span
|
||||
className={`sat-amount ${amount === Number(k) ? "active" : ""}`}
|
||||
key={k}
|
||||
onClick={() => setAmount(Number(k))}>
|
||||
{v}
|
||||
{k === "1000" ? "1K" : formatShort(Number(k))}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function custom() {
|
||||
const min = props.zapper.minAmount() / 1000;
|
||||
const max = props.zapper.maxAmount() / 1000;
|
||||
|
||||
return (
|
||||
<div className="flex g8">
|
||||
<input
|
||||
type="number"
|
||||
min={min}
|
||||
max={max}
|
||||
className="grow"
|
||||
placeholder={formatMessage(messages.Custom)}
|
||||
value={customAmount}
|
||||
onChange={e => setCustomAmount(parseInt(e.target.value))}
|
||||
/>
|
||||
<button
|
||||
className="secondary"
|
||||
type="button"
|
||||
disabled={!customAmount}
|
||||
onClick={() => setAmount(customAmount ?? 0)}>
|
||||
<FormattedMessage {...messages.Confirm} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col g24">
|
||||
<div className="flex flex-col g8">
|
||||
<h3>
|
||||
<FormattedMessage defaultMessage="Zap amount in sats" id="zcaOTs" />
|
||||
</h3>
|
||||
{renderAmounts()}
|
||||
{custom()}
|
||||
{props.zapper.maxComment() > 0 && (
|
||||
<input
|
||||
type="text"
|
||||
placeholder={formatMessage(messages.Comment)}
|
||||
className="grow"
|
||||
maxLength={props.zapper.maxComment()}
|
||||
onChange={e => setComment(e.target.value)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<SendSatsZapTypeSelector zapType={zapType} setZapType={setZapType} />
|
||||
{(amount ?? 0) > 0 && (
|
||||
<AsyncButton onClick={() => props.onNextStage(getValue())}>
|
||||
<Icon name="zap" />
|
||||
<FormattedMessage defaultMessage="Zap {n} sats" id="8QDesP" values={{ n: formatShort(amount) }} />
|
||||
</AsyncButton>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SendSatsZapTypeSelector({ zapType, setZapType }: { zapType: ZapType; setZapType: (t: ZapType) => void }) {
|
||||
const { readonly } = useLogin(s => ({ readonly: s.readonly }));
|
||||
const makeTab = (t: ZapType, n: React.ReactNode) => (
|
||||
<button type="button" className={zapType === t ? "" : "secondary"} onClick={() => setZapType(t)}>
|
||||
{n}
|
||||
</button>
|
||||
);
|
||||
return (
|
||||
<div className="flex flex-col g8">
|
||||
<h3>
|
||||
<FormattedMessage defaultMessage="Zap Type" id="+aZY2h" />
|
||||
</h3>
|
||||
<div className="flex g8">
|
||||
{!readonly &&
|
||||
makeTab(ZapType.PublicZap, <FormattedMessage defaultMessage="Public" id="/PCavi" description="Public Zap" />)}
|
||||
{/*makeTab(ZapType.PrivateZap, "Private")*/}
|
||||
{makeTab(ZapType.AnonZap, <FormattedMessage defaultMessage="Anon" id="wWLwvh" description="Anonymous Zap" />)}
|
||||
{makeTab(
|
||||
ZapType.NonZap,
|
||||
<FormattedMessage defaultMessage="Non-Zap" id="AnLrRC" description="Non-Zap, Regular LN payment" />,
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SendSatsInvoice(props: {
|
||||
invoice: Array<ZapTargetResult>;
|
||||
wallet?: LNWallet;
|
||||
notice?: ReactNode;
|
||||
onInvoicePaid: () => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex flex-col items-center g12 txt-center">
|
||||
{props.notice && <b className="error">{props.notice}</b>}
|
||||
{props.invoice.map(v => (
|
||||
<>
|
||||
<QrCode data={v.pr} link={`lightning:${v.pr}`} />
|
||||
<div className="flex flex-col g12">
|
||||
<Copy text={v.pr} maxSize={26} className="items-center" />
|
||||
<a href={`lightning:${v.pr}`}>
|
||||
<button type="button">
|
||||
<FormattedMessage defaultMessage="Open Wallet" id="HbefNb" />
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
131
packages/app/src/Components/SendSats/SendSatsInput.tsx
Normal file
131
packages/app/src/Components/SendSats/SendSatsInput.tsx
Normal file
@ -0,0 +1,131 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import AsyncButton from "@/Components/Button/AsyncButton";
|
||||
import Icon from "@/Components/Icons/Icon";
|
||||
import messages from "@/Components/messages";
|
||||
import { SendSatsZapTypeSelector } from "@/Components/SendSats/SendSatsZapTypeSelector";
|
||||
import { ZapType } from "@/Components/SendSats/ZapType";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import { formatShort } from "@/Utils/Number";
|
||||
import { Zapper } from "@/Utils/Zapper";
|
||||
|
||||
export interface SendSatsInputSelection {
|
||||
amount: number;
|
||||
comment?: string;
|
||||
type: ZapType;
|
||||
}
|
||||
|
||||
export function SendSatsInput(props: {
|
||||
zapper: Zapper;
|
||||
onChange?: (v: SendSatsInputSelection) => void;
|
||||
onNextStage: (v: SendSatsInputSelection) => Promise<void>;
|
||||
}) {
|
||||
const { defaultZapAmount, readonly } = useLogin(s => ({
|
||||
defaultZapAmount: s.appData.item.preferences.defaultZapAmount,
|
||||
readonly: s.readonly,
|
||||
}));
|
||||
const { formatMessage } = useIntl();
|
||||
const amounts: Record<string, string> = {
|
||||
[defaultZapAmount.toString()]: "",
|
||||
"1000": "👍",
|
||||
"5000": "💜",
|
||||
"10000": "😍",
|
||||
"20000": "🤩",
|
||||
"50000": "🔥",
|
||||
"100000": "🚀",
|
||||
"1000000": "🤯",
|
||||
};
|
||||
const [comment, setComment] = useState<string>();
|
||||
const [amount, setAmount] = useState<number>(defaultZapAmount);
|
||||
const [customAmount, setCustomAmount] = useState<number>(defaultZapAmount);
|
||||
const [zapType, setZapType] = useState(readonly ? ZapType.AnonZap : ZapType.PublicZap);
|
||||
|
||||
function getValue() {
|
||||
return {
|
||||
amount,
|
||||
comment,
|
||||
type: zapType,
|
||||
} as SendSatsInputSelection;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (props.onChange) {
|
||||
props.onChange(getValue());
|
||||
}
|
||||
}, [amount, comment, zapType]);
|
||||
|
||||
function renderAmounts() {
|
||||
const min = props.zapper.minAmount() / 1000;
|
||||
const max = props.zapper.maxAmount() / 1000;
|
||||
const filteredAmounts = Object.entries(amounts).filter(([k]) => Number(k) >= min && Number(k) <= max);
|
||||
|
||||
return (
|
||||
<div className="amounts">
|
||||
{filteredAmounts.map(([k, v]) => (
|
||||
<span
|
||||
className={`sat-amount ${amount === Number(k) ? "active" : ""}`}
|
||||
key={k}
|
||||
onClick={() => setAmount(Number(k))}>
|
||||
{v}
|
||||
{k === "1000" ? "1K" : formatShort(Number(k))}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function custom() {
|
||||
const min = props.zapper.minAmount() / 1000;
|
||||
const max = props.zapper.maxAmount() / 1000;
|
||||
|
||||
return (
|
||||
<div className="flex g8">
|
||||
<input
|
||||
type="number"
|
||||
min={min}
|
||||
max={max}
|
||||
className="grow"
|
||||
placeholder={formatMessage(messages.Custom)}
|
||||
value={customAmount}
|
||||
onChange={e => setCustomAmount(parseInt(e.target.value))}
|
||||
/>
|
||||
<button
|
||||
className="secondary"
|
||||
type="button"
|
||||
disabled={!customAmount}
|
||||
onClick={() => setAmount(customAmount ?? 0)}>
|
||||
<FormattedMessage {...messages.Confirm} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col g24">
|
||||
<div className="flex flex-col g8">
|
||||
<h3>
|
||||
<FormattedMessage defaultMessage="Zap amount in sats" id="zcaOTs" />
|
||||
</h3>
|
||||
{renderAmounts()}
|
||||
{custom()}
|
||||
{props.zapper.maxComment() > 0 && (
|
||||
<input
|
||||
type="text"
|
||||
placeholder={formatMessage(messages.Comment)}
|
||||
className="grow"
|
||||
maxLength={props.zapper.maxComment()}
|
||||
onChange={e => setComment(e.target.value)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<SendSatsZapTypeSelector zapType={zapType} setZapType={setZapType} />
|
||||
{(amount ?? 0) > 0 && (
|
||||
<AsyncButton onClick={() => props.onNextStage(getValue())}>
|
||||
<Icon name="zap" />
|
||||
<FormattedMessage defaultMessage="Zap {n} sats" id="8QDesP" values={{ n: formatShort(amount) }} />
|
||||
</AsyncButton>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
33
packages/app/src/Components/SendSats/SendSatsInvoice.tsx
Normal file
33
packages/app/src/Components/SendSats/SendSatsInvoice.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import React, { ReactNode } from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import Copy from "@/Components/Copy/Copy";
|
||||
import QrCode from "@/Components/QrCode";
|
||||
import { ZapTargetResult } from "@/Utils/Zapper";
|
||||
import { LNWallet } from "@/Wallet";
|
||||
|
||||
export function SendSatsInvoice(props: {
|
||||
invoice: Array<ZapTargetResult>;
|
||||
wallet?: LNWallet;
|
||||
notice?: ReactNode;
|
||||
onInvoicePaid: () => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex flex-col items-center g12 txt-center">
|
||||
{props.notice && <b className="error">{props.notice}</b>}
|
||||
{props.invoice.map(v => (
|
||||
<>
|
||||
<QrCode data={v.pr} link={`lightning:${v.pr}`} />
|
||||
<div className="flex flex-col g12">
|
||||
<Copy text={v.pr} maxSize={26} className="items-center" />
|
||||
<a href={`lightning:${v.pr}`}>
|
||||
<button type="button">
|
||||
<FormattedMessage defaultMessage="Open Wallet" id="HbefNb" />
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
75
packages/app/src/Components/SendSats/SendSatsTitle.tsx
Normal file
75
packages/app/src/Components/SendSats/SendSatsTitle.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
import React from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import { SendSatsInputSelection } from "@/Components/SendSats/SendSatsInput";
|
||||
import ProfileImage from "@/Components/User/ProfileImage";
|
||||
import { formatShort } from "@/Utils/Number";
|
||||
import { Zapper, ZapTarget } from "@/Utils/Zapper";
|
||||
|
||||
export function SendSatsTitle({
|
||||
targets,
|
||||
zapper,
|
||||
amount,
|
||||
}: {
|
||||
targets?: Array<ZapTarget>;
|
||||
zapper?: Zapper;
|
||||
amount?: SendSatsInputSelection;
|
||||
}) {
|
||||
if (!targets) {
|
||||
return (
|
||||
<>
|
||||
<h2>
|
||||
{zapper?.canZap() ? (
|
||||
<FormattedMessage defaultMessage="Send zap" id="5ykRmX" />
|
||||
) : (
|
||||
<FormattedMessage defaultMessage="Send sats" id="DKnriN" />
|
||||
)}
|
||||
</h2>
|
||||
</>
|
||||
);
|
||||
}
|
||||
if (targets.length === 1 && targets[0].name) {
|
||||
const t = targets[0];
|
||||
const values = {
|
||||
name: t.name,
|
||||
};
|
||||
return (
|
||||
<>
|
||||
{t.zap?.pubkey && <ProfileImage pubkey={t.zap.pubkey} showUsername={false} />}
|
||||
<h2>
|
||||
{zapper?.canZap() ? (
|
||||
<FormattedMessage defaultMessage="Send zap to {name}" id="SMO+on" values={values} />
|
||||
) : (
|
||||
<FormattedMessage defaultMessage="Send sats to {name}" id="JGrt9q" values={values} />
|
||||
)}
|
||||
</h2>
|
||||
</>
|
||||
);
|
||||
}
|
||||
if (targets.length > 1) {
|
||||
const total = targets.reduce((acc, v) => (acc += v.weight), 0);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col g12">
|
||||
<h2>
|
||||
{zapper?.canZap() ? (
|
||||
<FormattedMessage defaultMessage="Send zap splits to" id="ZS+jRE" />
|
||||
) : (
|
||||
<FormattedMessage defaultMessage="Send sats splits to" id="uc0din" />
|
||||
)}
|
||||
</h2>
|
||||
<div className="flex g4 f-wrap">
|
||||
{targets.map(v => (
|
||||
<ProfileImage
|
||||
key={v.value}
|
||||
pubkey={v.value}
|
||||
showUsername={false}
|
||||
showFollowDistance={false}
|
||||
imageOverlay={formatShort(Math.floor((amount?.amount ?? 0) * (v.weight / total)))}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
import React from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import { ZapType } from "@/Components/SendSats/ZapType";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
|
||||
export function SendSatsZapTypeSelector({
|
||||
zapType,
|
||||
setZapType,
|
||||
}: {
|
||||
zapType: ZapType;
|
||||
setZapType: (t: ZapType) => void;
|
||||
}) {
|
||||
const { readonly } = useLogin(s => ({ readonly: s.readonly }));
|
||||
const makeTab = (t: ZapType, n: React.ReactNode) => (
|
||||
<button type="button" className={zapType === t ? "" : "secondary"} onClick={() => setZapType(t)}>
|
||||
{n}
|
||||
</button>
|
||||
);
|
||||
return (
|
||||
<div className="flex flex-col g8">
|
||||
<h3>
|
||||
<FormattedMessage defaultMessage="Zap Type" id="+aZY2h" />
|
||||
</h3>
|
||||
<div className="flex g8">
|
||||
{!readonly &&
|
||||
makeTab(ZapType.PublicZap, <FormattedMessage defaultMessage="Public" id="/PCavi" description="Public Zap" />)}
|
||||
{/*makeTab(ZapType.PrivateZap, "Private")*/}
|
||||
{makeTab(ZapType.AnonZap, <FormattedMessage defaultMessage="Anon" id="wWLwvh" description="Anonymous Zap" />)}
|
||||
{makeTab(
|
||||
ZapType.NonZap,
|
||||
<FormattedMessage defaultMessage="Non-Zap" id="AnLrRC" description="Non-Zap, Regular LN payment" />,
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
23
packages/app/src/Components/SendSats/SuccessAction.tsx
Normal file
23
packages/app/src/Components/SendSats/SuccessAction.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import { LNURLSuccessAction } from "@snort/shared";
|
||||
import React from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import Icon from "@/Components/Icons/Icon";
|
||||
|
||||
export function SuccessAction({ success }: { success: LNURLSuccessAction }) {
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<p className="flex g12">
|
||||
<Icon name="check" className="success" />
|
||||
{success?.description ?? <FormattedMessage defaultMessage="Paid" id="u/vOPu" />}
|
||||
</p>
|
||||
{success.url && (
|
||||
<p>
|
||||
<a href={success.url} rel="noreferrer" target="_blank">
|
||||
{success.url}
|
||||
</a>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
6
packages/app/src/Components/SendSats/ZapType.tsx
Normal file
6
packages/app/src/Components/SendSats/ZapType.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
export enum ZapType {
|
||||
PublicZap = 1,
|
||||
AnonZap = 2,
|
||||
PrivateZap = 3,
|
||||
NonZap = 4,
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
/* eslint-disable max-lines */
|
||||
|
||||
import { sha256 } from "@noble/hashes/sha256";
|
||||
|
||||
const animals = [
|
||||
|
Loading…
Reference in New Issue
Block a user