diff --git a/src/components/SetupErrorDisplay.tsx b/src/components/SetupErrorDisplay.tsx
index b8f8697b..d0a96c26 100644
--- a/src/components/SetupErrorDisplay.tsx
+++ b/src/components/SetupErrorDisplay.tsx
@@ -221,6 +221,14 @@ export function SetupErrorDisplay(props: {
)}
+
+ {i18n.t("error.on_boot.loading_failed.in_the_meantime")}{" "}
+
+ {" "}
+ {i18n.t("error.on_boot.loading_failed.safe_mode")}
+
+ .
+
diff --git a/src/components/layout/Misc.tsx b/src/components/layout/Misc.tsx
index f9025d6f..592b7b6c 100644
--- a/src/components/layout/Misc.tsx
+++ b/src/components/layout/Misc.tsx
@@ -349,10 +349,10 @@ export function ModalCloseButton() {
);
}
-const SIMPLE_OVERLAY = "fixed inset-0 z-50 bg-black/50 backdrop-blur-lg";
-const SIMPLE_DIALOG_POSITIONER =
+export const SIMPLE_OVERLAY = "fixed inset-0 z-50 bg-black/50 backdrop-blur-lg";
+export const SIMPLE_DIALOG_POSITIONER =
"fixed inset-0 z-50 flex items-center justify-center";
-const SIMPLE_DIALOG_CONTENT =
+export const SIMPLE_DIALOG_CONTENT =
"max-w-[500px] w-[90vw] max-h-device overflow-y-scroll disable-scrollbars mx-4 p-4 bg-neutral-800/90 rounded-xl border border-white/10";
export const SimpleDialog: ParentComponent<{
diff --git a/src/routes/settings/ManageFederations.tsx b/src/routes/settings/ManageFederations.tsx
index 87dfca6b..e3e07477 100644
--- a/src/routes/settings/ManageFederations.tsx
+++ b/src/routes/settings/ManageFederations.tsx
@@ -1,3 +1,4 @@
+import { Progress } from "@kobalte/core";
import {
createForm,
required,
@@ -7,7 +8,14 @@ import {
} from "@modular-forms/solid";
import { FederationBalance, TagItem } from "@mutinywallet/mutiny-wasm";
import { A, useNavigate, useSearchParams } from "@solidjs/router";
-import { ArrowLeftRight, BadgeCheck, LogOut, Scan, Trash } from "lucide-solid";
+import {
+ ArrowLeftRight,
+ BadgeCheck,
+ LogOut,
+ RefreshCw,
+ Scan,
+ Trash
+} from "lucide-solid";
import {
createResource,
createSignal,
@@ -45,7 +53,7 @@ import {
} from "~/components";
import { useI18n } from "~/i18n/context";
import { useMegaStore } from "~/state/megaStore";
-import { eify, timeAgo } from "~/utils";
+import { eify, timeAgo, timeout } from "~/utils";
type FederationForm = {
federation_code: string;
@@ -68,6 +76,12 @@ export type Metadata = {
about?: string;
};
+export type ResyncProgress = {
+ total: number;
+ complete: number;
+ done: boolean;
+};
+
export type DiscoveredFederation = {
id: string;
invite_codes: string[];
@@ -298,6 +312,22 @@ function RecommendButton(props: { fed: MutinyFederationIdentity }) {
);
}
+function ResyncLoadingBar(props: { value: number; max: number }) {
+ return (
+
+
+
+
+
+
+ );
+}
+
function FederationListItem(props: {
fed: MutinyFederationIdentity;
balance?: bigint;
@@ -317,10 +347,37 @@ function FederationListItem(props: {
setConfirmLoading(false);
}
+ async function resyncFederation() {
+ setResyncLoading(true);
+ try {
+ await sw.resync_federation(props.fed.federation_id);
+
+ for (let i = 0; i < 60; i++) {
+ await timeout(1000);
+ const progress = await sw.get_federation_resync_progress(
+ props.fed.federation_id
+ );
+ console.log("progress", progress);
+ if (progress?.total !== 0) {
+ setResyncProgress(progress);
+ setResyncOpen(false);
+ break;
+ }
+ }
+ } catch (e) {
+ console.error(e);
+ setResyncLoading(false);
+ }
+ }
+
async function confirmRemove() {
setConfirmOpen(true);
}
+ async function confirmResync() {
+ setResyncOpen(true);
+ }
+
const [transferDialogOpen, setTransferDialogOpen] = createSignal(false);
async function transferFunds() {
@@ -335,6 +392,13 @@ function FederationListItem(props: {
const [confirmOpen, setConfirmOpen] = createSignal(false);
const [confirmLoading, setConfirmLoading] = createSignal(false);
+ const [resyncOpen, setResyncOpen] = createSignal(false);
+ const [resyncLoading, setResyncLoading] = createSignal(false);
+
+ const [resyncProgress, setResyncProgress] = createSignal<
+ ResyncProgress | undefined
+ >(undefined);
+
return (
<>
@@ -404,6 +468,12 @@ function FederationListItem(props: {
{i18n.t("settings.manage_federations.remove")}
+
+
+
+ Resync
+
+
+ setResyncOpen(false)}
+ >
+ Are you sure you want to resync this federation? This will
+ rescan the federation for your ecash, this can take multiple
+ hours in some cases. If you stop the rescan it can cause your
+ wallet to be bricked. Please be sure you can run the rescan
+ before you start it.
+
+ {/* todo put this in a dialog */}
+
+
+
>
);
}
diff --git a/src/workers/walletWorker.ts b/src/workers/walletWorker.ts
index 338b5102..0b0c86dd 100644
--- a/src/workers/walletWorker.ts
+++ b/src/workers/walletWorker.ts
@@ -25,7 +25,8 @@ import { MutinyWalletSettingStrings } from "~/logic/mutinyWalletSetup";
import { FakeDirectMessage, OnChainTx } from "~/routes";
import {
DiscoveredFederation,
- MutinyFederationIdentity
+ MutinyFederationIdentity,
+ ResyncProgress
} from "~/routes/settings";
const RELEASE_VERSION = import.meta.env.__RELEASE_VERSION__;
@@ -1201,6 +1202,26 @@ export async function remove_federation(federation_id: string): Promise {
await wallet!.remove_federation(federation_id);
}
+/**
+ * Resyncs a federation
+ * @param {string} federation_id
+ * @returns {Promise}
+ */
+export async function resync_federation(federation_id: string): Promise {
+ await wallet!.resync_federation(federation_id);
+}
+
+/**
+ * Gets the resync progress for a federation
+ * @param {string} federation_id
+ * @returns {Promise}
+ */
+export async function get_federation_resync_progress(
+ federation_id: string
+): Promise {
+ return wallet!.get_federation_resync_progress(federation_id);
+}
+
/**
* Opens a channel from our selected node to the given pubkey.
* The amount is in satoshis.