Skip to content

Commit

Permalink
move method chooser to receive amount screen
Browse files Browse the repository at this point in the history
  • Loading branch information
futurepaul committed May 6, 2024
1 parent 465dd2a commit bb54b63
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 83 deletions.
3 changes: 2 additions & 1 deletion public/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@
"title": "Receive Method",
"body": "Lightning receives will automatically go into your chosen federation. You can swap to self-custodial later if you want."
},
"receive_strings_error": "Something went wrong generating an invoice or on-chain address."
"receive_strings_error": "Something went wrong generating an invoice or on-chain address.",
"error_under_min_onchain": "That's under the dust limit! On-chain transactions should be much bigger."
},
"send": {
"search": {
Expand Down
15 changes: 15 additions & 0 deletions src/components/ReceiveWarnings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import { createResource, Match, Switch } from "solid-js";
import { InfoBox } from "~/components/InfoBox";
import { FeesModal } from "~/components/MoreInfoModal";
import { useI18n } from "~/i18n/context";
import { ReceiveFlavor } from "~/routes";
import { useMegaStore } from "~/state/megaStore";

export function ReceiveWarnings(props: {
amountSats: bigint;
from_fedi_to_ln?: boolean;
flavor?: ReceiveFlavor;
}) {
const i18n = useI18n();
const [state, _actions, sw] = useMegaStore();
Expand Down Expand Up @@ -65,8 +67,21 @@ export function ReceiveWarnings(props: {
}
};

const tooSmallWarning = () => {
if (
props.flavor === "onchain" &&
props.amountSats > 0n &&
props.amountSats < 546n
) {
return i18n.t("receive.error_under_min_onchain");
}
};

return (
<Switch>
<Match when={tooSmallWarning()}>
<InfoBox accent="red">{tooSmallWarning()}</InfoBox>
</Match>
<Match when={sillyAmountWarning()}>
<InfoBox accent="red">{sillyAmountWarning()}</InfoBox>
</Match>
Expand Down
157 changes: 75 additions & 82 deletions src/routes/Receive.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { MutinyInvoice } from "@mutinywallet/mutiny-wasm";
import { useNavigate } from "@solidjs/router";
import { ArrowLeftRight, CircleHelp, Users } from "lucide-solid";
import { CircleHelp, Link, Users, Zap } from "lucide-solid";
import {
createEffect,
createMemo,
Expand All @@ -20,7 +20,6 @@ import {
BackButton,
BackLink,
Button,
Checkbox,
DefaultMain,
Fee,
FeesModal,
Expand All @@ -33,6 +32,7 @@ import {
MutinyWalletGuard,
NavBar,
ReceiveWarnings,
SharpButton,
showToast,
SimpleDialog,
SimpleInput,
Expand Down Expand Up @@ -86,6 +86,53 @@ function FeeWarning(props: { fee: bigint; flavor: ReceiveFlavor }) {
);
}

function FlavorChooser(props: {
flavor: ReceiveFlavor;
setFlavor: (value: string) => void;
}) {
const [methodChooserOpen, setMethodChooserOpen] = createSignal(false);
const i18n = useI18n();

const RECEIVE_FLAVORS = [
{
value: "lightning",
label: i18n.t("receive.lightning_label"),
caption: i18n.t("receive.lightning_caption")
},
{
value: "onchain",
label: i18n.t("receive.onchain_label"),
caption: i18n.t("receive.onchain_caption")
}
];
return (
<>
<SharpButton onClick={() => setMethodChooserOpen(true)}>
{props.flavor === "lightning" ? (
<Zap class="h-4 w-4" />
) : (
<Link class="h-4 w-4" />
)}
{props.flavor === "lightning" ? "Lightning" : "On-chain"}
</SharpButton>
<SimpleDialog
title={i18n.t("receive.choose_payment_format")}
open={methodChooserOpen()}
setOpen={(open) => setMethodChooserOpen(open)}
>
<StyledRadioGroup
initialValue={props.flavor}
onValueChange={props.setFlavor}
choices={RECEIVE_FLAVORS}
accent="white"
vertical
delayOnChange
/>
</SimpleDialog>
</>
);
}

function ReceiveMethodHelp() {
const i18n = useI18n();
const [open, setOpen] = createSignal(false);
Expand All @@ -107,7 +154,7 @@ function ReceiveMethodHelp() {
}

export function Receive() {
const [state, actions, sw] = useMegaStore();
const [state, _actions, sw] = useMegaStore();
const navigate = useNavigate();
const i18n = useI18n();

Expand All @@ -133,12 +180,7 @@ export function Receive() {
const [paymentInvoice, setPaymentInvoice] = createSignal<MutinyInvoice>();

// The flavor of the receive
const [flavor, setFlavor] = createSignal<ReceiveFlavor>(
// Have to support "legacy" unified setting
state.preferredInvoiceType === "unified"
? "lightning"
: state.preferredInvoiceType
);
const [flavor, setFlavor] = createSignal<ReceiveFlavor>("lightning");

// loading state for the continue button
const [loading, setLoading] = createSignal(false);
Expand All @@ -149,34 +191,19 @@ export function Receive() {
const [detailsKind, setDetailsKind] = createSignal<HackActivityType>();
const [detailsId, setDetailsId] = createSignal<string>("");

const RECEIVE_FLAVORS = [
{
value: "lightning",
label: i18n.t("receive.lightning_label"),
caption: i18n.t("receive.lightning_caption")
},
{
value: "onchain",
label: i18n.t("receive.onchain_label"),
caption: i18n.t("receive.onchain_caption")
}
];

const [rememberChoice, setRememberChoice] = createSignal(false);

function clearAll() {
setAmount(0n);
function clearAllButAmount() {
setReceiveState("edit");
setReceiveStrings(undefined);
setPaymentTx(undefined);
setPaymentInvoice(undefined);
setError("");
// Have to support "legacy" unified setting
setFlavor(
state.preferredInvoiceType === "unified"
? "lightning"
: state.preferredInvoiceType
);
}

function clearAll() {
clearAllButAmount();
setAmount(0n);
setFlavor("lightning");
setWhatForInput("");
}

function openDetailsModal() {
Expand Down Expand Up @@ -217,16 +244,14 @@ export function Receive() {
return `lightning:${bolt11}`;
} catch (e) {
console.error(e);
if (e === "Satoshi amount is invalid") {
setError(i18n.t("receive.error_under_min_lightning"));
} else {
setError(i18n.t("receive.error_creating_unified"));
}
}
}

async function getOnchainReceiveString(amount: bigint) {
try {
if (amount < 546n) {
throw new Error(i18n.t("receive.error_under_min_onchain"));
}
const raw = await sw.get_new_address(receiveTags());

const btc_amount = await sw.convert_sats_to_btc(amount);
Expand All @@ -240,7 +265,6 @@ export function Receive() {
return `bitcoin:${address}?${params}`;
} catch (e) {
console.error(e);
setError(i18n.t("receive.error_creating_address"));
}
}

Expand Down Expand Up @@ -338,15 +362,6 @@ export function Receive() {
}
}

async function selectFlavor(flavor: string) {
setFlavor(flavor as ReceiveFlavor);
if (rememberChoice()) {
actions.setPreferredInvoiceType(flavor as ReceiveFlavor);
}
await getQr();
setMethodChooserOpen(false);
}

const [paidState, { refetch }] = createResource(
rawReceiveStrings,
checkIfPaid
Expand All @@ -364,14 +379,12 @@ export function Receive() {
});
});

const [methodChooserOpen, setMethodChooserOpen] = createSignal(false);

return (
<MutinyWalletGuard>
<DefaultMain>
<Show when={receiveState() === "show"} fallback={<BackLink />}>
<BackButton
onClick={() => clearAll()}
onClick={() => clearAllButAmount()}
title={i18n.t("receive.edit")}
showOnDesktop
/>
Expand All @@ -391,14 +404,21 @@ export function Receive() {
>
<div class="flex-1" />
<VStack>
<AmountEditable
initialAmountSats={amount() || "0"}
setAmountSats={setAmount}
onSubmit={getQr}
/>
<div class="mx-auto flex w-full max-w-[400px] flex-col items-center">
<AmountEditable
initialAmountSats={amount() || "0"}
setAmountSats={setAmount}
onSubmit={getQr}
/>
<FlavorChooser
flavor={flavor()}
setFlavor={setFlavor}
/>
</div>
<ReceiveWarnings
amountSats={amount() || 0n}
from_fedi_to_ln={false}
flavor={flavor()}
/>
</VStack>
<div class="flex-1" />
Expand Down Expand Up @@ -446,33 +466,6 @@ export function Receive() {
<p class="text-center text-m-grey-350">
{i18n.t("receive.keep_mutiny_open")}
</p>
{/* Only show method chooser when we have an invoice */}
<button
class="mx-auto flex items-center gap-2 pb-8 font-bold text-m-grey-400"
onClick={() => setMethodChooserOpen(true)}
>
<span>{i18n.t("receive.choose_format")}</span>
<ArrowLeftRight class="h-4 w-4" />
</button>
<SimpleDialog
title={i18n.t("receive.choose_payment_format")}
open={methodChooserOpen()}
setOpen={(open) => setMethodChooserOpen(open)}
>
<StyledRadioGroup
initialValue={flavor()}
onValueChange={selectFlavor}
choices={RECEIVE_FLAVORS}
accent="white"
vertical
delayOnChange
/>
<Checkbox
label={i18n.t("receive.remember_choice")}
checked={rememberChoice()}
onChange={setRememberChoice}
/>
</SimpleDialog>
</Match>
<Match when={receiveState() === "paid"}>
<SuccessModal
Expand Down

0 comments on commit bb54b63

Please sign in to comment.