lint extravaganza

This commit is contained in:
Paul Miller
2023-05-23 19:03:52 -05:00
parent 6ee7bd068b
commit c937bcbf9e
36 changed files with 1206 additions and 1216 deletions

View File

@@ -1,8 +1,7 @@
import { LoadingSpinner, NiceP, SmallAmount, SmallHeader } from "./layout"
import { LoadingSpinner, NiceP } from "./layout"
import {
For,
Match,
Show,
Switch,
createEffect,
createMemo,
@@ -11,9 +10,6 @@ import {
} from "solid-js"
import { useMegaStore } from "~/state/megaStore"
import { MutinyInvoice } from "@mutinywallet/mutiny-wasm"
import { JsonModal } from "~/components/JsonModal"
import utxoIcon from "~/assets/icons/coin.svg"
import { getRedshifted } from "~/utils/fakeLabels"
import { ActivityItem } from "./ActivityItem"
import { MutinyTagItem } from "~/utils/tags"
import { Network } from "~/logic/mutinyWalletSetup"
@@ -76,7 +72,7 @@ function OnChainItem(props: {
}
date={props.item.confirmation_time?.Confirmed?.time}
positive={isReceive()}
onClick={() => setOpen(!open())}
onClick={() => setOpen(o => !o)}
/>
</>
)
@@ -96,52 +92,12 @@ function InvoiceItem(props: { item: MutinyInvoice; labels: MutinyTagItem[] }) {
amount={props.item.amount_sats || 0n}
date={props.item.last_updated}
positive={!isSend()}
onClick={() => setOpen(!open())}
onClick={() => setOpen(o => !o)}
/>
</>
)
}
function Utxo(props: { item: UtxoItem }) {
const spent = createMemo(() => props.item.is_spent)
const [open, setOpen] = createSignal(false)
const redshifted = createMemo(() => getRedshifted(props.item.outpoint))
return (
<>
<JsonModal
open={open()}
data={props.item}
title="Unspent Transaction Output"
setOpen={setOpen}
/>
<div class={THREE_COLUMNS} onClick={() => setOpen(!open())}>
<div class="flex items-center">
<img src={utxoIcon} alt="coin" />
</div>
<div class={CENTER_COLUMN}>
<div class="flex gap-2">
<Show
when={redshifted()}
fallback={<h2 class={MISSING_LABEL}>Unknown</h2>}
>
<h2 class={REDSHIFT_LABEL}>Redshift</h2>
</Show>
</div>
<SmallAmount amount={props.item.txout.value} />
</div>
<div class={RIGHT_COLUMN}>
<SmallHeader class={spent() ? "text-m-red" : "text-m-green"}>
{/* {spent() ? "SPENT" : "UNSPENT"} */}
</SmallHeader>
</div>
</div>
</>
)
}
type ActivityItem = {
type: "onchain" | "lightning"
item: OnChainTx | MutinyInvoice

View File

@@ -41,8 +41,8 @@ export const ActivityAmount: ParentComponent<{ amount: string, price: number, po
function LabelCircle(props: { name?: string, contact: boolean }) {
// TODO: don't need to run this if it's not a contact
const [gradient] = createResource(props.name, async (name: string) => {
return generateGradient(name || "?")
const [gradient] = createResource(async () => {
return generateGradient(props.name || "?")
})
const text = () => (props.contact && props.name && props.name.length) ? props.name[0] : (props.name && props.name.length) ? "≡" : "?"

View File

@@ -57,10 +57,6 @@ function add(a: string, b?: string) {
return Number(a || 0) + Number(b || 0);
}
function subtract(a: string, b?: string) {
return Number(a || 0) - Number(b || 0);
}
export function AmountCard(props: {
amountSats: string;
fee?: string;

View File

@@ -18,7 +18,7 @@ function SingleDigitButton(props: { character: string, onClick: (c: string) => v
<Show when={props.fiat || !(props.character === ".")} fallback={<div />}>
<button
disabled={props.character === "."}
class="disabled:opacity-50 p-2 rounded-lg hover:bg-white/10 active:bg-m-blue text-white text-4xl font-semi font-mono"
class="disabled:opacity-50 p-2 rounded-lg md:hover:bg-white/10 active:bg-m-blue text-white text-4xl font-semi font-mono"
onClick={() => props.onClick(props.character)}
>
{props.character}
@@ -148,7 +148,7 @@ export const AmountEditable: ParentComponent<{ initialAmountSats: string, initia
<Dialog.Content class={DIALOG_CONTENT} onEscapeKeyDown={() => setIsOpen(false)}>
{/* TODO: figure out how to submit on enter */}
<div class="w-full flex justify-end">
<button tabindex="-1" onClick={() => setIsOpen(false)} class="hover:bg-white/10 rounded-lg active:bg-m-blue">
<button onClick={() => setIsOpen(false)} class="hover:bg-white/10 rounded-lg active:bg-m-blue w-8 h-8">
<img src={close} alt="Close" />
</button>
</div>

View File

@@ -1,5 +1,5 @@
import logo from '~/assets/icons/mutiny-logo.svg';
import { DefaultMain, SafeArea, VStack, Card, LoadingSpinner } from "~/components/layout";
import { DefaultMain, SafeArea, VStack, Card } from "~/components/layout";
import BalanceBox, { LoadingShimmer } from "~/components/BalanceBox";
import NavBar from "~/components/NavBar";
import ReloadPrompt from "~/components/Reload";
@@ -18,7 +18,7 @@ export default function App() {
<DefaultMain>
<header class="w-full flex justify-between items-center mt-4 mb-2">
<img src={logo} class="h-10" alt="logo" />
<A class="md:hidden p-2 hover:bg-white/5 rounded-lg active:bg-m-blue" href="/activity"><img src={userClock} alt="Activity" /></A>
<A class="md:hidden p-2 hover:bg-white/5 rounded-lg active:bg-m-blue" href="/activity"><img src={userClock} alt="Activity" class="h-8 w-8" /></A>
</header>
<Show when={!state.wallet_loading}>
<OnboardWarning />

View File

@@ -1,5 +1,5 @@
import { Show, Suspense } from "solid-js";
import { Button, ButtonLink, FancyCard, Indicator } from "~/components/layout";
import { Button, FancyCard, Indicator } from "~/components/layout";
import { useMegaStore } from "~/state/megaStore";
import { Amount } from "./Amount";
import { A, useNavigate } from "solid-start";
@@ -15,10 +15,10 @@ function prettyPrintAmount(n?: number | bigint): string {
export function LoadingShimmer() {
return (<div class="flex flex-col gap-2 animate-pulse">
<h1 class="text-4xl font-light">
<div class="w-[12rem] rounded bg-neutral-700 h-[2.5rem]"></div>
<div class="w-[12rem] rounded bg-neutral-700 h-[2.5rem]" />
</h1>
<h2 class="text-xl font-light text-white/70" >
<div class="w-[8rem] rounded bg-neutral-700 h-[1.75rem]"></div>
<div class="w-[8rem] rounded bg-neutral-700 h-[1.75rem]" />
</h2>
</div>)
}
@@ -26,7 +26,7 @@ export function LoadingShimmer() {
const STYLE = "px-2 py-1 rounded-xl border border-neutral-400 text-sm flex gap-2 items-center font-semibold"
export default function BalanceBox(props: { loading?: boolean }) {
const [state, actions] = useMegaStore();
const [state, _actions] = useMegaStore();
const emptyBalance = () => (state.balance?.confirmed || 0n) === 0n && (state.balance?.lightning || 0n) === 0n
@@ -46,7 +46,7 @@ export default function BalanceBox(props: { loading?: boolean }) {
<Amount amountSats={state.balance?.confirmed} showFiat />
<div class="self-end justify-self-end">
<A href="/swap" class={STYLE}>
<img src={shuffle} alt="swap" />
<img src={shuffle} alt="swap" class="h-8 w-8" />
</A>
</div>
</div>

View File

@@ -8,11 +8,10 @@ import {
Switch,
createMemo,
} from "solid-js"
import { Hr, TinyButton, VStack } from "~/components/layout"
import { Hr, ModalCloseButton, TinyButton, VStack } from "~/components/layout"
import { MutinyInvoice } from "@mutinywallet/mutiny-wasm"
import { OnChainTx } from "./Activity"
import close from "~/assets/icons/close.svg"
import bolt from "~/assets/icons/bolt-black.svg"
import chain from "~/assets/icons/chain-black.svg"
import copyIcon from "~/assets/icons/copy.svg"
@@ -27,9 +26,9 @@ import mempoolTxUrl from "~/utils/mempoolTxUrl"
import { Network } from "~/logic/mutinyWalletSetup"
import { AmountSmall } from "./Amount"
const OVERLAY = "fixed inset-0 z-50 bg-black/50 backdrop-blur-sm"
const DIALOG_POSITIONER = "fixed inset-0 z-50 flex items-center justify-center"
const DIALOG_CONTENT =
export const OVERLAY = "fixed inset-0 z-50 bg-black/50 backdrop-blur-sm"
export const DIALOG_POSITIONER = "fixed inset-0 z-50 flex items-center justify-center"
export const DIALOG_CONTENT =
"max-w-[500px] w-[90vw] max-h-[100dvh] overflow-y-scroll disable-scrollbars mx-4 p-4 bg-neutral-800/80 backdrop-blur-md shadow-xl rounded-xl border border-white/10"
function LightningHeader(props: { info: MutinyInvoice }) {
@@ -37,7 +36,7 @@ function LightningHeader(props: { info: MutinyInvoice }) {
const tags = createMemo(() => {
if (props.info.labels.length) {
let contact = state.mutiny_wallet?.get_contact(props.info.labels[0])
const contact = state.mutiny_wallet?.get_contact(props.info.labels[0])
if (contact) {
return [tagToMutinyTag(contact)]
} else {
@@ -64,7 +63,9 @@ function LightningHeader(props: { info: MutinyInvoice }) {
/>
<For each={tags()}>
{(tag) => (
<TinyButton tag={tag} onClick={() => {}}>
<TinyButton tag={tag} onClick={() => {
// noop
}}>
{tag.name}
</TinyButton>
)}
@@ -78,7 +79,7 @@ function OnchainHeader(props: { info: OnChainTx }) {
const tags = createMemo(() => {
if (props.info.labels.length) {
let contact = state.mutiny_wallet?.get_contact(props.info.labels[0])
const contact = state.mutiny_wallet?.get_contact(props.info.labels[0])
if (contact) {
return [tagToMutinyTag(contact)]
} else {
@@ -117,7 +118,9 @@ function OnchainHeader(props: { info: OnChainTx }) {
/>
<For each={tags()}>
{(tag) => (
<TinyButton tag={tag} onClick={() => {}}>
<TinyButton tag={tag} onClick={() => {
// noop
}}>
{tag.name}
</TinyButton>
)}
@@ -250,7 +253,7 @@ export function DetailsModal(props: {
return (
<Dialog.Root
open={props.open}
onOpenChange={(isOpen) => props.setOpen(isOpen)}
onOpenChange={props.setOpen}
>
<Dialog.Portal>
<Dialog.Overlay class={OVERLAY} />
@@ -259,12 +262,7 @@ export function DetailsModal(props: {
<div class="flex justify-between mb-2">
<div />
<Dialog.CloseButton>
<button
tabindex="-1"
class="self-center hover:bg-white/10 rounded-lg active:bg-m-blue "
>
<img src={close} alt="Close" class="w-8 h-8" />
</button>
<ModalCloseButton />
</Dialog.CloseButton>
</div>
<Dialog.Title>

View File

@@ -1,5 +1,5 @@
import { Title } from "solid-start";
import { Button, ButtonLink, DefaultMain, LargeHeader, SafeArea, SmallHeader } from "~/components/layout";
import { Button, DefaultMain, LargeHeader, SafeArea, SmallHeader } from "~/components/layout";
export default function ErrorDisplay(props: { error: Error }) {
return (

View File

@@ -34,14 +34,16 @@ export function ImportExport() {
reject(new Error("No text found in file"));
}
};
fileReader.onerror = e => reject(new Error("File read error"));
fileReader.onerror = _e => reject(new Error("File read error"));
fileReader.readAsText(file, "UTF-8");
});
// This should throw if there's a parse error, so we won't end up clearing
JSON.parse(text);
if (text) {
JSON.parse(text);
MutinyWallet.import_json(text);
}
MutinyWallet.import_json(text);
if (state.mutiny_wallet) {
await state.mutiny_wallet.stop();
}

View File

@@ -1,5 +1,4 @@
import { ParentComponent } from "solid-js";
import { ButtonLink, SmallHeader } from "~/components/layout"
import info from "~/assets/icons/info.svg"
export const InfoBox: ParentComponent<{ accent: "red" | "blue" | "green" | "white" }> = (props) => {

View File

@@ -1,42 +1,36 @@
import { Dialog } from "@kobalte/core";
import { JSX, createMemo } from "solid-js";
import { Button, SmallHeader } from "~/components/layout";
import { useCopy } from "~/utils/useCopy";
import { ModalCloseButton, SmallHeader } from "~/components/layout";
import { DIALOG_CONTENT, DIALOG_POSITIONER, OVERLAY } from "~/components/DetailsModal";
import { CopyButton } from "./ShareCard";
const OVERLAY = "fixed inset-0 z-50 bg-black/50 backdrop-blur-sm"
const DIALOG_POSITIONER = "fixed inset-0 z-50 flex items-center justify-center"
const DIALOG_CONTENT = "max-w-[600px] max-h-full mx-4 p-4 bg-neutral-900/50 backdrop-blur-md shadow-xl rounded-xl border border-white/10"
export function JsonModal(props: { title: string, open: boolean, data?: unknown, setOpen: (open: boolean) => void, children?: JSX.Element }) {
const json = createMemo(() => JSON.stringify(props.data, null, 2));
const [copy, copied] = useCopy({ copiedTimeout: 1000 });
export function JsonModal(props: { title: string, open: boolean, plaintext?: string, data?: unknown, setOpen: (open: boolean) => void, children?: JSX.Element }) {
const json = createMemo(() => props.plaintext ? props.plaintext : JSON.stringify(props.data, null, 2));
return (
<Dialog.Root open={props.open} onOpenChange={(isOpen) => props.setOpen(isOpen)}>
<Dialog.Root open={props.open} onOpenChange={props.setOpen}>
<Dialog.Portal>
<Dialog.Overlay class={OVERLAY} />
<div class={DIALOG_POSITIONER}>
<Dialog.Content class={DIALOG_CONTENT}>
<div class="flex justify-between mb-2">
<div class="flex justify-between mb-2 items-center">
<Dialog.Title>
<SmallHeader>
{props.title}
</SmallHeader>
</Dialog.Title>
<Dialog.CloseButton>
<code>X</code>
<ModalCloseButton />
</Dialog.CloseButton>
</div>
<Dialog.Description class="flex flex-col gap-4">
<div class="bg-white/10 rounded-xl max-h-[50vh] overflow-y-scroll disable-scrollbars p-4">
<Dialog.Description class="flex flex-col gap-4 items-center">
<div class="bg-white/5 rounded-xl max-h-[50vh] overflow-y-scroll disable-scrollbars p-4">
<pre class="whitespace-pre-wrap break-all">
{json()}
</pre>
</div>
{props.children}
<Button onClick={(_) => copy(json() ?? "")}>{copied() ? "Copied" : "Copy"}</Button>
<Button onClick={(_) => props.setOpen(false)}>Close</Button>
<CopyButton title="Copy" text={json()} />
</Dialog.Description>
</Dialog.Content>
</div>

View File

@@ -7,13 +7,15 @@ export default function Scanner(props: { onResult: (result: string) => void }) {
// TODO: not sure it's appropriate to use a signal for this but it works!
const [scanner, setScanner] = createSignal<QrScanner | null>(null);
const handleResult = (result: { data: string }) => {
props.onResult(result.data);
}
onMount(() => {
if (container) {
const newScanner = new QrScanner(
container,
(result: { data: string }) => {
props.onResult(result.data);
},
handleResult,
{
returnDetailedScanResult: true,
}

View File

@@ -2,26 +2,26 @@ import type { Component } from 'solid-js'
import { Show } from 'solid-js'
// eslint-disable-next-line import/no-unresolved
import { useRegisterSW } from 'virtual:pwa-register/solid'
import { Button, Card } from '~/components/layout'
const ReloadPrompt: Component = () => {
const {
offlineReady: [offlineReady, setOfflineReady],
needRefresh: [needRefresh, setNeedRefresh],
updateServiceWorker,
offlineReady: [offlineReady, _setOfflineReady],
needRefresh: [needRefresh, _setNeedRefresh],
updateServiceWorker: _update,
} = useRegisterSW({
onRegistered(r: ServiceWorkerRegistration) {
console.log('SW Registered: ' + r.scope)
immediate: true,
onRegisteredSW(swUrl, r) {
console.log('SW Registered: ' + r?.scope)
},
onRegisterError(error: Error) {
console.log('SW registration error', error)
},
})
const close = () => {
setOfflineReady(false)
setNeedRefresh(false)
}
// const close = () => {
// setOfflineReady(false)
// setNeedRefresh(false)
// }
return (
<Show when={offlineReady() || needRefresh()}>

View File

@@ -1,12 +1,11 @@
import { Button, Card, NiceP, VStack } from "~/components/layout";
import { useMegaStore } from "~/state/megaStore";
import { downloadTextFile } from "~/utils/download";
export function Restart() {
const [state, _] = useMegaStore()
async function handleStop() {
const result = await state.mutiny_wallet?.stop()
await state.mutiny_wallet?.stop()
}
return (

View File

@@ -6,7 +6,7 @@ import eyeIcon from "~/assets/icons/eye.svg"
import { Show, createSignal } from "solid-js";
import { JsonModal } from "./JsonModal";
const STYLE = "px-4 py-2 rounded-xl border-2 border-white flex gap-2 items-center font-semibold"
const STYLE = "px-4 py-2 rounded-xl border-2 border-white flex gap-2 items-center font-semibold hover:text-m-blue transition-colors"
export function ShareButton(props: { receiveString: string }) {
async function share(receiveString: string) {
@@ -34,7 +34,7 @@ export function StringShower(props: { text: string }) {
const [open, setOpen] = createSignal(false);
return (
<>
<JsonModal open={open()} data={props.text} title="Details" setOpen={setOpen} />
<JsonModal open={open()} plaintext={props.text} title="Details" setOpen={setOpen} />
<div class="w-full grid grid-cols-[minmax(0,_1fr)_auto]">
<pre class="truncate text-neutral-400">{props.text}</pre>
<button class="w-[2rem]" onClick={() => setOpen(true)}>

View File

@@ -44,6 +44,11 @@ export function TagEditor(props: {
}
};
// FIXME: eslint is mad about reactivity
const onTagTap = (tag: MutinyTagItem) => {
props.setSelectedValues([...props.selectedValues!, tag]);
};
return (
<div class="flex flex-col gap-2 flex-shrink flex-1" >
<Select
@@ -57,7 +62,8 @@ export function TagEditor(props: {
<Show when={availableTags() && availableTags()!.length > 0}>
<For each={availableTags()!.slice(0, 3)}>
{(tag) => (
<TinyButton tag={tag} onClick={() => props.setSelectedValues([...props.selectedValues!, tag])}>
// eslint-disable-next-line solid/reactivity
<TinyButton tag={tag} onClick={() => onTagTap(tag)}>
{tag.name}
</TinyButton>
)}

View File

@@ -15,8 +15,13 @@ type FullscreenModalProps = {
}
export function FullscreenModal(props: FullscreenModalProps) {
const onNice = () => {
props.onConfirm ? props.onConfirm() : props.setOpen(false)
}
return (
<Dialog.Root open={props.open} onOpenChange={(isOpen) => props.setOpen(isOpen)}>
<Dialog.Root open={props.open} onOpenChange={props.setOpen}>
<Dialog.Portal>
<div class={DIALOG_POSITIONER}>
<Dialog.Content class={DIALOG_CONTENT}>
@@ -34,7 +39,7 @@ export function FullscreenModal(props: FullscreenModalProps) {
{props.children}
</Dialog.Description>
<div class="w-full flex">
<Button onClick={(_) => props.onConfirm ? props.onConfirm() : props.setOpen(false)}>{props.confirmText ?? "Nice"}</Button>
<Button onClick={onNice}>{props.confirmText ?? "Nice"}</Button>
</div>
</Dialog.Content>
</div>

View File

@@ -23,7 +23,7 @@ export default function Linkify(props: LinkifyProps): JSX.Element {
links.push(beforeLink);
}
links.push(<a href={href} target="_blank" rel="noopener noreferrer">{link}</a>);
links.push(<a href={href} class="break-all" target="_blank" rel="noopener noreferrer">{link}</a>);
}
const remainingText = text.slice(lastIndex);

View File

@@ -7,7 +7,7 @@ type Choices = { value: string, label: string, caption: string }[]
export function StyledRadioGroup(props: { value: string, choices: Choices, onValueChange: (value: string) => void, small?: boolean, accent?: "red" | "white" }) {
return (
// TODO: rewrite this with CVA, props are bad for tailwind
<RadioGroup.Root value={props.value} onChange={(e) => props.onValueChange(e)}
<RadioGroup.Root value={props.value} onChange={props.onValueChange}
class={"grid w-full gap-4"}
classList={{ "grid-cols-2": props.choices.length === 2, "grid-cols-3": props.choices.length === 3, "gap-2": props.small }}
>

View File

@@ -1,4 +1,4 @@
import { JSX, ParentComponent, Show, Suspense, createResource, createSignal } from "solid-js"
import { JSX, ParentComponent, Show, Suspense, createResource } from "solid-js"
import Linkify from "./Linkify"
import { Button, ButtonLink } from "./Button"
import { Checkbox as KCheckbox, Separator } from "@kobalte/core"
@@ -6,6 +6,7 @@ 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"
export {
Button,
@@ -128,8 +129,8 @@ export const NiceP: ParentComponent = (props) => {
export const TinyButton: ParentComponent<{ onClick: () => void, tag?: MutinyTagItem }> = (props) => {
// TODO: don't need to run this if it's not a contact
const [gradient] = createResource(props.tag?.name, async (name: string) => {
return generateGradient(name || "?")
const [gradient] = createResource(async () => {
return generateGradient(props.tag?.name || "?")
})
const bg = () => (props.tag?.name && props.tag?.kind === "Contact") ? gradient() : "rgb(255 255 255 / 0.1)"
@@ -161,4 +162,12 @@ export function Checkbox(props: { label: string, checked: boolean, onChange: (ch
<KCheckbox.Label class="flex-1 text-xl font-light">{props.label}</KCheckbox.Label>
</KCheckbox.Root>
)
}
export function ModalCloseButton() {
return (<button
class="self-center justify-self-center hover:bg-white/10 rounded-lg active:bg-m-blue"
>
<img src={close} alt="Close" class="w-8 h-8" />
</button>)
}

View File

@@ -35,12 +35,12 @@ export function WaitlistAlreadyIn() {
const [posts] = createResource("", postsFetcher);
return (
<main class='flex flex-col gap-2 sm:gap-4 py-8 px-4 max-w-xl mx-auto items-start drop-shadow-blue-glow'>
<main class='flex flex-col gap-4 sm:gap-4 py-8 px-4 max-w-xl mx-auto items-start drop-shadow-blue-glow'>
<a href="https://mutinywallet.com">
<img src={logo} class="h-10" alt="logo" />
</a>
<h1 class="text-4xl font-bold">You're on a list!</h1>
<h2 class="text-xl">
<h2 class="text-xl pr-4">
We'll message you when Mutiny Wallet is ready.
</h2>
<div class="px-4 sm:px-8 py-8 rounded-xl bg-half-black w-full">

View File

@@ -19,9 +19,10 @@ function ContactRow() {
const contacts = state.mutiny_wallet?.get_contacts();
console.log(contacts)
let c: Contact[] = []
// FIXME: this is just types shenanigans I believe
const c: Contact[] = []
if (contacts) {
for (let contact in contacts) {
for (const contact in contacts) {
c.push(contacts[contact])
}
}
@@ -37,7 +38,7 @@ function ContactRow() {
}
//
async function saveContact(contact: ContactFormValues) {
async function saveContact(_contact: ContactFormValues) {
showToast(new Error("Unimplemented"))
// await editContact(contact)
refetch();

View File

@@ -1,5 +1,5 @@
import { Contact, MutinyBip21RawMaterials, MutinyInvoice } from "@mutinywallet/mutiny-wasm";
import { createEffect, createMemo, createResource, createSignal, Match, onCleanup, onMount, Show, Switch } from "solid-js";
import { createEffect, createMemo, createResource, createSignal, Match, onCleanup, Show, Switch } from "solid-js";
import { QRCodeSVG } from "solid-qr-code";
import { Button, Card, DefaultMain, Indicator, LargeHeader, MutinyWalletGuard, SafeArea } from "~/components/layout";
import NavBar from "~/components/NavBar";
@@ -51,7 +51,7 @@ type ReceiveState = "edit" | "show" | "paid"
type PaidState = "lightning_paid" | "onchain_paid";
export default function Receive() {
const [state, actions] = useMegaStore()
const [state, _actions] = useMegaStore()
const navigate = useNavigate();
const [amount, setAmount] = createSignal("")

View File

@@ -1,12 +1,10 @@
import {
Component,
createEffect,
createMemo,
createResource,
createSignal,
For,
Match,
onCleanup,
onMount,
ParentComponent,
Show,
@@ -53,8 +51,8 @@ type ShiftStage = "choose" | "observe" | "success" | "failure"
type OutPoint = string // Replace with the actual TypeScript type for OutPoint
type RedshiftStatus = string // Replace with the actual TypeScript type for RedshiftStatus
type RedshiftRecipient = any // Replace with the actual TypeScript type for RedshiftRecipient
type PublicKey = any // Replace with the actual TypeScript type for PublicKey
type RedshiftRecipient = unknown // Replace with the actual TypeScript type for RedshiftRecipient
type PublicKey = unknown // Replace with the actual TypeScript type for PublicKey
interface RedshiftResult {
id: string
@@ -96,52 +94,50 @@ function RedshiftReport(props: { redshift: RedshiftResult; utxo: UtxoItem }) {
return (await state.mutiny_wallet?.list_utxos()) as UtxoItem[]
}
function findUtxoByOutpoint(
outpoint?: string,
utxos: UtxoItem[] = []
): UtxoItem | undefined {
if (!outpoint) return undefined
return utxos.find((utxo) => utxo.outpoint === outpoint)
}
// function findUtxoByOutpoint(
// outpoint?: string,
// utxos: UtxoItem[] = []
// ): UtxoItem | undefined {
// if (!outpoint) return undefined
// return utxos.find((utxo) => utxo.outpoint === outpoint)
// }
const [utxos, { refetch: _refetchUtxos }] = createResource(getUtXos)
const [_utxos, { refetch: _refetchUtxos }] = createResource(getUtXos)
const inputUtxo = createMemo(() => {
console.log(utxos())
const foundUtxo = findUtxoByOutpoint(props.redshift.input_utxo, utxos())
console.log("Found utxo:", foundUtxo)
return foundUtxo
})
// const inputUtxo = createMemo(() => {
// console.log(utxos())
// const foundUtxo = findUtxoByOutpoint(props.redshift.input_utxo, utxos())
// console.log("Found utxo:", foundUtxo)
// return foundUtxo
// })
async function checkRedshift(id: string) {
// const rs = redshiftItems[0] as RedshiftResult;
console.log("Checking redshift", id)
const redshift = await state.mutiny_wallet?.get_redshift(id)
console.log(redshift)
return redshift
}
const [redshiftResource, { refetch: _refetchRedshift }] = createResource(
const [redshiftResource, { refetch }] = createResource(
props.redshift.id,
checkRedshift
async () => {
console.log("Checking redshift", props.redshift.id)
const redshift = await state.mutiny_wallet?.get_redshift(props.redshift.id)
console.log(redshift)
return redshift
}
)
onMount(() => {
const interval = setInterval(() => {
if (redshiftResource()) refetch()
// if (sentAmount() === 200000) {
// clearInterval(interval)
// props.setShiftStage("success");
// // setSentAmount((0))
// const interval = setInterval(() => {
// if (redshiftResource()) refetch()
// // if (sentAmount() === 200000) {
// // clearInterval(interval)
// // props.setShiftStage("success");
// // // setSentAmount((0))
// } else {
// setSentAmount((sentAmount() + 50000))
// }
}, 1000)
// // } else {
// // setSentAmount((sentAmount() + 50000))
// // }
// }, 1000)
})
const outputUtxo = createMemo(() => {
return findUtxoByOutpoint(redshiftResource()?.output_utxo, utxos())
})
// const outputUtxo = createMemo(() => {
// return findUtxoByOutpoint(redshiftResource()?.output_utxo, utxos())
// })
createEffect(() => {
setRedshifted(true, redshiftResource()?.output_utxo)
@@ -170,23 +166,23 @@ function RedshiftReport(props: { redshift: RedshiftResult; utxo: UtxoItem }) {
</Show>
</KV> */}
<KV key="Starting amount">
<Amount amountSats={redshiftResource().amount_sats} />
<Amount amountSats={redshiftResource()!.amount_sats} />
</KV>
<KV key="Fees paid">
<Amount amountSats={redshiftResource().fees_paid} />
<Amount amountSats={redshiftResource()!.fees_paid} />
</KV>
<KV key="Change">
<Amount amountSats={redshiftResource().change_amt} />
<Amount amountSats={redshiftResource()!.change_amt} />
</KV>
<KV key="Outbound channel">
<VStack>
<pre class="whitespace-pre-wrap break-all">
{redshiftResource().introduction_channel}
{redshiftResource()!.introduction_channel}
</pre>
<a
class=""
href={mempoolTxUrl(
redshiftResource().introduction_channel?.split(":")[0],
redshiftResource()!.introduction_channel?.split(":")[0],
network
)}
target="_blank"
@@ -196,16 +192,16 @@ function RedshiftReport(props: { redshift: RedshiftResult; utxo: UtxoItem }) {
</a>
</VStack>
</KV>
<Show when={redshiftResource().output_channel}>
<Show when={redshiftResource()!.output_channel}>
<KV key="Return channel">
<VStack>
<pre class="whitespace-pre-wrap break-all">
{redshiftResource().output_channel}
{redshiftResource()!.output_channel}
</pre>
<a
class=""
href={mempoolTxUrl(
redshiftResource().output_channel?.split(":")[0],
redshiftResource()!.output_channel?.split(":")[0],
network
)}
target="_blank"
@@ -219,7 +215,6 @@ function RedshiftReport(props: { redshift: RedshiftResult; utxo: UtxoItem }) {
</VStack>
</Card>
</Show>
<SmallHeader></SmallHeader>
</VStack>
</VStack>
)
@@ -238,7 +233,7 @@ export function Utxo(props: { item: UtxoItem; onClick?: () => void }) {
const redshifted = createMemo(() => getRedshifted(props.item.outpoint))
return (
<>
<div class={THREE_COLUMNS} onClick={props.onClick}>
<div class={THREE_COLUMNS} onClick={() => props.onClick && props.onClick()}>
<div class="flex items-center">
<img src={utxoIcon} alt="coin" />
</div>
@@ -275,11 +270,11 @@ const FAKE_STATES = [
function ShiftObserver(props: {
setShiftStage: (stage: ShiftStage) => void
redshiftId: String
redshiftId: string
}) {
const [state, _actions] = useMegaStore()
const [_state, _actions] = useMegaStore()
const [fakeStage, setFakeStage] = createSignal(2)
const [fakeStage, _setFakeStage] = createSignal(2)
const [sentAmount, setSentAmount] = createSignal(0)
@@ -295,17 +290,17 @@ function ShiftObserver(props: {
}, 1000)
})
async function checkRedshift(id: string) {
console.log("Checking redshift", id)
const redshift = await state.mutiny_wallet?.get_redshift(id)
console.log(redshift)
return redshift
}
// async function checkRedshift(id: string) {
// console.log("Checking redshift", id)
// const redshift = await state.mutiny_wallet?.get_redshift(id)
// console.log(redshift)
// return redshift
// }
const [redshiftResource, { refetch }] = createResource(
props.redshiftId,
checkRedshift
)
// const [redshiftResource, { refetch }] = createResource(
// props.redshiftId,
// checkRedshift
// )
// onMount(() => {
// const interval = setInterval(() => {
@@ -377,7 +372,7 @@ export default function Redshift() {
}
const [utxos, { refetch: _refetchUtxos }] = createResource(getUtXos)
const [channels, { refetch: _refetchChannels }] = createResource(getChannels)
const [_channels, { refetch: _refetchChannels }] = createResource(getChannels)
const redshiftedUtxos = createMemo(() => {
return utxos()?.filter((utxo) => getRedshifted(utxo.outpoint))
@@ -515,7 +510,7 @@ export default function Redshift() {
<Match when={shiftStage() === "observe" && chosenUtxo()}>
<ShiftObserver
setShiftStage={setShiftStage}
utxo={chosenUtxo()!}
redshiftId="dummy-redshift"
/>
</Match>
<Match when={shiftStage() === "success" && chosenUtxo()}>

View File

@@ -32,6 +32,7 @@ import { StringShower } from "~/components/ShareCard";
import { AmountCard } from "~/components/AmountCard";
import { MutinyTagItem } from "~/utils/tags";
import { BackButton } from "~/components/layout/BackButton";
import { Network } from "~/logic/mutinyWalletSetup";
export type SendSource = "lightning" | "onchain";
@@ -407,6 +408,8 @@ export default function Send() {
const sendButtonDisabled = createMemo(() => {
return !destination() || sending() || amountSats() === 0n;
});
const network = state.mutiny_wallet?.get_network() as Network
return (
<MutinyWalletGuard>
@@ -441,7 +444,7 @@ export default function Send() {
<Amount amountSats={sentDetails()?.amount} showFiat />
<Show when={sentDetails()?.txid}>
<a
href={mempoolTxUrl(sentDetails()?.txid, state.mutiny_wallet?.get_network())}
href={mempoolTxUrl(sentDetails()?.txid, network)}
target="_blank"
rel="noreferrer"
>

View File

@@ -1,32 +1,12 @@
import { Match, Show, Switch } from "solid-js";
import { ActivityItem } from "~/components/ActivityItem";
import { Amount } from "~/components/Amount";
import { AmountCard } from "~/components/AmountCard";
import NavBar from "~/components/NavBar";
import { OnboardWarning } from "~/components/OnboardWarning";
import { ShareCard } from "~/components/ShareCard";
import { Card, DefaultMain, LargeHeader, SafeArea, SmallHeader, VStack } from "~/components/layout";
import { FullscreenModal } from "~/components/layout/FullscreenModal";
import mempoolTxUrl from "~/utils/mempoolTxUrl";
import megaex from "~/assets/icons/megaex.png";
import megacheck from "~/assets/icons/megacheck.png";
import { DefaultMain, LargeHeader, SafeArea, VStack } from "~/components/layout";
const SAMPLE =
"bitcoin:tb1prqm8xtlgme0vmw5s30lgf0a4f5g4mkgsqundwmpu6thrg8zr6uvq2qrhzq?amount=0.001&lightning=lntbs1m1pj9n9xjsp5xgdrmvprtm67p7nq4neparalexlhlmtxx87zx6xeqthsplu842zspp546d6zd2seyaxpapaxx62m88yz3xueqtjmn9v6wj8y56np8weqsxqdqqnp4qdn2hj8tfknpuvdg6tz9yrf3e27ltrx9y58c24jh89lnm43yjwfc5xqrpwjcqpj9qrsgq5sdgh0m3ur5mu5hrmmag4mx9yvy86f83pd0x9ww80kgck6tac3thuzkj0mrtltaxwnlfea95h2re7tj4qsnwzxlvrdmyq2h9mgapnycpppz6k6";
export default function Admin() {
const channelOpenResult = () => {
return {
channel: {
balance: 100000n,
reserve: 1000n,
outpoint: "123:0"
},
failure_reason: undefined
};
};
const setChannelOpenResult = (result: any) => {};
return (
<SafeArea>
<DefaultMain>
@@ -35,64 +15,9 @@ export default function Admin() {
<VStack>
<AmountCard amountSats={"100000"} fee={"69"} />
<ShareCard text={SAMPLE} />
{/* <Card title="Activity">
<ActivityItem kind="lightning" labels={["benthecarman"]} amount={100000} date={1683664966} />
<ActivityItem kind="onchain" labels={["tony"]} amount={42000000} positive date={1683664966} />
<ActivityItem kind="onchain" labels={["a fake name thati is too long"]} amount={42000000} date={1683664966} />
<ActivityItem kind="onchain" labels={["a fake name thati is too long"]} amount={42000000} date={1683664966} />
</Card> */}
<FullscreenModal
title={channelOpenResult()?.channel ? "Channel Opened" : "Channel Open Failed"}
confirmText={channelOpenResult()?.channel ? "Nice" : "Too Bad"}
open={!!channelOpenResult()}
setOpen={(open: boolean) => {
if (!open) setChannelOpenResult(undefined);
}}
onConfirm={() => {
setChannelOpenResult(undefined);
// navigate("/");
}}
>
<div class="flex flex-col items-center gap-8 pb-8">
<Switch>
<Match when={channelOpenResult()?.failure_reason}>
<img src={megaex} alt="fail" class="w-1/2 mx-auto max-w-[30vh] flex-shrink" />
<p class="text-xl font-light py-2 px-4 rounded-xl bg-white/10">
{channelOpenResult()?.failure_reason?.message}
</p>
</Match>
<Match when={true}>
<img
src={megacheck}
alt="success"
class="w-1/2 mx-auto max-w-[30vh] flex-shrink"
/>
<AmountCard
amountSats={channelOpenResult()?.channel?.balance.toString()}
reserve={"1000"}
/>
<Show when={channelOpenResult()?.channel?.outpoint}>
<a
class=""
href={mempoolTxUrl(
channelOpenResult()?.channel?.outpoint?.split(":")[0],
"signet"
)}
target="_blank"
rel="noreferrer"
>
Mempool Link
</a>
</Show>
{/* <pre>{JSON.stringify(channelOpenResult()?.channel?.value, null, 2)}</pre> */}
</Match>
</Switch>
</div>
</FullscreenModal>
</VStack>
</DefaultMain>
<NavBar activeTab="none" />
</SafeArea>
);
}
)
}

View File

@@ -12,7 +12,7 @@ import {
LargeHeader,
MutinyWalletGuard,
SafeArea,
VStack
VStack,
} from "~/components/layout";
import { BackLink } from "~/components/layout/BackLink";
import { TextField } from "~/components/layout/TextField";
@@ -25,7 +25,6 @@ import { InfoBox } from "~/components/InfoBox";
import { FullscreenModal } from "~/components/layout/FullscreenModal";
import { useNavigate } from "solid-start";
import mempoolTxUrl from "~/utils/mempoolTxUrl";
import { Network } from "~/logic/mutinyWalletSetup";
const CHANNEL_FEE_ESTIMATE_ADDRESS =
"bc1qf7546vg73ddsjznzq57z3e8jdn6gtw6au576j07kt6d9j7nz8mzsyn6lgf";
@@ -40,7 +39,7 @@ type ChannelOpenDetails = {
};
export default function Swap() {
const [state, actions] = useMegaStore();
const [state, _actions] = useMegaStore();
const navigate = useNavigate();
const [source, setSource] = createSignal<SendSource>("onchain");
@@ -166,8 +165,6 @@ export default function Swap() {
return undefined;
};
const network = state.mutiny_wallet?.get_network() as Network;
return (
<MutinyWalletGuard>
<SafeArea>

View File

@@ -154,6 +154,7 @@ export const Provider: ParentComponent = (props) => {
// Fetch status from remote on load
onMount(() => {
// eslint-disable-next-line
actions.fetchUserStatus().then(status => {
setState({ user_status: status })
@@ -163,7 +164,7 @@ export const Provider: ParentComponent = (props) => {
actions.setupMutinyWallet().then(() => console.log("node manager setup done"))
// Setup an event listener to stop the mutiny wallet when the page unloads
window.onunload = async (e) => {
window.onunload = async (_e) => {
console.log("stopping mutiny_wallet")
await state.mutiny_wallet?.stop();
console.log("mutiny_wallet stopped")

View File

@@ -1,2 +1,2 @@
export const DIALOG_POSITIONER = "fixed inset-0 h-[100dvh] z-50"
export const DIALOG_CONTENT = "h-[100dvh] flex flex-col justify-between px-4 pt-4 pb-8 bg-neutral-800/80 backdrop-blur-xl"
export const DIALOG_CONTENT = "h-[100dvh] flex flex-col justify-between px-4 pt-4 pb-8 bg-neutral-800/80 backdrop-blur-xl touch-manipulation select-none"

View File

@@ -1,6 +1,6 @@
// https://stackoverflow.com/questions/34156282/how-do-i-save-json-to-local-text-file
export function downloadTextFile(content: string, fileName: string, type: string) {
export function downloadTextFile(content: string, fileName: string, type?: string) {
const contentType = type ? type : "application/json";
const a = document.createElement("a");
const file = new Blob([content], { type: contentType });

View File

@@ -1,4 +1,4 @@
import { Contact, TagItem } from "@mutinywallet/mutiny-wasm"
import { TagItem } from "@mutinywallet/mutiny-wasm"
export type MutinyTagItem = {
id: string,
@@ -20,8 +20,7 @@ export function tagsToIds(tags?: MutinyTagItem[]): string[] {
}
export function tagToMutinyTag(tag: TagItem): MutinyTagItem {
// @ts-ignore
// FIXME: make typescript less mad about this
// @ts-expect-error: FIXME: make typescript less mad about this
return tag as MutinyTagItem
}