mirror of
https://github.com/aljazceru/mutiny-web.git
synced 2025-12-17 06:14:21 +01:00
show federation popups
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
45
src/components/FederationPopup.tsx
Normal file
45
src/components/FederationPopup.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -56,3 +56,4 @@ export * from "./EditProfileForm";
|
||||
export * from "./ImportNsecForm";
|
||||
export * from "./LightningAddressShower";
|
||||
export * from "./FederationInviteShower";
|
||||
export * from "./FederationPopup";
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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[] = [];
|
||||
|
||||
Reference in New Issue
Block a user