feat: add new success screens

This commit is contained in:
benalleng
2023-07-18 19:02:16 -04:00
committed by Paul Miller
parent 14a21e5290
commit d4a0655bf8
22 changed files with 314 additions and 217 deletions

View File

@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="icons">
<path id="Vector" d="M10.0463 7.50016L9.44634 8.1135C8.96634 8.5935 8.66634 9.00016 8.66634 10.0002H7.33301V9.66683C7.33301 8.92683 7.63301 8.26016 8.11301 7.78016L8.93967 6.94016C9.18634 6.70016 9.33301 6.36683 9.33301 6.00016C9.33301 5.64654 9.19253 5.3074 8.94248 5.05735C8.69243 4.8073 8.3533 4.66683 7.99967 4.66683C7.64605 4.66683 7.30691 4.8073 7.05687 5.05735C6.80682 5.3074 6.66634 5.64654 6.66634 6.00016H5.33301C5.33301 5.29292 5.61396 4.61464 6.11406 4.11454C6.61415 3.61445 7.29243 3.3335 7.99967 3.3335C8.70692 3.3335 9.38519 3.61445 9.88529 4.11454C10.3854 4.61464 10.6663 5.29292 10.6663 6.00016C10.6654 6.56232 10.4426 7.10138 10.0463 7.50016ZM8.66634 12.6668H7.33301V11.3335H8.66634M7.99967 1.3335C7.1242 1.3335 6.25729 1.50593 5.44845 1.84097C4.63961 2.176 3.90469 2.66706 3.28563 3.28612C2.03539 4.53636 1.33301 6.23205 1.33301 8.00016C1.33301 9.76827 2.03539 11.464 3.28563 12.7142C3.90469 13.3333 4.63961 13.8243 5.44845 14.1594C6.25729 14.4944 7.1242 14.6668 7.99967 14.6668C9.76778 14.6668 11.4635 13.9644 12.7137 12.7142C13.964 11.464 14.6663 9.76827 14.6663 8.00016C14.6663 4.3135 11.6663 1.3335 7.99967 1.3335Z" fill="white"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 70 KiB

View File

@@ -87,8 +87,6 @@ function UnifiedActivityItem(props: {
); );
} }
export function CombinedActivity(props: { limit?: number }) { export function CombinedActivity(props: { limit?: number }) {
const [state, _actions] = useMegaStore(); const [state, _actions] = useMegaStore();
const i18n = useI18n(); const i18n = useI18n();

View File

@@ -1,11 +1,4 @@
import { import { Match, ParentComponent, Switch, createResource } from "solid-js";
Match,
ParentComponent,
Switch,
createMemo,
createResource
} from "solid-js";
import { satsToUsd } from "~/utils/conversions";
import bolt from "~/assets/icons/bolt.svg"; import bolt from "~/assets/icons/bolt.svg";
import chain from "~/assets/icons/chain.svg"; import chain from "~/assets/icons/chain.svg";
import shuffle from "~/assets/icons/shuffle.svg"; import shuffle from "~/assets/icons/shuffle.svg";
@@ -16,6 +9,7 @@ import { timeAgo } from "~/utils/prettyPrintTime";
import { generateGradient } from "~/utils/gradientHash"; import { generateGradient } from "~/utils/gradientHash";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
import { Contact } from "@mutinywallet/mutiny-wasm"; import { Contact } from "@mutinywallet/mutiny-wasm";
import { Amount } from "~/components/Amount";
export const ActivityAmount: ParentComponent<{ export const ActivityAmount: ParentComponent<{
amount: string; amount: string;
@@ -23,24 +17,6 @@ export const ActivityAmount: ParentComponent<{
positive?: boolean; positive?: boolean;
center?: boolean; center?: boolean;
}> = (props) => { }> = (props) => {
const amountInUsd = createMemo(() => {
const parsed = Number(props.amount);
if (isNaN(parsed)) {
return props.amount;
} else {
return satsToUsd(props.price, parsed, true);
}
});
const prettyPrint = createMemo(() => {
const parsed = Number(props.amount);
if (isNaN(parsed)) {
return props.amount;
} else {
return parsed.toLocaleString();
}
});
return ( return (
<div <div
class="flex flex-col" class="flex flex-col"
@@ -49,17 +25,13 @@ export const ActivityAmount: ParentComponent<{
"items-center": props.center "items-center": props.center
}} }}
> >
<div <Amount
class="text-base" amountSats={Number(props.amount)}
classList={{ "text-m-green": props.positive }} align="right"
> icon={props.positive ? "plus" : undefined}
{props.positive && "+ "} showFiat
{prettyPrint()}&nbsp;<span class="text-sm">SATS</span> green={props.positive ? true : false}
</div> />
<div class="text-sm text-neutral-500">
&#8776;&nbsp;{amountInUsd()}&nbsp;
<span class="text-sm">USD</span>
</div>
</div> </div>
); );
}; };

View File

@@ -15,9 +15,11 @@ export function Amount(props: {
amountSats: bigint | number | undefined; amountSats: bigint | number | undefined;
showFiat?: boolean; showFiat?: boolean;
loading?: boolean; loading?: boolean;
centered?: boolean;
icon?: "lightning" | "chain";
whiteBg?: boolean; whiteBg?: boolean;
align?: "left" | "center" | "right";
icon?: "lightning" | "chain" | "plus" | "minus";
size?: "small" | "large" | "xl";
green?: boolean;
}) { }) {
const [state, _] = useMegaStore(); const [state, _] = useMegaStore();
@@ -28,7 +30,9 @@ export function Amount(props: {
<div <div
class="flex flex-col gap-1" class="flex flex-col gap-1"
classList={{ classList={{
"items-center": props.centered "items-start": props.align === "left",
"items-center": props.align === "center",
"items-end": props.align === "right"
}} }}
> >
<div class="flex gap-2 items-center"> <div class="flex gap-2 items-center">
@@ -39,28 +43,78 @@ export function Amount(props: {
<img src={chain} alt="chain" class="h-[18px]" /> <img src={chain} alt="chain" class="h-[18px]" />
</Show> </Show>
<h1 <h1
class="text-2xl font-light" class="font-light text-right"
classList={{ classList={{
"text-black": props.whiteBg "text-black": props.whiteBg,
"text-sm": props.size === "small",
"text-xl": props.size === "large",
"text-2xl": props.size === "xl",
"text-m-green": props.green
}} }}
> >
<Show when={props.icon === "plus"}>
<span>+</span>
</Show>
<Show when={props.icon === "minus"}>
<span>-</span>
</Show>
{props.loading {props.loading
? "..." ? "..."
: prettyPrintAmount(props.amountSats)} : prettyPrintAmount(props.amountSats)}
&nbsp; &nbsp;
<span class="text-base font-light">SATS</span> <span
class="font-light text-base"
classList={{
"text-sm": props.size === "small",
"text-lg": props.size === "xl"
}}
>
<Show
when={
!props.amountSats ||
Number(props.amountSats) > 1 ||
Number(props.amountSats) === 0
}
>
SATS
</Show>
<Show
when={
props.amountSats &&
Number(props.amountSats) === 1
}
>
SAT
</Show>
</span>
</h1> </h1>
</div> </div>
<Show when={props.showFiat}> <Show when={props.showFiat}>
<h2 <h2
class="text-sm font-light" class="font-light text-white/70"
classList={{ classList={{
"text-black": props.whiteBg, "text-black": props.whiteBg,
"text-white/70": !props.whiteBg "text-white/70": !props.whiteBg,
"text-sm": !props.size,
"text-xs": props.size === "small",
"text-base": props.size === "large",
"text-lg": props.size === "xl"
}} }}
> >
&#8776; {props.loading ? "..." : amountInUsd()}&nbsp; ~
<span class="text-sm">USD</span> <Show when={props.size === "xl"}>
<span>&nbsp;</span>
</Show>
{props.loading ? "..." : amountInUsd()}
<span
class="text-sm"
classList={{
"text-xs": props.size === "small",
"text-base": props.size === "large"
}}
>
&nbsp;USD
</span>
</h2> </h2>
</Show> </Show>
</div> </div>

View File

@@ -0,0 +1,22 @@
import { useMegaStore } from "~/state/megaStore";
import { satsToUsd } from "~/utils/conversions";
export function AmountFiat(props: {
amountSats: bigint | number | undefined;
loading?: boolean;
classes?: string;
}) {
const [state, _] = useMegaStore();
const amountInUsd = () =>
satsToUsd(state.price, Number(props.amountSats) || 0, true);
return (
<div class={`flex flex-col gap-1 ${props.classes}`}>
<h2 class="font-light text-white/70">
~{props.loading ? "..." : amountInUsd()}
<span>&nbsp;USD</span>
</h2>
</div>
);
}

View File

@@ -45,6 +45,7 @@ export default function BalanceBox(props: { loading?: boolean }) {
amountSats={state.balance?.lightning || 0} amountSats={state.balance?.lightning || 0}
showFiat showFiat
icon="lightning" icon="lightning"
size="xl"
/> />
</Show> </Show>
<hr class="my-2 border-m-grey-750" /> <hr class="my-2 border-m-grey-750" />
@@ -54,6 +55,7 @@ export default function BalanceBox(props: { loading?: boolean }) {
amountSats={totalOnchain()} amountSats={totalOnchain()}
showFiat showFiat
icon="chain" icon="chain"
size="xl"
/> />
<div class="flex flex-col items-end gap-1 justify-between"> <div class="flex flex-col items-end gap-1 justify-between">
<Show when={state.balance?.unconfirmed != 0n}> <Show when={state.balance?.unconfirmed != 0n}>

View File

@@ -1,20 +1,26 @@
import { Dialog } from "@kobalte/core"; import { Dialog } from "@kobalte/core";
import { ParentComponent, createSignal } from "solid-js"; import { ParentComponent, createSignal, JSXElement } from "solid-js";
import { DIALOG_CONTENT, DIALOG_POSITIONER, OVERLAY } from "./DetailsModal"; import { DIALOG_CONTENT, DIALOG_POSITIONER, OVERLAY } from "./DetailsModal";
import { ModalCloseButton, SmallHeader } from "./layout"; import { ModalCloseButton, SmallHeader } from "./layout";
import { ExternalLink } from "./layout/ExternalLink"; import { ExternalLink } from "./layout/ExternalLink";
import help from "~/assets/icons/help.svg";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
export function FeesModal() { export function FeesModal(props: { icon?: boolean }) {
const i18n = useI18n(); const i18n = useI18n();
return ( return (
<MoreInfoModal title={i18n.t("whats_with_the_fees")} linkText={i18n.t("why?")}> <MoreInfoModal
<p> title={i18n.t("whats_with_the_fees")}
{i18n.t("more_info_modal_p1")} linkText={
</p> props.icon ? (
<p> <img src={help} alt="help" class="w-4 h-4 cursor-pointer" />
{i18n.t("more_info_modal_p2")} ) : (
</p> i18n.t("why?")
)
}
>
<p>{i18n.t("more_info_modal_p1")}</p>
<p>{i18n.t("more_info_modal_p2")}</p>
<p> <p>
<ExternalLink href="https://github.com/MutinyWallet/mutiny-web/wiki/Understanding-liquidity"> <ExternalLink href="https://github.com/MutinyWallet/mutiny-web/wiki/Understanding-liquidity">
{i18n.t("learn_more_about_liquidity")} {i18n.t("learn_more_about_liquidity")}
@@ -25,7 +31,7 @@ export function FeesModal() {
} }
export const MoreInfoModal: ParentComponent<{ export const MoreInfoModal: ParentComponent<{
linkText: string; linkText: string | JSXElement;
title: string; title: string;
}> = (props) => { }> = (props) => {
const [open, setOpen] = createSignal(false); const [open, setOpen] = createSignal(false);

View File

@@ -16,7 +16,8 @@ export function ResetRouter() {
<InnerCard> <InnerCard>
<VStack> <VStack>
<NiceP> <NiceP>
Failing to make payments? Try resetting the lightning router. Failing to make payments? Try resetting the lightning
router.
</NiceP> </NiceP>
<Button intent="red" onClick={reset}> <Button intent="red" onClick={reset}>
Reset Router Reset Router

View File

@@ -12,7 +12,7 @@ const button = cva(
intent: { intent: {
active: "bg-white text-black border border-white hover:text-[#3B6CCC]", active: "bg-white text-black border border-white hover:text-[#3B6CCC]",
inactive: inactive:
"bg-black text-white border border-white hover:text-[#3B6CCC]", "bg-transparent text-white border border-white hover:text-[#3B6CCC]",
glowy: "bg-black/10 shadow-xl text-white border border-m-blue hover:m-blue-dark hover:text-m-blue", glowy: "bg-black/10 shadow-xl text-white border border-m-blue hover:m-blue-dark hover:text-m-blue",
blue: "bg-m-blue text-white shadow-inner-button hover:bg-m-blue-dark text-shadow-button", blue: "bg-m-blue text-white shadow-inner-button hover:bg-m-blue-dark text-shadow-button",
red: "bg-m-red text-white shadow-inner-button hover:bg-m-red-dark text-shadow-button", red: "bg-m-red text-white shadow-inner-button hover:bg-m-red-dark text-shadow-button",

View File

@@ -10,7 +10,12 @@ import {
} from "solid-js"; } from "solid-js";
import Linkify from "./Linkify"; import Linkify from "./Linkify";
import { Button, ButtonLink } from "./Button"; import { Button, ButtonLink } from "./Button";
import { Collapsible, Checkbox as KCheckbox, Dialog, Separator } from "@kobalte/core"; import {
Collapsible,
Checkbox as KCheckbox,
Dialog,
Separator
} from "@kobalte/core";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
import check from "~/assets/icons/check.svg"; import check from "~/assets/icons/check.svg";
import { MutinyTagItem } from "~/utils/tags"; import { MutinyTagItem } from "~/utils/tags";
@@ -207,12 +212,26 @@ export const LoadingSpinner = (props: { big?: boolean; wide?: boolean }) => {
export const Hr = () => <Separator.Root class="my-4 border-m-grey-750" />; export const Hr = () => <Separator.Root class="my-4 border-m-grey-750" />;
export const LargeHeader: ParentComponent<{ action?: JSX.Element }> = ( export const LargeHeader: ParentComponent<{
props action?: JSX.Element;
) => { centered?: boolean;
}> = (props) => {
return ( return (
<header class="w-full flex justify-between items-center mt-4 mb-2"> <header
<h1 class="text-2xl font-semibold">{props.children}</h1> class="w-full flex justify-between items-center mt-4 mb-2"
classList={{
"justify-between": !props.centered,
"justify-center": props.centered
}}
>
<h1
class="text-3xl font-semibold"
classList={{
"text-center": props.centered
}}
>
{props.children}
</h1>
<Show when={props.action}>{props.action}</Show> <Show when={props.action}>{props.action}</Show>
</header> </header>
); );

View File

@@ -1,10 +1,9 @@
import { Dialog } from "@kobalte/core"; import { Dialog } from "@kobalte/core";
import { JSX } from "solid-js"; import { JSX } from "solid-js";
import { Button, LargeHeader } from "~/components/layout"; import { Button } from "~/components/layout";
import { DIALOG_CONTENT, DIALOG_POSITIONER } from "~/styles/dialogs"; import { DIALOG_CONTENT, DIALOG_POSITIONER } from "~/styles/dialogs";
type SuccessModalProps = { type SuccessModalProps = {
title: string;
open: boolean; open: boolean;
setOpen: (open: boolean) => void; setOpen: (open: boolean) => void;
children?: JSX.Element; children?: JSX.Element;
@@ -23,17 +22,11 @@ export function SuccessModal(props: SuccessModalProps) {
<Dialog.Portal> <Dialog.Portal>
<div class={DIALOG_POSITIONER}> <div class={DIALOG_POSITIONER}>
<Dialog.Content class={DIALOG_CONTENT}> <Dialog.Content class={DIALOG_CONTENT}>
<div class="flex justify-between items-center mb-2"> <Dialog.Description class="flex flex-col items-center justify-center gap-6 h-full w-full max-w-[400px] mx-auto">
<Dialog.Title>
<LargeHeader>{props.title}</LargeHeader>
</Dialog.Title>
<div />
</div>
<Dialog.Description class="flex flex-col items-center justify-center gap-8 pb-4 h-full w-full max-w-[400px] mx-auto">
{props.children} {props.children}
</Dialog.Description> </Dialog.Description>
<div class="w-full flex max-w-[300px] mx-auto"> <div class="w-full flex max-w-[300px] mx-auto">
<Button onClick={onNice}> <Button onClick={onNice} intent="inactive">
{props.confirmText ?? "Nice"} {props.confirmText ?? "Nice"}
</Button> </Button>
</div> </div>

View File

@@ -1,12 +1,12 @@
import { createContext, useContext } from 'solid-js'; import { createContext, useContext } from "solid-js";
import { i18n } from "i18next"; import { i18n } from "i18next";
export const I18nContext = createContext<i18n>(); export const I18nContext = createContext<i18n>();
export function useI18n() { export function useI18n() {
const context = useContext(I18nContext); const context = useContext(I18nContext);
if (!context) throw new ReferenceError('I18nContext'); if (!context) throw new ReferenceError("I18nContext");
return context; return context;
} }

View File

@@ -83,7 +83,16 @@ export async function setAndGetMutinySettings(
); );
storage && localStorage.setItem("MUTINY_SETTINGS_storage", storage); storage && localStorage.setItem("MUTINY_SETTINGS_storage", storage);
return { network, proxy, esplora, rgs, lsp, auth, subscriptions, storage }; return {
network,
proxy,
esplora,
rgs,
lsp,
auth,
subscriptions,
storage
};
} catch (error) { } catch (error) {
console.error(error); console.error(error);
throw error; throw error;

View File

@@ -65,5 +65,5 @@ select {
} }
strong { strong {
@apply font-semibold text-m-red; @apply font-semibold text-m-red;
} }

View File

@@ -121,28 +121,6 @@ function FeeWarning(props: { fee: bigint; flavor: ReceiveFlavor }) {
); );
} }
function FeeExplanation(props: { fee: bigint }) {
return (
// TODO: probably won't always be a fixed 2500?
<Switch>
<Match when={props.fee > 1000n}>
<InfoBox accent="blue">
A lightning setup fee of{" "}
<AmountSmall amountSats={props.fee} /> was charged for this
receive. <FeesModal />
</InfoBox>
</Match>
<Match when={props.fee > 0n}>
<InfoBox accent="blue">
A lightning service fee of{" "}
<AmountSmall amountSats={props.fee} /> was charged for this
receive. <FeesModal />
</InfoBox>
</Match>
</Switch>
);
}
export default function Receive() { export default function Receive() {
const [state, _actions] = useMegaStore(); const [state, _actions] = useMegaStore();
const navigate = useNavigate(); const navigate = useNavigate();
@@ -448,14 +426,8 @@ export default function Receive() {
</SimpleDialog> </SimpleDialog>
</Show> </Show>
</Match> </Match>
<Match <Match when={receiveState() === "paid"}>
when={
receiveState() === "paid" &&
paidState() === "lightning_paid"
}
>
<SuccessModal <SuccessModal
title="Payment Received"
open={!!paidState()} open={!!paidState()}
setOpen={(open: boolean) => { setOpen={(open: boolean) => {
if (!open) clearAll(); if (!open) clearAll();
@@ -466,45 +438,66 @@ export default function Receive() {
}} }}
> >
<MegaCheck /> <MegaCheck />
<FeeExplanation fee={lspFee()} /> <h1 class="w-full mt-4 mb-2 text-2xl font-semibold text-center md:text-3xl">
{receiveState() === "paid" &&
paidState() === "lightning_paid"
? "Payment Received"
: "Payment Initiated"}
</h1>
<Amount <Amount
amountSats={paymentInvoice()?.amount_sats} amountSats={
receiveState() === "paid" &&
paidState() === "lightning_paid"
? paymentInvoice()?.amount_sats
: paymentTx()?.received
}
showFiat showFiat
centered align="center"
size="large"
icon="plus"
/> />
</SuccessModal> <hr class="w-16 bg-m-grey-400" />
</Match> <Show
<Match when={
when={ receiveState() === "paid" &&
receiveState() === "paid" && paidState() === "lightning_paid"
paidState() === "onchain_paid" }
}
>
<SuccessModal
title="Payment Received"
open={!!paidState()}
setOpen={(open: boolean) => {
if (!open) clearAll();
}}
onConfirm={() => {
clearAll();
navigate("/");
}}
>
<MegaCheck />
<Amount
amountSats={paymentTx()?.received}
showFiat
centered
/>
<ExternalLink
href={mempoolTxUrl(
paymentTx()?.txid,
network
)}
> >
View Transaction <div class="flex flex-row items-start gap-3">
</ExternalLink> <p class="text-m-grey-400 text-sm leading-[17px] items-center">
Fee
</p>
<div class="flex items-start gap-1">
<Amount
amountSats={lspFee()}
align="right"
size="small"
showFiat
/>
<div class="flex items-start py-[1px]">
{/*TODO: Add different fee hints insode of <FeesModal />*/}
<FeesModal icon />
</div>
</div>
</div>
</Show>
{/*TODO: Confirmation time estimate still not possible needs to be implemented in mutiny-node first
{/*TODO: add internal payment detail page* for lightning*/}
<Show
when={
receiveState() === "paid" &&
paidState() === "onchain_paid"
}
>
<ExternalLink
href={mempoolTxUrl(
paymentTx()?.txid,
network
)}
>
View payment details
</ExternalLink>
</Show>
</SuccessModal> </SuccessModal>
</Match> </Match>
</Switch> </Switch>

View File

@@ -28,8 +28,8 @@ import { Contact, MutinyInvoice } from "@mutinywallet/mutiny-wasm";
import { StyledRadioGroup } from "~/components/layout/Radio"; import { StyledRadioGroup } from "~/components/layout/Radio";
import { showToast } from "~/components/Toaster"; import { showToast } from "~/components/Toaster";
import eify from "~/utils/eify"; import eify from "~/utils/eify";
import megacheck from "~/assets/icons/megacheck.png"; import { MegaCheck } from "~/components/successfail/MegaCheck";
import megaex from "~/assets/icons/megaex.png"; import { MegaEx } from "~/components/successfail/MegaEx";
import mempoolTxUrl from "~/utils/mempoolTxUrl"; import mempoolTxUrl from "~/utils/mempoolTxUrl";
import { BackLink } from "~/components/layout/BackLink"; import { BackLink } from "~/components/layout/BackLink";
import { useNavigate } from "solid-start"; import { useNavigate } from "solid-start";
@@ -44,6 +44,7 @@ import { ExternalLink } from "~/components/layout/ExternalLink";
import { InfoBox } from "~/components/InfoBox"; import { InfoBox } from "~/components/InfoBox";
import { useI18n } from "~/i18n/context"; import { useI18n } from "~/i18n/context";
import { ParsedParams, toParsedParams } from "~/logic/waila"; import { ParsedParams, toParsedParams } from "~/logic/waila";
import { FeesModal } from "~/components/MoreInfoModal";
export type SendSource = "lightning" | "onchain"; export type SendSource = "lightning" | "onchain";
@@ -56,6 +57,7 @@ type SentDetails = {
destination?: string; destination?: string;
txid?: string; txid?: string;
failure_reason?: string; failure_reason?: string;
fee_estimate?: bigint | number;
}; };
export function MethodChooser(props: { export function MethodChooser(props: {
@@ -504,6 +506,7 @@ export default function Send() {
sentDetails.amount = amountSats(); sentDetails.amount = amountSats();
sentDetails.destination = address(); sentDetails.destination = address();
sentDetails.txid = txid; sentDetails.txid = txid;
sentDetails.fee_estimate = feeEstimate() ?? 0;
} else { } else {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const txid = await state.mutiny_wallet?.send_to_address( const txid = await state.mutiny_wallet?.send_to_address(
@@ -514,6 +517,7 @@ export default function Send() {
sentDetails.amount = amountSats(); sentDetails.amount = amountSats();
sentDetails.destination = address(); sentDetails.destination = address();
sentDetails.txid = txid; sentDetails.txid = txid;
sentDetails.fee_estimate = feeEstimate() ?? 0;
} }
} }
setSentDetails(sentDetails as SentDetails); setSentDetails(sentDetails as SentDetails);
@@ -562,10 +566,7 @@ export default function Send() {
</Show> </Show>
<LargeHeader>{i18n.t("send_bitcoin")}</LargeHeader> <LargeHeader>{i18n.t("send_bitcoin")}</LargeHeader>
<SuccessModal <SuccessModal
title={ confirmText={sentDetails()?.amount ? "Nice" : "Home"}
sentDetails()?.amount ? "Sent" : "Payment Failed"
}
confirmText={sentDetails()?.amount ? "Nice" : "Too Bad"}
open={!!sentDetails()} open={!!sentDetails()}
setOpen={(open: boolean) => { setOpen={(open: boolean) => {
if (!open) setSentDetails(undefined); if (!open) setSentDetails(undefined);
@@ -577,26 +578,47 @@ export default function Send() {
> >
<Switch> <Switch>
<Match when={sentDetails()?.failure_reason}> <Match when={sentDetails()?.failure_reason}>
<img <MegaEx />
src={megaex} <h1 class="w-full mt-4 mb-2 text-2xl font-semibold text-center md:text-3xl">
alt="fail" {sentDetails()?.amount
class="w-1/2 mx-auto max-w-[50vh]" ? "Payment Initiated"
/> : sentDetails()?.failure_reason}
<p class="text-xl font-light py-2 px-4 rounded-xl bg-white/10"> </h1>
{sentDetails()?.failure_reason} {/*TODO: add failure hint logic for different failure conditions*/}
</p>
</Match> </Match>
<Match when={true}> <Match when={true}>
<img <MegaCheck />
src={megacheck} <h1 class="w-full mt-4 mb-2 text-2xl font-semibold text-center md:text-3xl">
alt="success" {sentDetails()?.amount
class="w-1/2 mx-auto max-w-[50vh]" ? "Payment Initiated"
/> : sentDetails()?.failure_reason}
</h1>
<Amount <Amount
amountSats={sentDetails()?.amount} amountSats={sentDetails()?.amount}
showFiat showFiat
centered align="center"
size="large"
icon="minus"
/> />
<hr class="w-16 bg-m-grey-400" />
<div class="flex flex-row items-start gap-3">
<p class="text-m-grey-400 text-sm leading-[17px] text-center">
Fees
</p>
<div class="flex items-start gap-1">
<Amount
amountSats={
sentDetails()?.fee_estimate
}
align="right"
size="small"
showFiat
/>
<div class="flex items-start py-[1px]">
<FeesModal icon />
</div>
</div>
</div>
<Show when={sentDetails()?.txid}> <Show when={sentDetails()?.txid}>
<ExternalLink <ExternalLink
href={mempoolTxUrl( href={mempoolTxUrl(
@@ -636,7 +658,9 @@ export default function Send() {
lnurl={lnurlp()} lnurl={lnurlp()}
clearAll={clearAll} clearAll={clearAll}
/> />
<SmallHeader>{i18n.t("private_tags")}</SmallHeader> <SmallHeader>
{i18n.t("private_tags")}
</SmallHeader>
<TagEditor <TagEditor
selectedValues={selectedContacts()} selectedValues={selectedContacts()}
setSelectedValues={ setSelectedValues={

View File

@@ -26,14 +26,16 @@ import { TextField } from "~/components/layout/TextField";
import { MethodChooser, SendSource } from "~/routes/Send"; import { MethodChooser, SendSource } from "~/routes/Send";
import { useMegaStore } from "~/state/megaStore"; import { useMegaStore } from "~/state/megaStore";
import eify from "~/utils/eify"; import eify from "~/utils/eify";
import megaex from "~/assets/icons/megaex.png"; import { MegaCheck } from "~/components/successfail/MegaCheck";
import megacheck from "~/assets/icons/megacheck.png"; import { MegaEx } from "~/components/successfail/MegaEx";
import { InfoBox } from "~/components/InfoBox"; import { InfoBox } from "~/components/InfoBox";
import { useNavigate } from "solid-start"; import { useNavigate } from "solid-start";
import mempoolTxUrl from "~/utils/mempoolTxUrl"; import mempoolTxUrl from "~/utils/mempoolTxUrl";
import { SuccessModal } from "~/components/successfail/SuccessModal"; import { SuccessModal } from "~/components/successfail/SuccessModal";
import { ExternalLink } from "~/components/layout/ExternalLink"; import { ExternalLink } from "~/components/layout/ExternalLink";
import { Network } from "~/logic/mutinyWalletSetup"; import { Network } from "~/logic/mutinyWalletSetup";
import { useI18n } from "~/i18n/context";
import { AmountFiat } from "~/components/AmountFiat";
const CHANNEL_FEE_ESTIMATE_ADDRESS = const CHANNEL_FEE_ESTIMATE_ADDRESS =
"bc1qf7546vg73ddsjznzq57z3e8jdn6gtw6au576j07kt6d9j7nz8mzsyn6lgf"; "bc1qf7546vg73ddsjznzq57z3e8jdn6gtw6au576j07kt6d9j7nz8mzsyn6lgf";
@@ -50,6 +52,7 @@ type ChannelOpenDetails = {
export default function Swap() { export default function Swap() {
const [state, _actions] = useMegaStore(); const [state, _actions] = useMegaStore();
const navigate = useNavigate(); const navigate = useNavigate();
const i18n = useI18n();
const [source, setSource] = createSignal<SendSource>("onchain"); const [source, setSource] = createSignal<SendSource>("onchain");
const [amountSats, setAmountSats] = createSignal(0n); const [amountSats, setAmountSats] = createSignal(0n);
@@ -265,13 +268,8 @@ export default function Swap() {
<BackLink /> <BackLink />
<LargeHeader>Swap to Lightning</LargeHeader> <LargeHeader>Swap to Lightning</LargeHeader>
<SuccessModal <SuccessModal
title={
channelOpenResult()?.channel
? "Swap Success"
: "Swap Failed"
}
confirmText={ confirmText={
channelOpenResult()?.channel ? "Nice" : "Too Bad" channelOpenResult()?.channel ? "Nice" : "Home"
} }
open={!!channelOpenResult()} open={!!channelOpenResult()}
setOpen={(open: boolean) => { setOpen={(open: boolean) => {
@@ -284,35 +282,37 @@ export default function Swap() {
> >
<Switch> <Switch>
<Match when={channelOpenResult()?.failure_reason}> <Match when={channelOpenResult()?.failure_reason}>
<img <MegaEx />
src={megaex} <h1 class="w-full mt-4 mb-2 text-2xl font-semibold text-center md:text-3xl">
alt="fail" {channelOpenResult()?.failure_reason
class="w-1/2 mx-auto max-w-[30vh] flex-shrink" ? channelOpenResult()?.failure_reason
/> ?.message
: ""}
<p class="text-xl font-light py-2 px-4 rounded-xl bg-white/10"> </h1>
{ {/*TODO: Error hint needs to be added for possible failure reasons*/}
channelOpenResult()?.failure_reason
?.message
}
</p>
</Match> </Match>
<Match when={true}> <Match when={channelOpenResult()?.channel}>
<img <MegaCheck />
src={megacheck} <div class="flex flex-col justify-center">
alt="success" <h1 class="w-full mt-4 mb-2 justify-center text-2xl font-semibold text-center md:text-3xl">
class="w-1/2 mx-auto max-w-[30vh] flex-shrink" Swap Initiated
/> </h1>
<AmountCard <p class="text-xl text-center">
amountSats={ +
channelOpenResult()?.channel?.balance?.toString() || {channelOpenResult()?.channel?.balance.toLocaleString() ??
"" "0"}{" "}
} sats will be added to your Lightning
reserve={ balance
channelOpenResult()?.channel?.reserve?.toString() || </p>
"" <AmountFiat
} amountSats={
/> channelOpenResult()?.channel
?.balance
}
classes="text-sm text-center"
/>
</div>
<hr class="w-16 bg-m-grey-400" />
<Show <Show
when={ when={
channelOpenResult()?.channel?.outpoint channelOpenResult()?.channel?.outpoint
@@ -326,7 +326,7 @@ export default function Swap() {
network network
)} )}
> >
View Transaction {i18n.t("view_transaction")}
</ExternalLink> </ExternalLink>
</Show> </Show>
{/* <pre>{JSON.stringify(channelOpenResult()?.channel?.value, null, 2)}</pre> */} {/* <pre>{JSON.stringify(channelOpenResult()?.channel?.value, null, 2)}</pre> */}

View File

@@ -241,13 +241,11 @@ export default function RestorePage() {
<VStack> <VStack>
<NiceP> <NiceP>
<p> <p>
You can restore an existing Mutiny Wallet from your 12 You can restore an existing Mutiny Wallet from your
word seed phrase. This will replace your existing 12 word seed phrase. This will replace your existing
wallet, so make sure you know what you're doing! wallet, so make sure you know what you're doing!
</p> </p>
<p> <p>Do not use on multiple browsers at the same time.</p>
Do not use on multiple browsers at the same time.
</p>
</NiceP> </NiceP>
<TwelveWordsEntry /> <TwelveWordsEntry />
</VStack> </VStack>
@@ -256,4 +254,3 @@ export default function RestorePage() {
</SafeArea> </SafeArea>
); );
} }

View File

@@ -10,7 +10,6 @@ import NavBar from "~/components/NavBar";
import { A } from "solid-start"; import { A } from "solid-start";
import { For, Show } from "solid-js"; import { For, Show } from "solid-js";
import forward from "~/assets/icons/forward.svg"; import forward from "~/assets/icons/forward.svg";
import { useMegaStore } from "~/state/megaStore";
function SettingsLinkList(props: { function SettingsLinkList(props: {
header: string; header: string;
@@ -58,8 +57,6 @@ function SettingsLinkList(props: {
} }
export default function Settings() { export default function Settings() {
const [state, _actions] = useMegaStore();
return ( return (
<SafeArea> <SafeArea>
<DefaultMain> <DefaultMain>

View File

@@ -25,6 +25,7 @@ module.exports = {
"m-green": "hsla(163, 70%, 38%, 1)", "m-green": "hsla(163, 70%, 38%, 1)",
"m-green-dark": "hsla(163, 70%, 28%, 1)", "m-green-dark": "hsla(163, 70%, 28%, 1)",
"m-blue": "hsla(220, 59%, 52%, 1)", "m-blue": "hsla(220, 59%, 52%, 1)",
"m-blue-400": "hsla(219, 57%, 52%, 1)",
"m-blue-dark": "hsla(220, 59%, 42%, 1)", "m-blue-dark": "hsla(220, 59%, 42%, 1)",
"m-red": "hsla(343, 92%, 54%, 1)", "m-red": "hsla(343, 92%, 54%, 1)",
"m-red-dark": "hsla(343, 92%, 44%, 1)", "m-red-dark": "hsla(343, 92%, 44%, 1)",
@@ -33,7 +34,9 @@ module.exports = {
"m-grey-700": "hsla(0, 0%, 25%, 1)", "m-grey-700": "hsla(0, 0%, 25%, 1)",
"m-grey-750": "hsla(0, 0%, 17%, 1)", "m-grey-750": "hsla(0, 0%, 17%, 1)",
"m-grey-800": "hsla(0, 0%, 12%, 1)", "m-grey-800": "hsla(0, 0%, 12%, 1)",
"m-grey-900": "hsla(0, 0%, 9%, 1)" "m-grey-900": "hsla(0, 0%, 9%, 1)",
"m-grey-750": "hsla(0, 0%, 17%, 1)",
"m-grey-400": "hsla(0, 0%, 64%, 1)"
}, },
backgroundImage: { backgroundImage: {
"fade-to-blue": "fade-to-blue":
@@ -52,7 +55,9 @@ module.exports = {
"inner-button-disabled": "inner-button-disabled":
"2px 2px 4px rgba(0, 0, 0, 0.05), inset 2px 2px 4px rgba(255, 255, 255, 0.05), inset -2px -2px 6px rgba(0, 0, 0, 0.1)", "2px 2px 4px rgba(0, 0, 0, 0.05), inset 2px 2px 4px rgba(255, 255, 255, 0.05), inset -2px -2px 6px rgba(0, 0, 0, 0.1)",
"fancy-card": "0px 4px 4px rgba(0, 0, 0, 0.1)", "fancy-card": "0px 4px 4px rgba(0, 0, 0, 0.1)",
above: "0px -4px 10px rgba(0, 0, 0, 0.25)" "subtle-bevel":
"inset -4px -4px 6px 0 rgba(0, 0, 0, 0.10), inset 4px 4px 4px 0 rgba(255, 255, 255, 0.10)",
above: "0px -4px 10px rgba(0, 0, 0, 0.25)",
}, },
textShadow: { textShadow: {
button: "1px 1px 0px rgba(0, 0, 0, 0.4)" button: "1px 1px 0px rgba(0, 0, 0, 0.4)"