From cdc7c880fa5fa4cdaf52bd7c0471cf8c817de2c5 Mon Sep 17 00:00:00 2001 From: benthecarman Date: Fri, 22 Mar 2024 16:08:05 -0500 Subject: [PATCH 01/20] Always pass along lnurl comment --- src/routes/Send.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/routes/Send.tsx b/src/routes/Send.tsx index 0cf91bde..94dab36f 100644 --- a/src/routes/Send.tsx +++ b/src/routes/Send.tsx @@ -509,13 +509,12 @@ export function Send() { visibility() !== "Not Available" && contact()?.npub ? contact()?.npub : undefined; - const comment = zapNpub ? whatForInput() : undefined; const payment = await state.mutiny_wallet?.lnurl_pay( lnurlp()!, amountSats(), zapNpub, // zap_npub tags, - comment, // comment + whatForInput(), // comment visibility() ); sentDetails.payment_hash = payment?.payment_hash; From 0dbcdc568bbaedb4231180c56219f61949f384dc Mon Sep 17 00:00:00 2001 From: benthecarman Date: Tue, 9 Apr 2024 15:26:41 -0500 Subject: [PATCH 02/20] Try each federation guardian when joining --- src/logic/errorDispatch.ts | 6 ++++ src/routes/settings/ManageFederations.tsx | 37 ++++++++++++++++++----- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/logic/errorDispatch.ts b/src/logic/errorDispatch.ts index f9002cdc..e8eae7ae 100644 --- a/src/logic/errorDispatch.ts +++ b/src/logic/errorDispatch.ts @@ -44,6 +44,7 @@ type MutinyError = | "Failed to read or write json from the front end" | "The given node pubkey is invalid." | "Failed to get nostr data." + | "Error with NIP-07 extension" | "Failed to get the bitcoin price." | "Satoshi amount is invalid" | "Failed to execute a dlc function" @@ -54,6 +55,11 @@ type MutinyError = | "Failed to create payjoin request." | "Payjoin response error: {0}" | "Payjoin configuration failed." + | "Error calling Cashu Mint" + | "Mint URL in token is empty" + | "Token has been already spent." + | "A federation is required" + | "Failed to connect to a federation." | "Unknown Error"; export function matchError(e: unknown): Error { diff --git a/src/routes/settings/ManageFederations.tsx b/src/routes/settings/ManageFederations.tsx index ec2b1d66..23d0a964 100644 --- a/src/routes/settings/ManageFederations.tsx +++ b/src/routes/settings/ManageFederations.tsx @@ -110,7 +110,7 @@ function AddFederationForm(props: { refetch?: RefetchType }) { f: FederationForm ) => { const federation_code = f.federation_code.trim(); - await onSelect(federation_code); + await onSelect([federation_code]); }; const [federations] = createResource(async () => { @@ -124,14 +124,36 @@ function AddFederationForm(props: { refetch?: RefetchType }) { } }); - const onSelect = async (inviteCode: string) => { + const onSelect = async (inviteCodes: string[]) => { setSuccess(""); setError(undefined); - setLoadingFederation(inviteCode); try { - const newFederation = - await state.mutiny_wallet?.new_federation(inviteCode); - console.log("New federation added:", newFederation); + for (const inviteCode of inviteCodes) { + try { + console.log("Adding federation:", inviteCode); + setLoadingFederation(inviteCode); + const newFederation = + await state.mutiny_wallet?.new_federation(inviteCode); + console.log("New federation added:", newFederation); + break; + } catch (e) { + const error = eify(e); + console.log("Error adding federation:", error.message); + // if we can't connect to the guardian, try to others, + // otherwise throw the error + if ( + error.message === + "Failed to connect to a federation." || + error.message === "Invalid Arguments were given" + ) { + console.error( + "Failed to connect to guardian, trying another one" + ); + } else { + throw e; + } + } + } setSuccess( i18n.t("settings.manage_federations.federation_added_success") ); @@ -275,11 +297,10 @@ function AddFederationForm(props: { refetch?: RefetchType }) { - {/* FIXME: do something smarter than just taking first code */} - - {error()?.message} - - - {success()} - - - - - {i18n.t("settings.manage_federations.discover")} - + ]} + > + {(field, props) => ( + + )} + + + + {error()?.message} + + + {success()} + + + + + {i18n.t("settings.manage_federations.discover")} + + + -
- -
+ + +
@@ -247,13 +260,17 @@ function AddFederationForm(props: { refetch?: RefetchType }) { - - - + + + + + {" "} - {/* */} {i18n.t("settings.manage_federations.title")} {i18n.t("settings.manage_federations.description")}{" "} + + {i18n.t("settings.manage_federations.descriptionpart2")}{" "} + + + + {i18n.t("settings.manage_federations.learn_more")} + + diff --git a/src/routes/setup/AddFederation.tsx b/src/routes/setup/AddFederation.tsx new file mode 100644 index 00000000..3e2460e7 --- /dev/null +++ b/src/routes/setup/AddFederation.tsx @@ -0,0 +1,65 @@ +import { useNavigate } from "@solidjs/router"; +import { createSignal, Suspense } from "solid-js"; + +import { + Button, + ConfirmDialog, + DefaultMain, + ExternalLink, + MutinyWalletGuard, + VStack +} from "~/components"; +import { useI18n } from "~/i18n/context"; + +import { AddFederationForm } from "../settings"; + +export function AddFederation() { + const i18n = useI18n(); + const navigate = useNavigate(); + + const [confirmOpen, setConfirmOpen] = createSignal(false); + + async function handleSkip() { + navigate("/"); + } + + return ( + + +
+

+ {i18n.t("setup.federation.pick")} +

+

+ {i18n.t("settings.manage_federations.description")} +

+

+ {i18n.t("settings.manage_federations.descriptionpart2")} +

+ + + setConfirmOpen(false)} + > + {i18n.t("setup.federation.skip_confirm")} + + + + + + +

+ + {i18n.t("settings.manage_federations.learn_more")} + +

+
+
+
+ ); +} diff --git a/src/routes/setup/NewProfile.tsx b/src/routes/setup/NewProfile.tsx index 3850ec7c..7c554a4c 100644 --- a/src/routes/setup/NewProfile.tsx +++ b/src/routes/setup/NewProfile.tsx @@ -31,7 +31,7 @@ export function NewProfile() { ); console.log("profile", profile); localStorage.setItem("profile_setup_stage", "skipped"); - navigate("/"); + navigate("/addfederation"); setSkipping(false); } @@ -46,7 +46,7 @@ export function NewProfile() { ); console.log("profile", profile); localStorage.setItem("profile_setup_stage", "saved"); - navigate("/"); + navigate("/addfederation"); } catch (e) { console.error(e); } @@ -57,7 +57,9 @@ export function NewProfile() {
-

Create your profile

+

+ {i18n.t("setup.new_profile.title")} +

{i18n.t("setup.new_profile.description")}

diff --git a/src/routes/setup/index.ts b/src/routes/setup/index.ts index 577594d3..81866c13 100644 --- a/src/routes/setup/index.ts +++ b/src/routes/setup/index.ts @@ -2,3 +2,4 @@ export * from "./Restore"; export * from "./ImportProfile"; export * from "./NewProfile"; export * from "./Root"; +export * from "./AddFederation"; From 99f461c99e2dafe2aa52c88045356455bddb3136 Mon Sep 17 00:00:00 2001 From: Paul Miller Date: Fri, 12 Apr 2024 10:52:59 -0500 Subject: [PATCH 08/20] default to private zap --- src/routes/Send.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/routes/Send.tsx b/src/routes/Send.tsx index 94dab36f..28c0ebab 100644 --- a/src/routes/Send.tsx +++ b/src/routes/Send.tsx @@ -647,6 +647,11 @@ export function Send() { const [visibility, setVisibility] = createSignal("Not Available"); + // If the contact has an npub set the default visibility to private zap + createEffect(() => { + contact()?.npub && setVisibility("Private"); + }); + function toggleVisibility() { if (visibility() === "Not Available") { setVisibility("Private"); From 2ce63c8a6e9d8d0c83cc3bcbc962fb1e6c1c06de Mon Sep 17 00:00:00 2001 From: Paul Miller Date: Fri, 12 Apr 2024 14:16:10 -0500 Subject: [PATCH 09/20] fix style typo --- src/components/layout/Misc.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/layout/Misc.tsx b/src/components/layout/Misc.tsx index 1a260e73..59e5bb79 100644 --- a/src/components/layout/Misc.tsx +++ b/src/components/layout/Misc.tsx @@ -138,7 +138,7 @@ export const DefaultMain = (props: { children?: JSX.Element }) => { <> {/* blur content that goes under the notification bar */}
-
+
{props.children} From 2f027bc82eeca513981e7de05519724a5938be7e Mon Sep 17 00:00:00 2001 From: Paul Miller Date: Fri, 12 Apr 2024 14:12:00 -0500 Subject: [PATCH 10/20] allow text selection in dms --- src/routes/Chat.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/routes/Chat.tsx b/src/routes/Chat.tsx index 1af48816..1576c21e 100644 --- a/src/routes/Chat.tsx +++ b/src/routes/Chat.tsx @@ -179,7 +179,9 @@ function SingleMessage(props: {
-

{props.dm.message}

+

+ {props.dm.message} +

- + + + )} @@ -460,15 +465,15 @@ function FederationListItem(props: { > + + + -
- -
@@ -580,6 +585,11 @@ export function ManageFederations() { + + + + + From 7480461ac286adbc979bc3c7e8d5eeebb36e8a84 Mon Sep 17 00:00:00 2001 From: Paul Miller Date: Thu, 11 Apr 2024 10:32:42 -0500 Subject: [PATCH 14/20] don't warn about 100k if using LSPS --- src/components/ReceiveWarnings.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/ReceiveWarnings.tsx b/src/components/ReceiveWarnings.tsx index 746051f1..3ba75d40 100644 --- a/src/components/ReceiveWarnings.tsx +++ b/src/components/ReceiveWarnings.tsx @@ -32,7 +32,10 @@ export function ReceiveWarnings(props: { if (state.federations?.length !== 0 && props.from_fedi_to_ln !== true) { return undefined; } - if ((state.balance?.lightning || 0n) === 0n) { + if ( + (state.balance?.lightning || 0n) === 0n && + !state.settings?.lsps_connection_string + ) { return i18n.t("receive.amount_editable.receive_too_small", { amount: "100,000" }); From b00d6340667384f462513be4005d0fc1052fc42f Mon Sep 17 00:00:00 2001 From: benthecarman Date: Wed, 10 Apr 2024 12:50:03 -0500 Subject: [PATCH 15/20] Unrecommend federations --- src/routes/settings/ManageFederations.tsx | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/routes/settings/ManageFederations.tsx b/src/routes/settings/ManageFederations.tsx index b665f7a7..1deeb741 100644 --- a/src/routes/settings/ManageFederations.tsx +++ b/src/routes/settings/ManageFederations.tsx @@ -378,6 +378,20 @@ function RecommendButton(props: { fed: MutinyFederationIdentity }) { setRecommendLoading(false); } + async function deleteRecommendation() { + setRecommendLoading(true); + try { + await state.mutiny_wallet?.delete_federation_recommendation( + props.fed.federation_id + ); + setRecommended(false); + refetch(); + } catch (e) { + console.error("Error deleting federation recommendation: ", e); + } + setRecommendLoading(false); + } + return ( @@ -385,6 +399,13 @@ function RecommendButton(props: { fed: MutinyFederationIdentity }) { Recommended by you

+
+ ); +} diff --git a/src/components/layout/index.ts b/src/components/layout/index.ts index 739257d4..55d58f7a 100644 --- a/src/components/layout/index.ts +++ b/src/components/layout/index.ts @@ -6,3 +6,4 @@ export * from "./Radio"; export * from "./TextField"; export * from "./ExternalLink"; export * from "./LoadingSpinner"; +export * from "./SubtleButton"; diff --git a/src/routes/settings/ManageFederations.tsx b/src/routes/settings/ManageFederations.tsx index 1deeb741..7ef73d0f 100644 --- a/src/routes/settings/ManageFederations.tsx +++ b/src/routes/settings/ManageFederations.tsx @@ -7,7 +7,7 @@ import { } from "@modular-forms/solid"; import { FederationBalance, TagItem } from "@mutinywallet/mutiny-wasm"; import { A, useNavigate, useSearchParams } from "@solidjs/router"; -import { BadgeCheck, Scan } from "lucide-solid"; +import { BadgeCheck, LogOut, Scan, Trash } from "lucide-solid"; import { createResource, createSignal, @@ -39,6 +39,7 @@ import { NavBar, NiceP, showToast, + SubtleButton, TextField, VStack } from "~/components"; @@ -345,6 +346,7 @@ export function AddFederationForm(props: { function RecommendButton(props: { fed: MutinyFederationIdentity }) { const [state] = useMegaStore(); + const i18n = useI18n(); const [recommendLoading, setRecommendLoading] = createSignal(false); // This is just some local state that makes it feel like they've recommended it // even if they aren't a "real person" @@ -395,26 +397,29 @@ function RecommendButton(props: { fed: MutinyFederationIdentity }) { return ( -

- - Recommended by you -

- +
+
+ + {i18n.t( + "settings.manage_federations.recommended_by_you" + )} +
+ + + +
- + + {i18n.t("settings.manage_federations.recommend")} +
); @@ -495,9 +500,10 @@ function FederationListItem(props: { - + Date: Fri, 12 Apr 2024 14:55:15 -0500 Subject: [PATCH 17/20] handle invoices inside of message --- src/routes/Chat.tsx | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/routes/Chat.tsx b/src/routes/Chat.tsx index 1576c21e..aab1b87a 100644 --- a/src/routes/Chat.tsx +++ b/src/routes/Chat.tsx @@ -76,9 +76,20 @@ function SingleMessage(props: { const parsed = createAsync( async () => { - const result = toParsedParams(props.dm.message, network); + let result = undefined; + + // Look for a long word that might be an invoice + const split_message_by_whitespace = props.dm.message.split(/\s+/g); + for (const word of split_message_by_whitespace) { + if (word.length > 15) { + result = toParsedParams(word, network); + if (result.ok) { + break; + } + } + } - if (!result.ok) { + if (!result || !result.ok) { return undefined; } @@ -92,6 +103,10 @@ function SingleMessage(props: { return { type: "invoice", status: "paid", + message_without_invoice: props.dm.message.replace( + result.value.original, + "" + ), value: result.value.invoice, amount: result.value.amount_sats }; @@ -103,6 +118,10 @@ function SingleMessage(props: { return { type: "invoice", status: "unpaid", + message_without_invoice: props.dm.message.replace( + result.value.original, + "" + ), from: props.dm.from, value: result.value.invoice, amount: result.value.amount_sats @@ -130,9 +149,9 @@ function SingleMessage(props: { navWithContactId(); } - function handlePay() { + function handlePay(invoice: string) { actions.handleIncomingString( - props.dm.message, + invoice, (error) => { showToast(error); }, @@ -153,6 +172,11 @@ function SingleMessage(props: {
+ +

+ {parsed()?.message_without_invoice} +

+
Lightning Invoice @@ -167,7 +191,7 @@ function SingleMessage(props: { From e137e5eccdf22f745309bb60763d8306888658e6 Mon Sep 17 00:00:00 2001 From: Paul Miller Date: Fri, 12 Apr 2024 16:45:41 -0500 Subject: [PATCH 18/20] fix private zap button logic --- src/routes/Send.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/Send.tsx b/src/routes/Send.tsx index 28c0ebab..e73f4c43 100644 --- a/src/routes/Send.tsx +++ b/src/routes/Send.tsx @@ -647,9 +647,9 @@ export function Send() { const [visibility, setVisibility] = createSignal("Not Available"); - // If the contact has an npub set the default visibility to private zap + // If the contact has an npub and it's an lnurlp send set the default visibility to private zap createEffect(() => { - contact()?.npub && setVisibility("Private"); + contact()?.npub && lnurlp() && setVisibility("Private"); }); function toggleVisibility() { From 437adf7e73bf51e5b3f0661e394f37fd4345ca06 Mon Sep 17 00:00:00 2001 From: Paul Miller Date: Fri, 12 Apr 2024 16:53:39 -0500 Subject: [PATCH 19/20] just commands for switching network --- justfile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/justfile b/justfile index 70615194..efb2462f 100644 --- a/justfile +++ b/justfile @@ -20,3 +20,9 @@ test: test-ui: pnpm exec playwright test --ui + +mainnet: + cp .env.mainnet .env.local + +signet: + cp .env.signet .env.local From 15926619c7804ff2241d6af00713a1f670ff6a03 Mon Sep 17 00:00:00 2001 From: Paul Miller Date: Fri, 12 Apr 2024 18:31:34 -0500 Subject: [PATCH 20/20] fix width overflow for federation list --- src/routes/settings/ManageFederations.tsx | 4 ++-- src/routes/setup/AddFederation.tsx | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/routes/settings/ManageFederations.tsx b/src/routes/settings/ManageFederations.tsx index 7ef73d0f..cf3133ec 100644 --- a/src/routes/settings/ManageFederations.tsx +++ b/src/routes/settings/ManageFederations.tsx @@ -180,7 +180,7 @@ export function AddFederationForm(props: { }; return ( - <> +
{i18n.t("settings.manage_federations.manual")} @@ -340,7 +340,7 @@ export function AddFederationForm(props: { - +
); } diff --git a/src/routes/setup/AddFederation.tsx b/src/routes/setup/AddFederation.tsx index 3e2460e7..20867ae3 100644 --- a/src/routes/setup/AddFederation.tsx +++ b/src/routes/setup/AddFederation.tsx @@ -48,11 +48,9 @@ export function AddFederation() { > {i18n.t("setup.federation.skip_confirm")} - - - - - + + +

{i18n.t("settings.manage_federations.learn_more")}