show federation popups

This commit is contained in:
Paul Miller
2024-05-08 16:48:23 -05:00
parent 87d8964b86
commit f27e87178a
11 changed files with 131 additions and 118 deletions

View File

@@ -568,7 +568,8 @@
"join_me": "Join me",
"recommend": "Recommend federation",
"recommended_by_you": "Recommended by you",
"transfer_funds": "Transfer funds"
"transfer_funds": "Transfer funds",
"transfer_funds_message": "Add a second federation to enable transfers."
},
"gift": {
"give_sats_link": "Give sats as a gift",

View File

@@ -8,7 +8,6 @@ import {
createSignal,
For,
Match,
onMount,
Show,
Suspense,
Switch
@@ -19,6 +18,7 @@ import {
Button,
ButtonCard,
ContactButton,
FederationPopup,
LoadingShimmer,
NiceP,
SimpleDialog
@@ -365,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);
@@ -408,17 +408,6 @@ 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()}>
@@ -437,30 +426,7 @@ 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>
<FederationPopup />
</Show>
<Show when={!state.has_backed_up}>
<ButtonCard

View File

@@ -23,14 +23,14 @@ export type MethodChoice = {
maxAmountSats?: bigint;
};
// Make sure to update this when we get the fedi option in here!
function methodToIcon(method: MethodChoice["method"]) {
if (method === "lightning") {
return "lightning";
} else if (method === "onchain") {
return "chain";
} else if (method === "fedimint") {
return "community";
switch (method) {
case "lightning":
return "lightning";
case "onchain":
return "chain";
case "fedimint":
return "community";
}
}

View File

@@ -0,0 +1,45 @@
import { useNavigate } from "@solidjs/router";
import { Users } from "lucide-solid";
import { createSignal } from "solid-js";
import { ButtonCard, NiceP, SimpleDialog } from "~/components/layout";
import { useI18n } from "~/i18n/context";
import { useMegaStore } from "~/state/megaStore";
export function FederationPopup() {
const [state, actions, _sw] = useMegaStore();
const [
showFederationExpirationWarning,
setShowFederationExpirationWarning
] = createSignal(!state.expiration_warning_seen);
const i18n = useI18n();
const navigate = useNavigate();
return (
<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={() => {
actions.clearExpirationWarning();
setShowFederationExpirationWarning(false);
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>
);
}

View File

@@ -56,3 +56,4 @@ export * from "./EditProfileForm";
export * from "./ImportNsecForm";
export * from "./LightningAddressShower";
export * from "./FederationInviteShower";
export * from "./FederationPopup";

View File

@@ -1,10 +1,5 @@
import { MutinyInvoice, TagItem } from "@mutinywallet/mutiny-wasm";
import {
createAsync,
useLocation,
useNavigate,
useSearchParams
} from "@solidjs/router";
import { useLocation, useNavigate, useSearchParams } from "@solidjs/router";
import { Eye, EyeOff, Link, X, Zap } from "lucide-solid";
import {
createEffect,

View File

@@ -1,6 +1,6 @@
import { createForm, required } from "@modular-forms/solid";
import { MutinyChannel } from "@mutinywallet/mutiny-wasm";
import { createAsync, useNavigate } from "@solidjs/router";
import { useNavigate } from "@solidjs/router";
import {
createEffect,
createMemo,

View File

@@ -36,10 +36,6 @@ export function Transfer() {
const [loading, setLoading] = createSignal(false);
const [params] = useSearchParams();
const canTransfer = createMemo(() => {
return true;
});
const [transferResult, setTransferResult] =
createSignal<TransferResultDetails>();
@@ -62,9 +58,10 @@ export function Transfer() {
});
const calculateMaxFederation = createAsync(async () => {
return federationBalances()?.find(
const balance = federationBalances()?.find(
(f) => f.identity_federation_id === fromFed()?.federation_id
)?.balance;
return balance || 0n;
});
const toBalance = createAsync(async () => {
@@ -77,6 +74,11 @@ export function Transfer() {
return amountSats() === calculateMaxFederation();
});
const canTransfer = createMemo(() => {
if (!calculateMaxFederation()) return false;
return amountSats() > 0n && amountSats() <= calculateMaxFederation()!;
});
async function handleTransfer() {
try {
setLoading(true);
@@ -111,8 +113,6 @@ export function Transfer() {
}
}
// const fromFederatationId = params.from;
return (
<MutinyWalletGuard>
<DefaultMain>
@@ -171,7 +171,11 @@ export function Transfer() {
</Match>
</Switch>
</SuccessModal>
<BackLink href="/settings/federations" />
<BackLink
title={i18n.t("common.back")}
href="/settings/federations"
showOnDesktop
/>
<LargeHeader>{i18n.t("transfer.title")}</LargeHeader>
<div class="flex flex-1 flex-col justify-between gap-2">
<div class="flex-1" />

View File

@@ -29,6 +29,7 @@ import {
ExternalLink,
FancyCard,
FederationInviteShower,
FederationPopup,
InfoBox,
KeyValue,
LabelCircle,
@@ -40,6 +41,7 @@ import {
NavBar,
NiceP,
showToast,
SimpleDialog,
SubtleButton,
TextField,
VStack
@@ -59,6 +61,8 @@ export type MutinyFederationIdentity = {
federation_expiry_timestamp: number;
invite_code: string;
meta_external_url?: string;
popup_end_timestamp?: number;
popup_countdown_message?: string;
};
export type Metadata = {
@@ -91,7 +95,7 @@ export function AddFederationForm(props: {
browseOnly?: boolean;
}) {
const i18n = useI18n();
const [_state, actions, sw] = useMegaStore();
const [state, actions, sw] = useMegaStore();
const navigate = useNavigate();
const [error, setError] = createSignal<Error>();
const [success, setSuccess] = createSignal("");
@@ -181,6 +185,9 @@ export function AddFederationForm(props: {
return (
<div class="flex w-full flex-col gap-4">
<Show when={state.expiration_warning}>
<FederationPopup />
</Show>
<Show when={!props.setup && !props.browseOnly}>
<MediumHeader>
{i18n.t("settings.manage_federations.manual")}
@@ -446,8 +453,15 @@ function FederationListItem(props: {
setConfirmOpen(true);
}
const [transferDialogOpen, setTransferDialogOpen] = createSignal(false);
async function transferFunds() {
navigate("/transfer?from=" + props.fed.federation_id);
// If there's only one federation we need to let them know to add another
if (state.federations?.length && state.federations.length < 2) {
setTransferDialogOpen(true);
} else {
navigate("/transfer?from=" + props.fed.federation_id);
}
}
const [confirmOpen, setConfirmOpen] = createSignal(false);
@@ -457,7 +471,6 @@ 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}
@@ -466,6 +479,19 @@ function FederationListItem(props: {
<Show when={props.fed.welcome_message}>
<p>{props.fed.welcome_message}</p>
</Show>
<SimpleDialog
title={i18n.t(
"settings.manage_federations.transfer_funds"
)}
open={transferDialogOpen()}
setOpen={setTransferDialogOpen}
>
<NiceP>
{i18n.t(
"settings.manage_federations.transfer_funds_message"
)}
</NiceP>
</SimpleDialog>
<Show when={props.balance !== undefined}>
<KeyValue
key={i18n.t("activity.transaction_details.balance")}
@@ -499,19 +525,10 @@ 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>
<SubtleButton onClick={transferFunds}>
<ArrowLeftRight class="h-4 w-4" />
{i18n.t("settings.manage_federations.transfer_funds")}
</SubtleButton>
<Suspense>
<RecommendButton fed={props.fed} />
</Suspense>

View File

@@ -90,7 +90,8 @@ export const makeMegaStoreContext = () => {
balanceView: localStorage.getItem("balanceView") || "sats",
expiration_warning: undefined as
| { expiresTimestamp: number; expiresMessage: string }
| undefined
| undefined,
expiration_warning_seen: false
});
const actions = {
@@ -235,45 +236,14 @@ export const makeMegaStoreContext = () => {
| { 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
};
}
}
federations.forEach((f) => {
if (f.popup_countdown_message && f.popup_end_timestamp) {
expiration_warning = {
expiresTimestamp: f.popup_end_timestamp,
expiresMessage: f.popup_countdown_message
};
}
} catch (e) {
console.error("Error getting federation metadata", e);
}
console.log("expiration_warning", expiration_warning);
});
setState({
wallet_loading: false,
@@ -513,7 +483,21 @@ export const makeMegaStoreContext = () => {
},
async refreshFederations() {
const federations = await sw.list_federations();
setState({ federations });
let expiration_warning:
| { expiresTimestamp: number; expiresMessage: string }
| undefined = undefined;
federations.forEach((f) => {
if (f.popup_countdown_message && f.popup_end_timestamp) {
expiration_warning = {
expiresTimestamp: f.popup_end_timestamp,
expiresMessage: f.popup_countdown_message
};
}
});
setState({ federations, expiration_warning });
},
cycleBalanceView() {
if (state.balanceView === "sats") {
@@ -556,7 +540,7 @@ export const makeMegaStoreContext = () => {
},
// Only show the expiration warning once per session
clearExpirationWarning() {
setState({ expiration_warning: undefined });
setState({ expiration_warning_seen: true });
}
};

View File

@@ -217,7 +217,6 @@ 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[];
}
@@ -1382,6 +1381,7 @@ export async function delete_federation_recommendation(
*/
export async function get_federation_balances(): Promise<FederationBalances> {
const balances = await wallet!.get_federation_balances();
if (!balances) return { balances: [] } as unknown as FederationBalances;
// PAIN
// Have to rebuild the balances from the raw data, which is a bit of a pain
const newBalances: FederationBalance[] = [];