mirror of
https://github.com/aljazceru/mutiny-web.git
synced 2026-02-19 13:14:20 +01:00
toggle anon zaps
This commit is contained in:
3
src/assets/icons/private-eye.svg
Normal file
3
src/assets/icons/private-eye.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
<path d="M9.8585 7.5L12.5002 10.1333V10C12.5002 9.33696 12.2368 8.70107 11.7679 8.23223C11.2991 7.76339 10.6632 7.5 10.0002 7.5H9.8585ZM6.27516 8.16667L7.56683 9.45833C7.52516 9.63333 7.50016 9.80833 7.50016 10C7.50016 10.663 7.76356 11.2989 8.2324 11.7678C8.70124 12.2366 9.33712 12.5 10.0002 12.5C10.1835 12.5 10.3668 12.475 10.5418 12.4333L11.8335 13.725C11.2752 14 10.6585 14.1667 10.0002 14.1667C8.89509 14.1667 7.83529 13.7277 7.05388 12.9463C6.27248 12.1649 5.8335 11.1051 5.8335 10C5.8335 9.34167 6.00016 8.725 6.27516 8.16667ZM1.66683 3.55833L3.56683 5.45833L3.94183 5.83333C2.56683 6.91667 1.4835 8.33333 0.833496 10C2.27516 13.6583 5.8335 16.25 10.0002 16.25C11.2918 16.25 12.5252 16 13.6502 15.55L14.0085 15.9L16.4418 18.3333L17.5002 17.275L2.72516 2.5M10.0002 5.83333C11.1052 5.83333 12.165 6.27232 12.9464 7.05372C13.7278 7.83512 14.1668 8.89493 14.1668 10C14.1668 10.5333 14.0585 11.05 13.8668 11.5167L16.3085 13.9583C17.5585 12.9167 18.5585 11.55 19.1668 10C17.7252 6.34167 14.1668 3.75 10.0002 3.75C8.8335 3.75 7.71683 3.95833 6.66683 4.33333L8.47516 6.125C8.95016 5.94167 9.4585 5.83333 10.0002 5.83333Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -7,7 +7,7 @@ import {
|
||||
Show
|
||||
} from "solid-js";
|
||||
|
||||
import { AmountSats, BigMoney } from "~/components";
|
||||
import { AmountSats, BigMoney, SharpButton } from "~/components";
|
||||
import { useMegaStore } from "~/state/megaStore";
|
||||
import {
|
||||
btcFloatRounding,
|
||||
@@ -106,7 +106,6 @@ export const AmountEditable: ParentComponent<{
|
||||
);
|
||||
}
|
||||
} else {
|
||||
console.log("we're in the fiat branch");
|
||||
sane = fiatInputSanitizer(
|
||||
value.replace(",", "."),
|
||||
state.fiat.maxFractionalDigits
|
||||
@@ -270,20 +269,17 @@ function MethodChooser(props: {
|
||||
props.setChosenMethod && props.setChosenMethod(nextMethod);
|
||||
}
|
||||
return (
|
||||
<button
|
||||
onClick={setNextMethod}
|
||||
disabled={props.methods.length === 1}
|
||||
class="flex gap-2 rounded px-2 py-1 text-sm font-light text-m-grey-400 md:text-base"
|
||||
classList={{
|
||||
"border-b border-t border-b-white/10 border-t-white/50 bg-neutral-700":
|
||||
props.methods?.length > 1
|
||||
}}
|
||||
>
|
||||
<AmountSats
|
||||
amountSats={props.activeMethod.maxAmountSats!}
|
||||
denominationSize="sm"
|
||||
icon={methodToIcon(props.activeMethod.method)}
|
||||
/>
|
||||
</button>
|
||||
<>
|
||||
<SharpButton
|
||||
onClick={setNextMethod}
|
||||
disabled={props.methods.length === 1}
|
||||
>
|
||||
<AmountSats
|
||||
amountSats={props.activeMethod.maxAmountSats!}
|
||||
denominationSize="sm"
|
||||
icon={methodToIcon(props.activeMethod.method)}
|
||||
/>
|
||||
</SharpButton>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ export function BigMoney(props: {
|
||||
<div
|
||||
class="mb-2 mt-4 h-[2px] w-full rounded-full"
|
||||
classList={{
|
||||
"bg-m-blue animate-pulse": props.inputFocused,
|
||||
"bg-m-blue": props.inputFocused,
|
||||
"bg-m-blue/0": !props.inputFocused
|
||||
}}
|
||||
/>
|
||||
|
||||
21
src/components/SharpButton.tsx
Normal file
21
src/components/SharpButton.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { JSX } from "solid-js";
|
||||
|
||||
export function SharpButton(props: {
|
||||
onClick: () => void;
|
||||
children: JSX.Element;
|
||||
disabled?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
onClick={() => props.onClick()}
|
||||
disabled={props.disabled}
|
||||
class="flex gap-2 rounded px-2 py-1 text-sm font-light text-m-grey-400 md:text-base"
|
||||
classList={{
|
||||
"border-b border-t border-b-white/10 border-t-white/50 bg-neutral-700":
|
||||
!props.disabled
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -50,3 +50,4 @@ export * from "./FeeDisplay";
|
||||
export * from "./ReceiveWarnings";
|
||||
export * from "./SimpleInput";
|
||||
export * from "./LabelCircle";
|
||||
export * from "./SharpButton";
|
||||
|
||||
@@ -126,6 +126,7 @@ export default {
|
||||
sats_sent: "sats sent"
|
||||
},
|
||||
what_for: "What's this for?",
|
||||
zap_note: "Zap note",
|
||||
error_low_balance:
|
||||
"We do not have enough balance to pay the given amount.",
|
||||
error_invoice_match:
|
||||
@@ -144,7 +145,9 @@ export default {
|
||||
payment_pending_description:
|
||||
"It's taking a while, but it's possible this payment may still go through. Please check 'Activity' for the current status.",
|
||||
hodl_invoice_warning:
|
||||
"This is a hodl invoice. Payments to hodl invoices can cause channel force closes, which results in high on-chain fees. Pay at your own risk!"
|
||||
"This is a hodl invoice. Payments to hodl invoices can cause channel force closes, which results in high on-chain fees. Pay at your own risk!",
|
||||
private: "Private",
|
||||
anonzap: "Anon Zap"
|
||||
},
|
||||
feedback: {
|
||||
header: "Give us feedback!",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MutinyInvoice } from "@mutinywallet/mutiny-wasm";
|
||||
import { MutinyInvoice, TagItem } from "@mutinywallet/mutiny-wasm";
|
||||
import { A, useNavigate, useSearchParams } from "@solidjs/router";
|
||||
import {
|
||||
createEffect,
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
import bolt from "~/assets/icons/bolt.svg";
|
||||
import chain from "~/assets/icons/chain.svg";
|
||||
import close from "~/assets/icons/close.svg";
|
||||
import privateEye from "~/assets/icons/private-eye.svg";
|
||||
import {
|
||||
ActivityDetailsModal,
|
||||
AmountEditable,
|
||||
@@ -36,6 +37,7 @@ import {
|
||||
MethodChoice,
|
||||
MutinyWalletGuard,
|
||||
NavBar,
|
||||
SharpButton,
|
||||
showToast,
|
||||
SimpleInput,
|
||||
SmallHeader,
|
||||
@@ -72,36 +74,18 @@ function DestinationShower(props: {
|
||||
nodePubkey?: string;
|
||||
lnurl?: string;
|
||||
lightning_address?: string;
|
||||
contact_id?: string;
|
||||
contact?: TagItem;
|
||||
}) {
|
||||
const [state, _actions] = useMegaStore();
|
||||
|
||||
async function getContact(id: string) {
|
||||
console.log("fetching contact", id);
|
||||
try {
|
||||
const contact = state.mutiny_wallet?.get_tag_item(id);
|
||||
console.log("fetching contact", contact);
|
||||
// This shouldn't happen
|
||||
if (!contact) throw new Error("Contact not found");
|
||||
return contact;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
showToast(eify(e));
|
||||
}
|
||||
}
|
||||
|
||||
const [contact] = createResource(() => props.contact_id, getContact);
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
<Match when={contact.latest}>
|
||||
<Match when={props.contact}>
|
||||
<DestinationItem
|
||||
title={contact()?.name || ""}
|
||||
value={contact()?.ln_address}
|
||||
title={props.contact?.name || ""}
|
||||
value={props.contact?.ln_address}
|
||||
icon={
|
||||
<LabelCircle
|
||||
name={contact()?.name || ""}
|
||||
image_url={contact()?.image_url}
|
||||
name={props.contact?.name || ""}
|
||||
image_url={props.contact?.image_url}
|
||||
contact
|
||||
label={false}
|
||||
/>
|
||||
@@ -253,6 +237,7 @@ export function Send() {
|
||||
|
||||
// These can be derived from the destination or set by the user
|
||||
const [amountSats, setAmountSats] = createSignal(0n);
|
||||
const [unparsedAmount, setUnparsedAmount] = createSignal(true);
|
||||
|
||||
// These are derived from the incoming destination
|
||||
const [isAmtEditable, setIsAmtEditable] = createSignal(true);
|
||||
@@ -509,8 +494,14 @@ export function Send() {
|
||||
} else {
|
||||
const parsed = BigInt(amountInput());
|
||||
console.log("parsed", parsed);
|
||||
if (!parsed) {
|
||||
setUnparsedAmount(true);
|
||||
}
|
||||
if (parsed > 0n) {
|
||||
setAmountSats(parsed);
|
||||
setUnparsedAmount(false);
|
||||
} else {
|
||||
setUnparsedAmount(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -574,12 +565,18 @@ export function Send() {
|
||||
sentDetails.fee_estimate = payment?.fees_paid || 0;
|
||||
}
|
||||
} else if (source() === "lightning" && lnurlp()) {
|
||||
const zapNpub =
|
||||
visibility() === "anonzap" && contact()?.npub
|
||||
? contact()?.npub
|
||||
: undefined;
|
||||
console.log("zapnpub", zapNpub);
|
||||
const comment = zapNpub ? whatForInput() : undefined;
|
||||
const payment = await state.mutiny_wallet?.lnurl_pay(
|
||||
lnurlp()!,
|
||||
amountSats(),
|
||||
undefined, // zap_npub
|
||||
zapNpub, // zap_npub
|
||||
tags,
|
||||
undefined // comment
|
||||
comment // comment
|
||||
);
|
||||
sentDetails.payment_hash = payment?.payment_hash;
|
||||
|
||||
@@ -646,9 +643,11 @@ export function Send() {
|
||||
|
||||
const sendButtonDisabled = createMemo(() => {
|
||||
return (
|
||||
unparsedAmount() ||
|
||||
parsingDestination() ||
|
||||
sending() ||
|
||||
amountSats() === 0n ||
|
||||
amountSats() == 0n ||
|
||||
amountSats() === undefined ||
|
||||
!!error()
|
||||
);
|
||||
});
|
||||
@@ -706,6 +705,34 @@ export function Send() {
|
||||
}
|
||||
});
|
||||
|
||||
const [visibility, setVisibility] = createSignal<"private" | "anonzap">(
|
||||
"private"
|
||||
);
|
||||
|
||||
function toggleVisibility() {
|
||||
if (visibility() === "private") {
|
||||
setVisibility("anonzap");
|
||||
} else {
|
||||
setVisibility("private");
|
||||
}
|
||||
}
|
||||
|
||||
async function getContact(id: string) {
|
||||
console.log("fetching contact", id);
|
||||
try {
|
||||
const contact = state.mutiny_wallet?.get_tag_item(id);
|
||||
console.log("fetching contact", contact);
|
||||
// This shouldn't happen
|
||||
if (!contact) throw new Error("Contact not found");
|
||||
return contact;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
showToast(eify(e));
|
||||
}
|
||||
}
|
||||
|
||||
const [contact] = createResource(contactId, getContact);
|
||||
|
||||
return (
|
||||
<MutinyWalletGuard>
|
||||
<DefaultMain>
|
||||
@@ -786,7 +813,7 @@ export function Send() {
|
||||
nodePubkey={nodePubkey()}
|
||||
lnurl={lnurlp()}
|
||||
lightning_address={lnAddress()}
|
||||
contact_id={contactId()}
|
||||
contact={contact()}
|
||||
/>
|
||||
</Suspense>
|
||||
<div class="flex-1" />
|
||||
@@ -843,6 +870,45 @@ export function Send() {
|
||||
<div class="flex-1" />
|
||||
|
||||
<VStack>
|
||||
<Suspense>
|
||||
<div class="flex w-full">
|
||||
<SharpButton
|
||||
onClick={toggleVisibility}
|
||||
disabled={!contact()?.npub}
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<Switch>
|
||||
<Match
|
||||
when={
|
||||
visibility() === "private"
|
||||
}
|
||||
>
|
||||
<img
|
||||
src={privateEye}
|
||||
alt="Private"
|
||||
/>
|
||||
<span>
|
||||
{i18n.t("send.private")}
|
||||
</span>
|
||||
</Match>
|
||||
<Match
|
||||
when={
|
||||
visibility() === "anonzap"
|
||||
}
|
||||
>
|
||||
<img
|
||||
src={bolt}
|
||||
alt="Anon Zap"
|
||||
/>
|
||||
<span>
|
||||
{i18n.t("send.anonzap")}
|
||||
</span>
|
||||
</Match>
|
||||
</Switch>
|
||||
</div>
|
||||
</SharpButton>
|
||||
</div>
|
||||
</Suspense>
|
||||
<form
|
||||
onSubmit={async (e) => {
|
||||
e.preventDefault();
|
||||
@@ -853,7 +919,11 @@ export function Send() {
|
||||
>
|
||||
<SimpleInput
|
||||
type="text"
|
||||
placeholder={i18n.t("send.what_for")}
|
||||
placeholder={
|
||||
visibility() === "private"
|
||||
? i18n.t("send.what_for")
|
||||
: i18n.t("send.zap_note")
|
||||
}
|
||||
onInput={(e) =>
|
||||
setWhatForInput(e.currentTarget.value)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user