multi nwc

This commit is contained in:
Paul Miller
2023-06-29 14:24:18 -05:00
parent f858c2c856
commit 7aea08c36c
5 changed files with 197 additions and 39 deletions

View File

@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="icons">
<path id="Vector" d="M7 10L12 15L17 10" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 250 B

View File

@@ -149,10 +149,10 @@ function OnchainHeader(props: {
);
}
const KeyValue: ParentComponent<{ key: string }> = (props) => {
export const KeyValue: ParentComponent<{ key: string }> = (props) => {
return (
<li class="flex justify-between items-center gap-4">
<span class="uppercase font-semibold whitespace-nowrap">
<span class="uppercase font-semibold whitespace-nowrap text-sm">
{props.key}
</span>
<span class="font-light">{props.children}</span>

View File

@@ -1,20 +1,23 @@
import {
JSX,
Match,
ParentComponent,
Show,
Suspense,
Switch,
createResource,
createSignal
} from "solid-js";
import Linkify from "./Linkify";
import { Button, ButtonLink } from "./Button";
import { Dialog, Checkbox as KCheckbox, Separator } from "@kobalte/core";
import { Collapsible, Checkbox as KCheckbox, Dialog, Separator } from "@kobalte/core";
import { useMegaStore } from "~/state/megaStore";
import check from "~/assets/icons/check.svg";
import { MutinyTagItem } from "~/utils/tags";
import { generateGradient } from "~/utils/gradientHash";
import close from "~/assets/icons/close.svg";
import { A } from "solid-start";
import down from "~/assets/icons/down.svg";
export { Button, ButtonLink, Linkify };
@@ -75,6 +78,39 @@ export const SettingsCard: ParentComponent<{
);
};
export const Collapser: ParentComponent<{
title: string;
defaultOpen?: boolean;
activityLight?: "on" | "off";
}> = (props) => {
return (
<Collapsible.Root class="collapsible">
<Collapsible.Trigger class="flex w-full justify-between py-2 hover:bg-m-grey-750 active:bg-m-grey-900 px-4">
<div class="flex items-center gap-2">
<Switch>
<Match when={props.activityLight === "on"}>
<div class="w-2 h-2 rounded-full bg-m-green" />
</Match>
<Match when={props.activityLight === "off"}>
<div class="w-2 h-2 rounded-full bg-m-red" />
</Match>
</Switch>
<span>{props.title}</span>
</div>
<img
src={down}
alt="expand / collapse"
class="collapsible__trigger-icon"
/>
</Collapsible.Trigger>
<Collapsible.Content class="p-4 bg-m-grey-900 shadow-inner">
{props.children}
</Collapsible.Content>
</Collapsible.Root>
);
};
export const SafeArea: ParentComponent = (props) => {
return (
<div class="h-[100dvh] safe-left safe-right">

View File

@@ -1,39 +1,171 @@
import { Show, createMemo } from "solid-js";
import { NwcProfile } from "@mutinywallet/mutiny-wasm";
import { For, Show, createResource, createSignal } from "solid-js";
import { QRCodeSVG } from "solid-qr-code";
import { KeyValue, MiniStringShower } from "~/components/DetailsModal";
import { InfoBox } from "~/components/InfoBox";
import NavBar from "~/components/NavBar";
import { ShareCard } from "~/components/ShareCard";
import {
Button,
Collapser,
DefaultMain,
LargeHeader,
MutinyWalletGuard,
NiceP,
SafeArea
SafeArea,
SettingsCard,
SimpleDialog,
VStack
} from "~/components/layout";
import { BackLink } from "~/components/layout/BackLink";
import { TextField } from "~/components/layout/TextField";
import { useMegaStore } from "~/state/megaStore";
import eify from "~/utils/eify";
export default function Connections() {
const [state, actions] = useMegaStore();
function Nwc() {
const [state, _actions] = useMegaStore();
const connectionURI = createMemo(() => {
if (state.nwc_enabled) {
return state.mutiny_wallet?.get_nwc_uri();
const [nwcProfiles, { refetch }] = createResource(async () => {
try {
const profiles: NwcProfile[] =
await state.mutiny_wallet?.get_nwc_profiles();
console.log("profiles:", profiles);
return profiles;
} catch (e) {
console.error(e);
}
});
const toggleNwc = async () => {
if (state.nwc_enabled) {
actions.setNwc(false);
window.location.reload();
} else {
actions.setNwc(true);
const nodes = await state.mutiny_wallet?.list_nodes();
const firstNode = (nodes[0] as string) || "";
await state.mutiny_wallet?.start_nostr_wallet_connect(firstNode);
}
};
const [dialogOpen, setDialogOpen] = createSignal(false);
const [formName, setFormName] = createSignal("");
const [createLoading, setCreateLoading] = createSignal(false);
const [error, setError] = createSignal("");
async function createConnection() {
try {
setError("");
setCreateLoading(true);
if (formName() === "") {
setError("Name cannot be empty");
return;
}
const profile = await state.mutiny_wallet?.create_nwc_profile(
formName(),
10000n
);
if (!profile) {
setError("Failed to create Wallet Connection");
return;
} else {
refetch();
}
setFormName("");
setDialogOpen(false);
} catch (e) {
setError(eify(e).message);
console.error(e);
} finally {
setCreateLoading(false);
}
}
async function toggleEnabled(profile: NwcProfile) {
try {
await state.mutiny_wallet?.edit_nwc_profile({
...profile,
enabled: !profile.enabled
});
refetch();
} catch (e) {
console.error(e);
}
}
// this is because the TextField component has a bunch of required props
function noop() {
// do nothing
return;
}
return (
<VStack biggap>
<Button intent="blue" onClick={() => setDialogOpen(true)}>
Add Connection
</Button>
<Show when={nwcProfiles() && nwcProfiles()!.length > 0}>
<SettingsCard title="Manage Connections">
<For each={nwcProfiles()}>
{(profile) => (
<Collapser
title={profile.name}
activityLight={profile.enabled ? "on" : "off"}
>
<VStack>
<KeyValue key="Relay">
<MiniStringShower
text={profile.relay}
/>
</KeyValue>
<div class="w-full bg-white rounded-xl">
<QRCodeSVG
value={profile.nwc_uri}
class="w-full h-full p-8 max-h-[320px]"
/>
</div>
<ShareCard text={profile.nwc_uri || ""} />
<Button
layout="small"
onClick={() => toggleEnabled(profile)}
>
{profile.enabled ? "Disable" : "Enable"}
</Button>
</VStack>
</Collapser>
)}
</For>
</SettingsCard>
</Show>
<SimpleDialog
open={dialogOpen()}
setOpen={setDialogOpen}
title="New Connection"
>
<div class="flex flex-col gap-4 py-4">
<TextField
name="name"
label="Name"
ref={noop}
value={formName()}
onInput={(e) => setFormName(e.currentTarget.value)}
error={""}
onBlur={noop}
onChange={noop}
placeholder="My favorite nostr client..."
/>
<Show when={error()}>
<InfoBox accent="red">{error()}</InfoBox>
</Show>
</div>
<Button
intent="blue"
loading={createLoading()}
onClick={createConnection}
>
Create Connection
</Button>
</SimpleDialog>
</VStack>
);
}
export default function Connections() {
return (
<MutinyWalletGuard>
<SafeArea>
@@ -41,24 +173,10 @@ export default function Connections() {
<BackLink href="/settings" title="Settings" />
<LargeHeader>Wallet Connections</LargeHeader>
<NiceP>
Authorize Mutiny Wallet with external services like
Nostr clients.
Authorize external services to request payments from
your wallet. Pairs great with Nostr clients.
</NiceP>
<Button onClick={toggleNwc}>
{state.nwc_enabled
? "Disable Nostr Wallet Connect"
: "Enable Nostr Wallet Connect"}
</Button>
<Show when={connectionURI() && state.nwc_enabled}>
<div class="w-full bg-white rounded-xl">
<QRCodeSVG
value={connectionURI() || ""}
class="w-full h-full p-8 max-h-[400px]"
/>
</div>
<ShareCard text={connectionURI() || ""} />
</Show>
<Nwc />
<div class="h-full" />
</DefaultMain>
<NavBar activeTab="settings" />

View File

@@ -56,7 +56,6 @@ export type MegaStore = [
dismissRestorePrompt(): void;
setHasBackedUp(): void;
listTags(): Promise<MutinyTagItem[]>;
setNwc(enabled: boolean): void;
syncActivity(): Promise<void>;
checkBrowserCompat(): Promise<boolean>;
}