mirror of
https://github.com/aljazceru/mutiny-web.git
synced 2025-12-19 07:14:22 +01:00
add transfer funds screen
This commit is contained in:
@@ -265,7 +265,8 @@
|
||||
"back_home": "back home"
|
||||
},
|
||||
"start_a_chat": "Start a chat?",
|
||||
"start_a_chat_are_you_sure": "This user isn't in your contact list."
|
||||
"start_a_chat_are_you_sure": "This user isn't in your contact list.",
|
||||
"federation_message": "Federation Message"
|
||||
},
|
||||
"scanner": {
|
||||
"paste": "Paste Something",
|
||||
@@ -566,7 +567,8 @@
|
||||
"descriptionpart2": "Each one is run by a group of different individuals or companies. Discover one that you or your friends might trust below.",
|
||||
"join_me": "Join me",
|
||||
"recommend": "Recommend federation",
|
||||
"recommended_by_you": "Recommended by you"
|
||||
"recommended_by_you": "Recommended by you",
|
||||
"transfer_funds": "Transfer funds"
|
||||
},
|
||||
"gift": {
|
||||
"give_sats_link": "Give sats as a gift",
|
||||
@@ -787,5 +789,11 @@
|
||||
"nowish": "Nowish",
|
||||
"seconds_future": "Seconds from now",
|
||||
"seconds_past": "Just now"
|
||||
},
|
||||
"transfer": {
|
||||
"completed": "Transfer Completed",
|
||||
"sats_moved": "+{{amount}} sats have been moved to {{federation_name}}",
|
||||
"confirm": "Confirm Transfer",
|
||||
"title": "Transfer funds"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
createSignal,
|
||||
For,
|
||||
Match,
|
||||
onMount,
|
||||
Show,
|
||||
Suspense,
|
||||
Switch
|
||||
@@ -364,7 +365,7 @@ function NewContactModal(props: { profile: PseudoContact; close: () => void }) {
|
||||
}
|
||||
|
||||
export function CombinedActivity() {
|
||||
const [state, _actions, sw] = useMegaStore();
|
||||
const [state, actions, sw] = useMegaStore();
|
||||
const i18n = useI18n();
|
||||
|
||||
const [detailsOpen, setDetailsOpen] = createSignal(false);
|
||||
@@ -407,6 +408,17 @@ export function CombinedActivity() {
|
||||
|
||||
const [newContact, setNewContact] = createSignal<PseudoContact>();
|
||||
|
||||
const [
|
||||
showFederationExpirationWarning,
|
||||
setShowFederationExpirationWarning
|
||||
] = createSignal(false);
|
||||
|
||||
onMount(() => {
|
||||
if (state.expiration_warning) {
|
||||
setShowFederationExpirationWarning(true);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Show when={detailsId() && detailsKind()}>
|
||||
@@ -424,6 +436,32 @@ export function CombinedActivity() {
|
||||
/>
|
||||
</Show>
|
||||
<Suspense fallback={<LoadingShimmer />}>
|
||||
<Show when={state.expiration_warning}>
|
||||
<SimpleDialog
|
||||
title={i18n.t("activity.federation_message")}
|
||||
open={showFederationExpirationWarning()}
|
||||
setOpen={(open: boolean) => {
|
||||
if (!open) {
|
||||
setShowFederationExpirationWarning(false);
|
||||
actions.clearExpirationWarning();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<NiceP>
|
||||
{state.expiration_warning?.expiresMessage}
|
||||
</NiceP>
|
||||
<ButtonCard
|
||||
onClick={() => navigate("/settings/federations")}
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<Users class="inline-block text-m-red" />
|
||||
<NiceP>
|
||||
{i18n.t("profile.manage_federation")}
|
||||
</NiceP>
|
||||
</div>
|
||||
</ButtonCard>
|
||||
</SimpleDialog>
|
||||
</Show>
|
||||
<Show when={!state.has_backed_up}>
|
||||
<ButtonCard
|
||||
red
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
} from "~/utils";
|
||||
|
||||
export type MethodChoice = {
|
||||
method: "lightning" | "onchain";
|
||||
method: "lightning" | "onchain" | "fedimint";
|
||||
maxAmountSats?: bigint;
|
||||
};
|
||||
|
||||
@@ -29,6 +29,8 @@ function methodToIcon(method: MethodChoice["method"]) {
|
||||
return "lightning";
|
||||
} else if (method === "onchain") {
|
||||
return "chain";
|
||||
} else if (method === "fedimint") {
|
||||
return "community";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,8 @@ import {
|
||||
Search,
|
||||
Send,
|
||||
Swap,
|
||||
SwapLightning
|
||||
SwapLightning,
|
||||
Transfer
|
||||
} from "~/routes";
|
||||
import {
|
||||
Admin,
|
||||
@@ -179,6 +180,7 @@ export function Router() {
|
||||
<Route path="/send" component={Send} />
|
||||
<Route path="/swap" component={Swap} />
|
||||
<Route path="/swaplightning" component={SwapLightning} />
|
||||
<Route path="/transfer" component={Transfer} />
|
||||
<Route path="/search" component={Search} />
|
||||
<Route path="/settings">
|
||||
<Route path="/" component={Settings} />
|
||||
|
||||
216
src/routes/Transfer.tsx
Normal file
216
src/routes/Transfer.tsx
Normal file
@@ -0,0 +1,216 @@
|
||||
import { FedimintSweepResult } from "@mutinywallet/mutiny-wasm";
|
||||
import { createAsync, useNavigate, useSearchParams } from "@solidjs/router";
|
||||
import { ArrowDown, Users } from "lucide-solid";
|
||||
import { createMemo, createSignal, Match, Suspense, Switch } from "solid-js";
|
||||
|
||||
import {
|
||||
AmountEditable,
|
||||
AmountFiat,
|
||||
AmountSats,
|
||||
BackLink,
|
||||
Button,
|
||||
DefaultMain,
|
||||
Failure,
|
||||
Fee,
|
||||
LargeHeader,
|
||||
MegaCheck,
|
||||
MutinyWalletGuard,
|
||||
SharpButton,
|
||||
SuccessModal,
|
||||
VStack
|
||||
} from "~/components";
|
||||
import { useI18n } from "~/i18n/context";
|
||||
import { useMegaStore } from "~/state/megaStore";
|
||||
import { eify, vibrateSuccess } from "~/utils";
|
||||
|
||||
type TransferResultDetails = {
|
||||
result?: FedimintSweepResult;
|
||||
failure_reason?: string;
|
||||
};
|
||||
|
||||
export function Transfer() {
|
||||
const [state, _actions, sw] = useMegaStore();
|
||||
const i18n = useI18n();
|
||||
const navigate = useNavigate();
|
||||
const [amountSats, setAmountSats] = createSignal(0n);
|
||||
const [loading, setLoading] = createSignal(false);
|
||||
const [params] = useSearchParams();
|
||||
|
||||
const canTransfer = createMemo(() => {
|
||||
return true;
|
||||
});
|
||||
|
||||
const [transferResult, setTransferResult] =
|
||||
createSignal<TransferResultDetails>();
|
||||
|
||||
const fromFed = () => {
|
||||
return state.federations?.find((f) => f.federation_id === params.from);
|
||||
};
|
||||
|
||||
const toFed = () => {
|
||||
return state.federations?.find((f) => f.federation_id !== params.from);
|
||||
};
|
||||
|
||||
const federationBalances = createAsync(async () => {
|
||||
try {
|
||||
const balances = await sw.get_federation_balances();
|
||||
return balances?.balances || [];
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
const calculateMaxFederation = createAsync(async () => {
|
||||
return federationBalances()?.find(
|
||||
(f) => f.identity_federation_id === fromFed()?.federation_id
|
||||
)?.balance;
|
||||
});
|
||||
|
||||
const toBalance = createAsync(async () => {
|
||||
return federationBalances()?.find(
|
||||
(f) => f.identity_federation_id === toFed()?.federation_id
|
||||
)?.balance;
|
||||
});
|
||||
|
||||
const isMax = createMemo(() => {
|
||||
return amountSats() === calculateMaxFederation();
|
||||
});
|
||||
|
||||
async function handleTransfer() {
|
||||
try {
|
||||
setLoading(true);
|
||||
if (!fromFed()) throw new Error("No from federation");
|
||||
if (!toFed()) throw new Error("No to federation");
|
||||
|
||||
if (isMax()) {
|
||||
const result = await sw.sweep_federation_balance(
|
||||
undefined,
|
||||
fromFed()?.federation_id,
|
||||
toFed()?.federation_id
|
||||
);
|
||||
|
||||
setTransferResult({ result: result });
|
||||
} else {
|
||||
const result = await sw.sweep_federation_balance(
|
||||
amountSats(),
|
||||
fromFed()?.federation_id,
|
||||
toFed()?.federation_id
|
||||
);
|
||||
|
||||
setTransferResult({ result: result });
|
||||
}
|
||||
|
||||
await vibrateSuccess();
|
||||
} catch (e) {
|
||||
const error = eify(e);
|
||||
setTransferResult({ failure_reason: error.message });
|
||||
console.error(e);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
// const fromFederatationId = params.from;
|
||||
|
||||
return (
|
||||
<MutinyWalletGuard>
|
||||
<DefaultMain>
|
||||
<SuccessModal
|
||||
confirmText={
|
||||
transferResult()?.result
|
||||
? i18n.t("common.nice")
|
||||
: i18n.t("common.home")
|
||||
}
|
||||
open={!!transferResult()}
|
||||
setOpen={(open: boolean) => {
|
||||
if (!open) setTransferResult(undefined);
|
||||
}}
|
||||
onConfirm={() => {
|
||||
setTransferResult(undefined);
|
||||
navigate("/");
|
||||
}}
|
||||
>
|
||||
<Switch>
|
||||
<Match when={transferResult()?.failure_reason}>
|
||||
<Failure
|
||||
reason={transferResult()!.failure_reason!}
|
||||
/>
|
||||
</Match>
|
||||
<Match when={transferResult()?.result}>
|
||||
<MegaCheck />
|
||||
<div class="flex flex-col justify-center">
|
||||
<h1 class="mb-2 mt-4 w-full justify-center text-center text-2xl font-semibold md:text-3xl">
|
||||
{i18n.t("transfer.completed")}
|
||||
</h1>
|
||||
<p class="text-center text-xl">
|
||||
{i18n.t("transfer.sats_moved", {
|
||||
amount: Number(
|
||||
transferResult()?.result?.amount
|
||||
).toLocaleString(),
|
||||
federation_name:
|
||||
toFed()?.federation_name
|
||||
})}
|
||||
</p>
|
||||
<div class="text-center text-sm text-white/70">
|
||||
<Suspense>
|
||||
<AmountFiat
|
||||
amountSats={Number(
|
||||
transferResult()?.result?.amount
|
||||
)}
|
||||
/>
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="w-16 bg-m-grey-400" />
|
||||
<Fee
|
||||
amountSats={Number(
|
||||
transferResult()?.result?.fees
|
||||
)}
|
||||
/>
|
||||
</Match>
|
||||
</Switch>
|
||||
</SuccessModal>
|
||||
<BackLink href="/settings/federations" />
|
||||
<LargeHeader>{i18n.t("transfer.title")}</LargeHeader>
|
||||
<div class="flex flex-1 flex-col justify-between gap-2">
|
||||
<div class="flex-1" />
|
||||
<div class="flex flex-col items-center">
|
||||
<AmountEditable
|
||||
initialAmountSats={amountSats()}
|
||||
setAmountSats={setAmountSats}
|
||||
/>
|
||||
<SharpButton disabled onClick={() => {}}>
|
||||
<Users class="w-[18px]" />
|
||||
{fromFed()?.federation_name}
|
||||
<AmountSats
|
||||
amountSats={calculateMaxFederation()}
|
||||
denominationSize="sm"
|
||||
/>
|
||||
</SharpButton>
|
||||
<ArrowDown class="h-4 w-4" />
|
||||
<SharpButton disabled onClick={() => {}}>
|
||||
<Users class="w-[18px]" />
|
||||
{toFed()?.federation_name}
|
||||
<AmountSats
|
||||
amountSats={toBalance()}
|
||||
denominationSize="sm"
|
||||
/>
|
||||
</SharpButton>
|
||||
</div>
|
||||
<div class="flex-1" />
|
||||
<VStack>
|
||||
<Button
|
||||
disabled={!canTransfer()}
|
||||
intent="blue"
|
||||
onClick={handleTransfer}
|
||||
loading={loading()}
|
||||
>
|
||||
{i18n.t("transfer.confirm")}
|
||||
</Button>
|
||||
</VStack>
|
||||
</div>
|
||||
</DefaultMain>
|
||||
</MutinyWalletGuard>
|
||||
);
|
||||
}
|
||||
@@ -12,3 +12,4 @@ export * from "./Request";
|
||||
export * from "./EditProfile";
|
||||
export * from "./Swap";
|
||||
export * from "./SwapLightning";
|
||||
export * from "./Transfer";
|
||||
|
||||
@@ -7,8 +7,9 @@ import {
|
||||
} from "@modular-forms/solid";
|
||||
import { FederationBalance, TagItem } from "@mutinywallet/mutiny-wasm";
|
||||
import { A, useNavigate, useSearchParams } from "@solidjs/router";
|
||||
import { BadgeCheck, LogOut, Scan, Trash } from "lucide-solid";
|
||||
import { ArrowLeftRight, BadgeCheck, LogOut, Scan, Trash } from "lucide-solid";
|
||||
import {
|
||||
createMemo,
|
||||
createResource,
|
||||
createSignal,
|
||||
For,
|
||||
@@ -57,6 +58,7 @@ export type MutinyFederationIdentity = {
|
||||
welcome_message: string;
|
||||
federation_expiry_timestamp: number;
|
||||
invite_code: string;
|
||||
meta_external_url?: string;
|
||||
};
|
||||
|
||||
export type Metadata = {
|
||||
@@ -240,99 +242,12 @@ export function AddFederationForm(props: {
|
||||
<Match when={federations.latest}>
|
||||
<For each={federations()}>
|
||||
{(fed) => (
|
||||
<FancyCard>
|
||||
<VStack>
|
||||
<div class="flex items-center gap-2 md:gap-4">
|
||||
<LabelCircle
|
||||
name={fed.metadata?.name}
|
||||
image_url={
|
||||
fed.metadata?.picture
|
||||
}
|
||||
contact={false}
|
||||
label={false}
|
||||
/>
|
||||
<div>
|
||||
<header class={`font-semibold`}>
|
||||
{fed.metadata?.name}
|
||||
</header>
|
||||
<Show
|
||||
when={fed.metadata?.about}
|
||||
>
|
||||
<p>{fed.metadata?.about}</p>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
<Show when={!props.setup}>
|
||||
<KeyValue
|
||||
key={i18n.t(
|
||||
"settings.manage_federations.federation_id"
|
||||
)}
|
||||
>
|
||||
<MiniStringShower
|
||||
text={fed.id}
|
||||
/>
|
||||
</KeyValue>
|
||||
</Show>
|
||||
<Show when={fed.created_at}>
|
||||
<KeyValue
|
||||
key={i18n.t(
|
||||
"settings.manage_federations.created_at"
|
||||
)}
|
||||
>
|
||||
<time>
|
||||
{timeAgo(fed.created_at)}
|
||||
</time>
|
||||
</KeyValue>
|
||||
</Show>
|
||||
<Show
|
||||
when={
|
||||
fed.recommendations.length > 0
|
||||
}
|
||||
>
|
||||
<KeyValue
|
||||
key={i18n.t(
|
||||
"settings.manage_federations.recommended_by"
|
||||
)}
|
||||
>
|
||||
<div class="flex items-center gap-2 overflow-scroll md:gap-4">
|
||||
<For
|
||||
each={
|
||||
fed.recommendations
|
||||
}
|
||||
>
|
||||
{(contact) => (
|
||||
<LabelCircle
|
||||
name={
|
||||
contact.name
|
||||
}
|
||||
image_url={
|
||||
contact.image_url
|
||||
}
|
||||
contact={true}
|
||||
label={false}
|
||||
/>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</KeyValue>
|
||||
</Show>
|
||||
<Show when={!props.browseOnly}>
|
||||
<Button
|
||||
intent="blue"
|
||||
onClick={() =>
|
||||
onSelect(fed.invite_codes)
|
||||
}
|
||||
loading={fed.invite_codes.includes(
|
||||
loadingFederation()
|
||||
)}
|
||||
>
|
||||
{i18n.t(
|
||||
"settings.manage_federations.add"
|
||||
)}
|
||||
</Button>
|
||||
</Show>
|
||||
</VStack>
|
||||
</FancyCard>
|
||||
<FederationFormItem
|
||||
fed={fed}
|
||||
onSelect={onSelect}
|
||||
loadingFederation={loadingFederation()}
|
||||
setup={!!props.setup}
|
||||
/>
|
||||
)}
|
||||
</For>
|
||||
</Match>
|
||||
@@ -342,6 +257,94 @@ export function AddFederationForm(props: {
|
||||
);
|
||||
}
|
||||
|
||||
function FederationFormItem(props: {
|
||||
fed: DiscoveredFederation;
|
||||
onSelect: (invite_codes: string[]) => void;
|
||||
loadingFederation: string;
|
||||
setup: boolean;
|
||||
}) {
|
||||
const [state, _actions, _sw] = useMegaStore();
|
||||
const i18n = useI18n();
|
||||
|
||||
const alreadyAdded = createMemo(() => {
|
||||
const matches = state.federations?.find((f) =>
|
||||
props.fed.invite_codes.includes(f.invite_code)
|
||||
);
|
||||
return matches !== undefined;
|
||||
});
|
||||
return (
|
||||
<FancyCard>
|
||||
<VStack>
|
||||
<div class="flex items-center gap-2 md:gap-4">
|
||||
<LabelCircle
|
||||
name={props.fed.metadata?.name}
|
||||
image_url={props.fed.metadata?.picture}
|
||||
contact={false}
|
||||
label={false}
|
||||
/>
|
||||
<div>
|
||||
<header class={`font-semibold`}>
|
||||
{props.fed.metadata?.name}
|
||||
</header>
|
||||
<Show when={props.fed.metadata?.about}>
|
||||
<p>{props.fed.metadata?.about}</p>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
<Show when={!props.setup}>
|
||||
<KeyValue
|
||||
key={i18n.t(
|
||||
"settings.manage_federations.federation_id"
|
||||
)}
|
||||
>
|
||||
<MiniStringShower text={props.fed.id} />
|
||||
</KeyValue>
|
||||
</Show>
|
||||
<Show when={props.fed.created_at}>
|
||||
<KeyValue
|
||||
key={i18n.t("settings.manage_federations.created_at")}
|
||||
>
|
||||
<time>{timeAgo(props.fed.created_at)}</time>
|
||||
</KeyValue>
|
||||
</Show>
|
||||
<Show when={props.fed.recommendations.length > 0}>
|
||||
<KeyValue
|
||||
key={i18n.t(
|
||||
"settings.manage_federations.recommended_by"
|
||||
)}
|
||||
>
|
||||
<div class="flex items-center gap-2 overflow-scroll md:gap-4">
|
||||
<For each={props.fed.recommendations}>
|
||||
{(contact) => (
|
||||
<LabelCircle
|
||||
name={contact.name}
|
||||
image_url={contact.image_url}
|
||||
contact={true}
|
||||
label={false}
|
||||
/>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</KeyValue>
|
||||
</Show>
|
||||
<Show
|
||||
when={!alreadyAdded() && !(state.federations?.length === 2)}
|
||||
>
|
||||
<Button
|
||||
intent="blue"
|
||||
onClick={() => props.onSelect(props.fed.invite_codes)}
|
||||
loading={props.fed.invite_codes.includes(
|
||||
props.loadingFederation
|
||||
)}
|
||||
>
|
||||
{i18n.t("settings.manage_federations.add")}
|
||||
</Button>
|
||||
</Show>
|
||||
</VStack>
|
||||
</FancyCard>
|
||||
);
|
||||
}
|
||||
|
||||
function RecommendButton(props: { fed: MutinyFederationIdentity }) {
|
||||
const [_state, _actions, sw] = useMegaStore();
|
||||
const i18n = useI18n();
|
||||
@@ -425,7 +428,8 @@ function FederationListItem(props: {
|
||||
balance?: bigint;
|
||||
}) {
|
||||
const i18n = useI18n();
|
||||
const [_state, actions, sw] = useMegaStore();
|
||||
const [state, actions, sw] = useMegaStore();
|
||||
const navigate = useNavigate();
|
||||
|
||||
async function removeFederation() {
|
||||
setConfirmLoading(true);
|
||||
@@ -442,6 +446,10 @@ function FederationListItem(props: {
|
||||
setConfirmOpen(true);
|
||||
}
|
||||
|
||||
async function transferFunds() {
|
||||
navigate("/transfer?from=" + props.fed.federation_id);
|
||||
}
|
||||
|
||||
const [confirmOpen, setConfirmOpen] = createSignal(false);
|
||||
const [confirmLoading, setConfirmLoading] = createSignal(false);
|
||||
|
||||
@@ -449,6 +457,7 @@ function FederationListItem(props: {
|
||||
<>
|
||||
<FancyCard>
|
||||
<VStack>
|
||||
{/* <pre>{JSON.stringify(props.fed, null, 2)}</pre> */}
|
||||
<Show when={props.fed.federation_name}>
|
||||
<header class={`font-semibold`}>
|
||||
{props.fed.federation_name}
|
||||
@@ -490,6 +499,19 @@ function FederationListItem(props: {
|
||||
inviteCode={props.fed.invite_code}
|
||||
/>
|
||||
</KeyValue>
|
||||
<Show
|
||||
when={
|
||||
state.federations?.length &&
|
||||
state.federations.length === 2
|
||||
}
|
||||
>
|
||||
<SubtleButton onClick={transferFunds}>
|
||||
<ArrowLeftRight class="h-4 w-4" />
|
||||
{i18n.t(
|
||||
"settings.manage_federations.transfer_funds"
|
||||
)}
|
||||
</SubtleButton>
|
||||
</Show>
|
||||
<Suspense>
|
||||
<RecommendButton fed={props.fed} />
|
||||
</Suspense>
|
||||
@@ -606,7 +628,7 @@ export function ManageFederations() {
|
||||
</VStack>
|
||||
<Suspense>
|
||||
<Show when={state.federations?.length}>
|
||||
<AddFederationForm refetch={refetch} browseOnly />
|
||||
<AddFederationForm refetch={refetch} />
|
||||
</Show>
|
||||
</Suspense>
|
||||
</DefaultMain>
|
||||
|
||||
@@ -87,7 +87,10 @@ export const makeMegaStoreContext = () => {
|
||||
testflightPromptDismissed:
|
||||
localStorage.getItem("testflightPromptDismissed") === "true",
|
||||
federations: undefined as MutinyFederationIdentity[] | undefined,
|
||||
balanceView: localStorage.getItem("balanceView") || "sats"
|
||||
balanceView: localStorage.getItem("balanceView") || "sats",
|
||||
expiration_warning: undefined as
|
||||
| { expiresTimestamp: number; expiresMessage: string }
|
||||
| undefined
|
||||
});
|
||||
|
||||
const actions = {
|
||||
@@ -226,15 +229,59 @@ export const makeMegaStoreContext = () => {
|
||||
const balance = await sw.get_balance();
|
||||
|
||||
// Get federations
|
||||
const federations =
|
||||
(await sw.list_federations()) as MutinyFederationIdentity[];
|
||||
const federations = await sw.list_federations();
|
||||
|
||||
let expiration_warning:
|
||||
| { expiresTimestamp: number; expiresMessage: string }
|
||||
| undefined = undefined;
|
||||
|
||||
try {
|
||||
if (federations.length) {
|
||||
const activeFederation = federations[0];
|
||||
const metadataUrl = activeFederation.meta_external_url;
|
||||
console.log("federation metadata url", metadataUrl);
|
||||
if (metadataUrl) {
|
||||
const response = await fetch(metadataUrl);
|
||||
if (response.ok) {
|
||||
const metadata = await response.json();
|
||||
console.log(
|
||||
"all federation metadata",
|
||||
metadata
|
||||
);
|
||||
const specificFederation =
|
||||
metadata[activeFederation.federation_id];
|
||||
console.log(
|
||||
"specific federation metadata",
|
||||
specificFederation
|
||||
);
|
||||
const expiresTimestamp =
|
||||
specificFederation.popup_end_timestamp;
|
||||
console.log(
|
||||
"federation expires",
|
||||
expiresTimestamp
|
||||
);
|
||||
const expiresMessage =
|
||||
specificFederation.popup_countdown_message;
|
||||
expiration_warning = {
|
||||
expiresTimestamp,
|
||||
expiresMessage
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Error getting federation metadata", e);
|
||||
}
|
||||
|
||||
console.log("expiration_warning", expiration_warning);
|
||||
|
||||
setState({
|
||||
wallet_loading: false,
|
||||
load_stage: "done",
|
||||
balance,
|
||||
federations,
|
||||
network: network as Network
|
||||
network: network as Network,
|
||||
expiration_warning
|
||||
});
|
||||
|
||||
// Timestamp our initialization for double init defense
|
||||
@@ -506,6 +553,10 @@ export const makeMegaStoreContext = () => {
|
||||
channel.postMessage({ type: "EXISTING_TAB" });
|
||||
}
|
||||
};
|
||||
},
|
||||
// Only show the expiration warning once per session
|
||||
clearExpirationWarning() {
|
||||
setState({ expiration_warning: undefined });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -217,6 +217,7 @@ export async function get_balance(): Promise<MutinyBalance> {
|
||||
*/
|
||||
export async function list_federations(): Promise<MutinyFederationIdentity[]> {
|
||||
const federations = await wallet!.list_federations();
|
||||
console.log("list_federations", federations);
|
||||
return federations as MutinyFederationIdentity[];
|
||||
}
|
||||
|
||||
@@ -1548,9 +1549,15 @@ export async function estimate_sweep_channel_open_fee(
|
||||
* @returns {Promise<FedimintSweepResult>}
|
||||
*/
|
||||
export async function sweep_federation_balance(
|
||||
amount?: bigint
|
||||
amount?: bigint,
|
||||
from_federation_id?: string,
|
||||
to_federation_id?: string
|
||||
): Promise<FedimintSweepResult> {
|
||||
const result = await wallet!.sweep_federation_balance(amount);
|
||||
const result = await wallet!.sweep_federation_balance(
|
||||
amount,
|
||||
from_federation_id,
|
||||
to_federation_id
|
||||
);
|
||||
return { ...result.value } as FedimintSweepResult;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user