mirror of
https://github.com/aljazceru/mutiny-web.git
synced 2025-12-26 18:34:26 +01:00
redshift optimistic ui
This commit is contained in:
@@ -1,17 +1,19 @@
|
||||
import send from '~/assets/icons/send.svg';
|
||||
import receive from '~/assets/icons/receive.svg';
|
||||
import { Card, LoadingSpinner, SmallAmount, SmallHeader, VStack } from './layout';
|
||||
import { ButtonLink, Card, LoadingSpinner, SmallAmount, SmallHeader, VStack } from './layout';
|
||||
import { For, Match, ParentComponent, Suspense, Switch, createMemo, createResource, createSignal } from 'solid-js';
|
||||
import { useMegaStore } from '~/state/megaStore';
|
||||
import { MutinyInvoice } from '@mutinywallet/mutiny-wasm';
|
||||
import { prettyPrintTime } from '~/utils/prettyPrintTime';
|
||||
import { JsonModal } from '~/components/JsonModal';
|
||||
import mempoolTxUrl from '~/utils/mempoolTxUrl';
|
||||
import wave from "~/assets/wave.gif"
|
||||
|
||||
const THREE_COLUMNS = 'grid grid-cols-[auto,1fr,auto] gap-4 py-2 px-2 border-b border-neutral-800 last:border-b-0'
|
||||
const CENTER_COLUMN = 'min-w-0 overflow-hidden max-w-full'
|
||||
const MISSING_LABEL = 'py-1 px-2 bg-white/10 rounded inline-block text-sm'
|
||||
const RIGHT_COLUMN = 'flex flex-col items-right text-right max-w-[8rem]'
|
||||
export const THREE_COLUMNS = 'grid grid-cols-[auto,1fr,auto] gap-4 py-2 px-2 border-b border-neutral-800 last:border-b-0'
|
||||
export const CENTER_COLUMN = 'min-w-0 overflow-hidden max-w-full'
|
||||
export const MISSING_LABEL = 'py-1 px-2 bg-white/10 rounded inline-block text-sm'
|
||||
export const REDSHIFT_LABEL = 'py-1 px-2 bg-white text-m-red rounded inline-block text-sm'
|
||||
export const RIGHT_COLUMN = 'flex flex-col items-right text-right max-w-[8rem]'
|
||||
|
||||
type OnChainTx = {
|
||||
txid: string
|
||||
@@ -26,14 +28,15 @@ type OnChainTx = {
|
||||
}
|
||||
}
|
||||
|
||||
type Utxo = {
|
||||
export type UtxoItem = {
|
||||
outpoint: string
|
||||
txout: {
|
||||
value: number
|
||||
script_pubkey: string
|
||||
}
|
||||
keychain: string
|
||||
is_spent: boolean
|
||||
is_spent: boolean,
|
||||
redshifted?: boolean
|
||||
}
|
||||
|
||||
const SubtleText: ParentComponent = (props) => {
|
||||
@@ -95,7 +98,7 @@ function InvoiceItem(props: { item: MutinyInvoice }) {
|
||||
)
|
||||
}
|
||||
|
||||
function Utxo(props: { item: Utxo }) {
|
||||
function Utxo(props: { item: UtxoItem }) {
|
||||
const spent = createMemo(() => props.item.is_spent);
|
||||
|
||||
const [open, setOpen] = createSignal(false)
|
||||
@@ -136,7 +139,7 @@ export function Activity() {
|
||||
|
||||
const getUtXos = async () => {
|
||||
console.log("Getting utxos");
|
||||
const utxos = await state.node_manager?.list_utxos() as Utxo[];
|
||||
const utxos = await state.node_manager?.list_utxos() as UtxoItem[];
|
||||
return utxos;
|
||||
}
|
||||
|
||||
@@ -197,6 +200,7 @@ export function Activity() {
|
||||
</For>
|
||||
</Match>
|
||||
</Switch>
|
||||
<ButtonLink href="/redshift" layout="small" class="flex items-center gap-2 self-center hover:text-m-red">Redshift <img src={wave} class="h-4" alt="redshift"></img></ButtonLink>
|
||||
</Card>
|
||||
</Suspense>
|
||||
</VStack>
|
||||
|
||||
203
src/routes/Redshift.tsx
Normal file
203
src/routes/Redshift.tsx
Normal file
@@ -0,0 +1,203 @@
|
||||
import { createEffect, createMemo, createResource, createSignal, For, Match, onMount, Suspense, Switch } from "solid-js";
|
||||
import { CENTER_COLUMN, MISSING_LABEL, REDSHIFT_LABEL, RIGHT_COLUMN, THREE_COLUMNS, UtxoItem } from "~/components/Activity";
|
||||
import { Card, DefaultMain, LargeHeader, LoadingSpinner, NiceP, NodeManagerGuard, SafeArea, SmallAmount, SmallHeader, VStack } from "~/components/layout";
|
||||
import { BackLink } from "~/components/layout/BackLink";
|
||||
import { StyledRadioGroup } from "~/components/layout/Radio";
|
||||
import NavBar from "~/components/NavBar";
|
||||
import { useMegaStore } from "~/state/megaStore";
|
||||
import wave from "~/assets/wave.gif"
|
||||
|
||||
type ShiftOption = "utxo" | "lightning"
|
||||
|
||||
type ShiftStage = "choose" | "observe" | "success" | "failure"
|
||||
|
||||
|
||||
const SHIFT_OPTIONS = [{ value: "utxo", label: "UTXO", caption: "Trade your UTXO for a fresh UTXO" }, { value: "lightning", label: "Lightning", caption: "Convert your UTXO into Lightning" }]
|
||||
|
||||
import receive from '~/assets/icons/receive.svg';
|
||||
import { Button } from "~/components/layout/Button";
|
||||
import { ProgressBar } from "~/components/layout/ProgressBar";
|
||||
|
||||
export function Utxo(props: { item: UtxoItem, onClick?: () => void, redshifted?: boolean }) {
|
||||
const spent = createMemo(() => props.item.is_spent);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<div class={`${THREE_COLUMNS} ${props.redshifted && "bg-gradient-to-t from-m-red/10 to-transparent rounded-lg"}`} onClick={props.onClick}>
|
||||
<img src={receive} alt="receive arrow" />
|
||||
|
||||
<div class={CENTER_COLUMN}>
|
||||
<div class="flex gap-2">
|
||||
{props.redshifted && <h2 class={REDSHIFT_LABEL}>RS</h2>}
|
||||
{!props.item.redshifted && <h2 class={MISSING_LABEL}>Unknown</h2>}
|
||||
</div>
|
||||
<SmallAmount amount={props.item.txout.value} />
|
||||
</div>
|
||||
<div class={RIGHT_COLUMN}>
|
||||
<SmallHeader class={props.item?.is_spent ? "text-m-red" : "text-m-green"}>
|
||||
{props.item?.is_spent ? "SPENT" : "UNSPENT"}
|
||||
</SmallHeader>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const FAKE_STATES = ["Creating a new node", "Opening a channel", "Sending funds through", "Closing the channel", "Redshift complete"]
|
||||
|
||||
function ShiftObserver(props: { setShiftStage: (stage: ShiftStage) => void }, utxo: UtxoItem) {
|
||||
const [fakeStage, setFakeStage] = createSignal(2);
|
||||
|
||||
// onMount(() => {
|
||||
// const interval = setInterval(() => {
|
||||
// console.log("intervaling")
|
||||
// if (fakeStage() === FAKE_STATES.length - 1) {
|
||||
// clearInterval(interval)
|
||||
// props.setShiftStage("success");
|
||||
// } else {
|
||||
// setFakeStage((fakeStage() + 1))
|
||||
// }
|
||||
// // cont()
|
||||
// }, 1000)
|
||||
// // return () => clearInterval(interval);
|
||||
// })
|
||||
|
||||
const [sentAmount, setSentAmount] = createSignal(0);
|
||||
|
||||
onMount(() => {
|
||||
const interval = setInterval(() => {
|
||||
if (sentAmount() === 200000) {
|
||||
// clearInterval(interval)
|
||||
// props.setShiftStage("success");
|
||||
setSentAmount((0))
|
||||
|
||||
} else {
|
||||
setSentAmount((sentAmount() + 50000))
|
||||
}
|
||||
}, 1000)
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<NiceP>Watch it go!</NiceP>
|
||||
<Card>
|
||||
<VStack>
|
||||
<pre class="self-center">{FAKE_STATES[fakeStage()]}</pre>
|
||||
<ProgressBar value={sentAmount()} max={200000} />
|
||||
<img src={wave} class="h-4 self-center" alt="sine wave" />
|
||||
</VStack>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default function Redshift() {
|
||||
const [state, _actions] = useMegaStore();
|
||||
|
||||
const [shiftStage, setShiftStage] = createSignal<ShiftStage>("observe");
|
||||
const [shiftType, setShiftType] = createSignal<ShiftOption>("utxo");
|
||||
|
||||
const [chosenUtxo, setChosenUtxo] = createSignal<UtxoItem>();
|
||||
|
||||
const getUtXos = async () => {
|
||||
console.log("Getting utxos");
|
||||
const utxos = await state.node_manager?.list_utxos() as UtxoItem[];
|
||||
return utxos;
|
||||
}
|
||||
|
||||
const [utxos, { refetch: _refetchUtxos }] = createResource(getUtXos);
|
||||
|
||||
createEffect(() => {
|
||||
if (chosenUtxo()) {
|
||||
setShiftStage("observe");
|
||||
}
|
||||
})
|
||||
|
||||
function resetState() {
|
||||
setShiftStage("choose");
|
||||
setShiftType("utxo");
|
||||
setChosenUtxo(undefined);
|
||||
}
|
||||
|
||||
return (
|
||||
<NodeManagerGuard>
|
||||
<SafeArea>
|
||||
<DefaultMain>
|
||||
<BackLink />
|
||||
<LargeHeader>Redshift</LargeHeader>
|
||||
<VStack biggap>
|
||||
<Switch>
|
||||
<Match when={shiftStage() === "choose"}>
|
||||
<VStack>
|
||||
<NiceP>Where is this going?</NiceP>
|
||||
<StyledRadioGroup red value={shiftType()} onValueChange={(newValue) => setShiftType(newValue as ShiftOption)} choices={SHIFT_OPTIONS} />
|
||||
</VStack>
|
||||
<VStack>
|
||||
<NiceP>Choose your <span class="inline-block"><img class="h-4" src={wave} alt="sine wave" /></span> UTXO to begin</NiceP>
|
||||
<Suspense>
|
||||
<Card title="Unshifted UTXOs">
|
||||
<Switch>
|
||||
<Match when={utxos.loading}>
|
||||
<LoadingSpinner wide />
|
||||
</Match>
|
||||
<Match when={utxos.state === "ready" && utxos().length === 0}>
|
||||
<code>No utxos (empty state)</code>
|
||||
</Match>
|
||||
<Match when={utxos.state === "ready" && utxos().length >= 0}>
|
||||
<For each={utxos()}>
|
||||
{(utxo) =>
|
||||
<Utxo item={utxo} onClick={() => setChosenUtxo(utxo)} />
|
||||
}
|
||||
</For>
|
||||
</Match>
|
||||
</Switch>
|
||||
</Card>
|
||||
</Suspense>
|
||||
<Suspense>
|
||||
<Card titleElement={<SmallHeader><span class="text-m-red">Redshifted </span>UTXOs</SmallHeader>}>
|
||||
<Switch>
|
||||
<Match when={utxos.loading}>
|
||||
<LoadingSpinner wide />
|
||||
</Match>
|
||||
<Match when={utxos.state === "ready" && utxos().length === 0}>
|
||||
<code>No utxos (empty state)</code>
|
||||
</Match>
|
||||
<Match when={utxos.state === "ready" && utxos().length >= 0}>
|
||||
<For each={utxos()}>
|
||||
{(utxo) =>
|
||||
<Utxo item={utxo} />
|
||||
}
|
||||
</For>
|
||||
</Match>
|
||||
</Switch>
|
||||
</Card>
|
||||
</Suspense>
|
||||
</VStack>
|
||||
</Match>
|
||||
<Match when={shiftStage() === "observe"}>
|
||||
<ShiftObserver setShiftStage={setShiftStage} />
|
||||
</Match>
|
||||
<Match when={shiftStage() === "success"}>
|
||||
<VStack>
|
||||
<NiceP>We did it. Here's your new UTXO:</NiceP>
|
||||
<Card>
|
||||
<Utxo item={chosenUtxo() ?? chosenUtxo()!} redshifted />
|
||||
</Card>
|
||||
<Button intent="red" onClick={resetState}>Nice</Button>
|
||||
</VStack>
|
||||
</Match>
|
||||
<Match when={shiftStage() === "failure"}>
|
||||
<NiceP>Oh dear</NiceP>
|
||||
<NiceP>Here's what happened:</NiceP>
|
||||
<Button intent="red" onClick={resetState}>Dangit</Button>
|
||||
</Match>
|
||||
</Switch>
|
||||
</VStack>
|
||||
</DefaultMain>
|
||||
<NavBar activeTab="redshift" />
|
||||
</SafeArea>
|
||||
</NodeManagerGuard>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user