mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-05-02 06:46:06 -04:00
feat(tauri): Initialize Context in background (#59)
This PR does the following: - The Context (including Bitcoin wallet, Monero wallet, ...) is initialized in the background. This allows the window to be displayed instantly upon startup. - Host sends events to Guest about progress of Context initialization. Those events are used to display an alert in the navigation bar. - If a Tauri command is invoked which requires the Context to be available, an error will be returned - As soon as the Context becomes available the `Guest` requests the history and Bitcoin balance - Re-enables Material UI animations
This commit is contained in:
parent
792fbbf746
commit
e4141c763b
17 changed files with 369 additions and 191 deletions
|
@ -26,19 +26,11 @@ const theme = createTheme({
|
|||
},
|
||||
secondary: indigo,
|
||||
},
|
||||
transitions: {
|
||||
create: () => "none",
|
||||
},
|
||||
props: {
|
||||
MuiButtonBase: {
|
||||
disableRipple: true,
|
||||
},
|
||||
},
|
||||
typography: {
|
||||
overline: {
|
||||
textTransform: 'none', // This prevents the text from being all caps
|
||||
textTransform: "none", // This prevents the text from being all caps
|
||||
},
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
function InnerContent() {
|
||||
|
|
|
@ -1,33 +1,44 @@
|
|||
import { Button, ButtonProps, IconButton } from "@material-ui/core";
|
||||
import {
|
||||
Button,
|
||||
ButtonProps,
|
||||
IconButton,
|
||||
IconButtonProps,
|
||||
Tooltip,
|
||||
} from "@material-ui/core";
|
||||
import CircularProgress from "@material-ui/core/CircularProgress";
|
||||
import { useSnackbar } from "notistack";
|
||||
import { ReactNode, useState } from "react";
|
||||
import { useIsContextAvailable } from "store/hooks";
|
||||
|
||||
interface PromiseInvokeButtonProps<T> {
|
||||
onSuccess?: (data: T) => void;
|
||||
onSuccess: (data: T) => void | null;
|
||||
onClick: () => Promise<T>;
|
||||
onPendingChange?: (isPending: boolean) => void;
|
||||
isLoadingOverride?: boolean;
|
||||
isIconButton?: boolean;
|
||||
loadIcon?: ReactNode;
|
||||
disabled?: boolean;
|
||||
displayErrorSnackbar?: boolean;
|
||||
tooltipTitle?: string;
|
||||
onPendingChange: (isPending: boolean) => void | null;
|
||||
isLoadingOverride: boolean;
|
||||
isIconButton: boolean;
|
||||
loadIcon: ReactNode;
|
||||
disabled: boolean;
|
||||
displayErrorSnackbar: boolean;
|
||||
tooltipTitle: string | null;
|
||||
requiresContext: boolean;
|
||||
}
|
||||
|
||||
export default function PromiseInvokeButton<T>({
|
||||
disabled,
|
||||
onSuccess,
|
||||
disabled = false,
|
||||
onSuccess = null,
|
||||
onClick,
|
||||
endIcon,
|
||||
loadIcon,
|
||||
isLoadingOverride,
|
||||
isIconButton,
|
||||
displayErrorSnackbar,
|
||||
onPendingChange,
|
||||
loadIcon = null,
|
||||
isLoadingOverride = false,
|
||||
isIconButton = false,
|
||||
displayErrorSnackbar = false,
|
||||
onPendingChange = null,
|
||||
requiresContext = true,
|
||||
tooltipTitle = null,
|
||||
...rest
|
||||
}: ButtonProps & PromiseInvokeButtonProps<T>) {
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
const isContextAvailable = useIsContextAvailable();
|
||||
|
||||
const [isPending, setIsPending] = useState(false);
|
||||
|
||||
|
@ -36,7 +47,7 @@ export default function PromiseInvokeButton<T>({
|
|||
? loadIcon || <CircularProgress size={24} />
|
||||
: endIcon;
|
||||
|
||||
async function handleClick(event: React.MouseEvent<HTMLButtonElement>) {
|
||||
async function handleClick() {
|
||||
if (!isPending) {
|
||||
try {
|
||||
onPendingChange?.(true);
|
||||
|
@ -57,18 +68,34 @@ export default function PromiseInvokeButton<T>({
|
|||
}
|
||||
}
|
||||
|
||||
const isDisabled = disabled || isLoading;
|
||||
const requiresContextButNotAvailable = requiresContext && !isContextAvailable;
|
||||
const isDisabled = disabled || isLoading || requiresContextButNotAvailable;
|
||||
|
||||
return isIconButton ? (
|
||||
<IconButton onClick={handleClick} disabled={isDisabled} {...(rest as any)}>
|
||||
{actualEndIcon}
|
||||
</IconButton>
|
||||
) : (
|
||||
<Button
|
||||
onClick={handleClick}
|
||||
disabled={isDisabled}
|
||||
endIcon={actualEndIcon}
|
||||
{...rest}
|
||||
/>
|
||||
const actualTooltipTitle =
|
||||
(requiresContextButNotAvailable
|
||||
? "Wait for the application to load all required components"
|
||||
: tooltipTitle) ?? "";
|
||||
|
||||
return (
|
||||
<Tooltip title={actualTooltipTitle}>
|
||||
<span>
|
||||
{isIconButton ? (
|
||||
<IconButton
|
||||
onClick={handleClick}
|
||||
disabled={isDisabled}
|
||||
{...(rest as IconButtonProps)}
|
||||
>
|
||||
{actualEndIcon}
|
||||
</IconButton>
|
||||
) : (
|
||||
<Button
|
||||
onClick={handleClick}
|
||||
disabled={isDisabled}
|
||||
endIcon={actualEndIcon}
|
||||
{...rest}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
|
76
src-gui/src/renderer/components/alert/DaemonStatusAlert.tsx
Normal file
76
src-gui/src/renderer/components/alert/DaemonStatusAlert.tsx
Normal file
|
@ -0,0 +1,76 @@
|
|||
import { CircularProgress } from "@material-ui/core";
|
||||
import { Alert, AlertProps } from "@material-ui/lab";
|
||||
import { TauriContextInitializationProgress } from "models/tauriModel";
|
||||
import { useState } from "react";
|
||||
import { useAppSelector } from "store/hooks";
|
||||
import { exhaustiveGuard } from "utils/typescriptUtils";
|
||||
|
||||
const FUNNY_INIT_MESSAGES = [
|
||||
"Initializing quantum entanglement...",
|
||||
"Generating one-time pads from cosmic background radiation...",
|
||||
"Negotiating key exchange with aliens...",
|
||||
"Optimizing elliptic curves for maximum sneakiness...",
|
||||
"Transforming plaintext into ciphertext via arcane XOR rituals...",
|
||||
"Salting your hash with exotic mathematical seasonings...",
|
||||
"Performing advanced modular arithmetic gymnastics...",
|
||||
"Consulting the Oracle of Randomness...",
|
||||
"Executing top-secret permutation protocols...",
|
||||
"Summoning prime factors from the mathematical aether...",
|
||||
"Deploying steganographic squirrels to hide your nuts of data...",
|
||||
"Initializing the quantum superposition of your keys...",
|
||||
"Applying post-quantum cryptographic voodoo...",
|
||||
"Encrypting your data with the tears of frustrated regulators...",
|
||||
];
|
||||
|
||||
function LoadingSpinnerAlert({ ...rest }: AlertProps) {
|
||||
return <Alert icon={<CircularProgress size={22} />} {...rest} />;
|
||||
}
|
||||
|
||||
export default function DaemonStatusAlert() {
|
||||
const contextStatus = useAppSelector((s) => s.rpc.status);
|
||||
|
||||
const [initMessage] = useState(
|
||||
FUNNY_INIT_MESSAGES[Math.floor(Math.random() * FUNNY_INIT_MESSAGES.length)],
|
||||
);
|
||||
|
||||
if (contextStatus == null) {
|
||||
return (
|
||||
<LoadingSpinnerAlert severity="warning">
|
||||
{initMessage}
|
||||
</LoadingSpinnerAlert>
|
||||
);
|
||||
}
|
||||
|
||||
switch (contextStatus.type) {
|
||||
case "Initializing":
|
||||
switch (contextStatus.content) {
|
||||
case TauriContextInitializationProgress.OpeningBitcoinWallet:
|
||||
return (
|
||||
<LoadingSpinnerAlert severity="warning">
|
||||
Connecting to the Bitcoin network
|
||||
</LoadingSpinnerAlert>
|
||||
);
|
||||
case TauriContextInitializationProgress.OpeningMoneroWallet:
|
||||
return (
|
||||
<LoadingSpinnerAlert severity="warning">
|
||||
Connecting to the Monero network
|
||||
</LoadingSpinnerAlert>
|
||||
);
|
||||
case TauriContextInitializationProgress.OpeningDatabase:
|
||||
return (
|
||||
<LoadingSpinnerAlert severity="warning">
|
||||
Opening the local database
|
||||
</LoadingSpinnerAlert>
|
||||
);
|
||||
}
|
||||
break;
|
||||
case "Available":
|
||||
return <Alert severity="success">The daemon is running</Alert>;
|
||||
case "Failed":
|
||||
return (
|
||||
<Alert severity="error">The daemon has stopped unexpectedly</Alert>
|
||||
);
|
||||
default:
|
||||
return exhaustiveGuard(contextStatus);
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
import { CircularProgress } from "@material-ui/core";
|
||||
import { Alert } from "@material-ui/lab";
|
||||
import { RpcProcessStateType } from "models/rpcModel";
|
||||
import { useAppSelector } from "store/hooks";
|
||||
|
||||
// TODO: Reimplement this using Tauri
|
||||
// Currently the RPC process is always available, so this component is not needed
|
||||
// since the UI is only displayed when the RPC process is available
|
||||
export default function RpcStatusAlert() {
|
||||
const rpcProcess = useAppSelector((s) => s.rpc.process);
|
||||
if (rpcProcess.type === RpcProcessStateType.STARTED) {
|
||||
return (
|
||||
<Alert severity="warning" icon={<CircularProgress size={22} />}>
|
||||
The swap daemon is starting
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
if (rpcProcess.type === RpcProcessStateType.LISTENING_FOR_CONNECTIONS) {
|
||||
return <Alert severity="success">The swap daemon is running</Alert>;
|
||||
}
|
||||
if (rpcProcess.type === RpcProcessStateType.NOT_STARTED) {
|
||||
return <Alert severity="warning">The swap daemon is being started</Alert>;
|
||||
}
|
||||
if (rpcProcess.type === RpcProcessStateType.EXITED) {
|
||||
return (
|
||||
<Alert severity="error">The swap daemon has stopped unexpectedly</Alert>
|
||||
);
|
||||
}
|
||||
return <></>;
|
||||
}
|
|
@ -72,6 +72,7 @@ export default function InitPage() {
|
|||
className={classes.initButton}
|
||||
endIcon={<PlayArrowIcon />}
|
||||
onClick={init}
|
||||
displayErrorSnackbar
|
||||
>
|
||||
Start swap
|
||||
</PromiseInvokeButton>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Box, makeStyles } from "@material-ui/core";
|
||||
import GitHubIcon from "@material-ui/icons/GitHub";
|
||||
import RedditIcon from "@material-ui/icons/Reddit";
|
||||
import DaemonStatusAlert from "../alert/DaemonStatusAlert";
|
||||
import FundsLeftInWalletAlert from "../alert/FundsLeftInWalletAlert";
|
||||
import MoneroWalletRpcUpdatingAlert from "../alert/MoneroWalletRpcUpdatingAlert";
|
||||
import UnfinishedSwapsAlert from "../alert/UnfinishedSwapsAlert";
|
||||
|
@ -28,11 +29,7 @@ export default function NavigationFooter() {
|
|||
<Box className={classes.outer}>
|
||||
<FundsLeftInWalletAlert />
|
||||
<UnfinishedSwapsAlert />
|
||||
|
||||
{
|
||||
// TODO: Uncomment when we have implemented a way for the UI to be displayed before the context has been initialized
|
||||
// <RpcStatusAlert />
|
||||
}
|
||||
<DaemonStatusAlert />
|
||||
<MoneroWalletRpcUpdatingAlert />
|
||||
<Box className={classes.linksOuter}>
|
||||
<LinkIconButton url="https://reddit.com/r/unstoppableswap">
|
||||
|
|
|
@ -2,9 +2,8 @@ import { Box, makeStyles } from "@material-ui/core";
|
|||
import FolderOpenIcon from "@material-ui/icons/FolderOpen";
|
||||
import PlayArrowIcon from "@material-ui/icons/PlayArrow";
|
||||
import StopIcon from "@material-ui/icons/Stop";
|
||||
import { RpcProcessStateType } from "models/rpcModel";
|
||||
import PromiseInvokeButton from "renderer/components/PromiseInvokeButton";
|
||||
import { useAppSelector } from "store/hooks";
|
||||
import { useIsContextAvailable } from "store/hooks";
|
||||
import InfoBox from "../../modal/swap/InfoBox";
|
||||
import CliLogsBox from "../../other/RenderedCliLog";
|
||||
|
||||
|
@ -17,20 +16,17 @@ const useStyles = makeStyles((theme) => ({
|
|||
}));
|
||||
|
||||
export default function RpcControlBox() {
|
||||
const rpcProcess = useAppSelector((state) => state.rpc.process);
|
||||
const isRunning =
|
||||
rpcProcess.type === RpcProcessStateType.STARTED ||
|
||||
rpcProcess.type === RpcProcessStateType.LISTENING_FOR_CONNECTIONS;
|
||||
const isRunning = useIsContextAvailable();
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<InfoBox
|
||||
title={`Swap Daemon (${rpcProcess.type})`}
|
||||
title={`Daemon Controller`}
|
||||
mainContent={
|
||||
isRunning || rpcProcess.type === RpcProcessStateType.EXITED ? (
|
||||
isRunning ? (
|
||||
<CliLogsBox
|
||||
label="Swap Daemon Logs (current session only)"
|
||||
logs={rpcProcess.logs}
|
||||
logs={[]}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
import {
|
||||
Box,
|
||||
Link,
|
||||
makeStyles,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableRow,
|
||||
Box,
|
||||
Link,
|
||||
makeStyles,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableRow,
|
||||
} from "@material-ui/core";
|
||||
import { OpenInNew } from "@material-ui/icons";
|
||||
import { GetSwapInfoResponse } from "models/tauriModel";
|
||||
import CopyableMonospaceTextBox from "renderer/components/other/CopyableAddress";
|
||||
import MonospaceTextBox from "renderer/components/other/InlineCode";
|
||||
import CopyableMonospaceTextBox from "renderer/components/other/CopyableMonospaceTextBox";
|
||||
import MonospaceTextBox from "renderer/components/other/MonospaceTextBox";
|
||||
import {
|
||||
MoneroBitcoinExchangeRate,
|
||||
PiconeroAmount,
|
||||
SatsAmount,
|
||||
MoneroBitcoinExchangeRate,
|
||||
PiconeroAmount,
|
||||
SatsAmount,
|
||||
} from "renderer/components/other/Units";
|
||||
import { isTestnet } from "store/config";
|
||||
import { getBitcoinTxExplorerUrl } from "utils/conversionUtils";
|
||||
|
|
|
@ -12,12 +12,16 @@ import {
|
|||
fetchXmrPrice,
|
||||
} from "./api";
|
||||
import App from "./components/App";
|
||||
import { checkBitcoinBalance, getRawSwapInfos } from "./rpc";
|
||||
import {
|
||||
checkBitcoinBalance,
|
||||
getAllSwapInfos,
|
||||
initEventListeners,
|
||||
} from "./rpc";
|
||||
import { persistor, store } from "./store/storeRenderer";
|
||||
|
||||
setInterval(() => {
|
||||
checkBitcoinBalance();
|
||||
getRawSwapInfos();
|
||||
getAllSwapInfos();
|
||||
}, 30 * 1000);
|
||||
|
||||
const container = document.getElementById("root");
|
||||
|
|
|
@ -9,11 +9,16 @@ import {
|
|||
ResumeSwapArgs,
|
||||
ResumeSwapResponse,
|
||||
SuspendCurrentSwapResponse,
|
||||
TauriContextStatusEvent,
|
||||
TauriSwapProgressEventWrapper,
|
||||
WithdrawBtcArgs,
|
||||
WithdrawBtcResponse,
|
||||
} from "models/tauriModel";
|
||||
import { rpcSetBalance, rpcSetSwapInfo } from "store/features/rpcSlice";
|
||||
import {
|
||||
contextStatusEventReceived,
|
||||
rpcSetBalance,
|
||||
rpcSetSwapInfo,
|
||||
} from "store/features/rpcSlice";
|
||||
import { swapTauriEventReceived } from "store/features/swapSlice";
|
||||
import { store } from "./store/storeRenderer";
|
||||
import { Provider } from "models/apiModel";
|
||||
|
@ -24,6 +29,11 @@ export async function initEventListeners() {
|
|||
console.log("Received swap progress event", event.payload);
|
||||
store.dispatch(swapTauriEventReceived(event.payload));
|
||||
});
|
||||
|
||||
listen<TauriContextStatusEvent>("context-init-progress-update", (event) => {
|
||||
console.log("Received context init progress event", event.payload);
|
||||
store.dispatch(contextStatusEventReceived(event.payload));
|
||||
});
|
||||
}
|
||||
|
||||
async function invoke<ARGS, RESPONSE>(
|
||||
|
@ -47,7 +57,7 @@ export async function checkBitcoinBalance() {
|
|||
store.dispatch(rpcSetBalance(response.balance));
|
||||
}
|
||||
|
||||
export async function getRawSwapInfos() {
|
||||
export async function getAllSwapInfos() {
|
||||
const response =
|
||||
await invokeNoArgs<GetSwapInfoResponse[]>("get_swap_infos_all");
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import { combineReducers, configureStore } from "@reduxjs/toolkit";
|
|||
import { persistReducer, persistStore } from "redux-persist";
|
||||
import sessionStorage from "redux-persist/lib/storage/session";
|
||||
import { reducers } from "store/combinedReducer";
|
||||
import { createMainListeners } from "store/middleware/storeListener";
|
||||
|
||||
// We persist the redux store in sessionStorage
|
||||
// The point of this is to preserve the store across reloads while not persisting it across GUI restarts
|
||||
|
@ -20,6 +21,8 @@ const persistedReducer = persistReducer(
|
|||
|
||||
export const store = configureStore({
|
||||
reducer: persistedReducer,
|
||||
middleware: (getDefaultMiddleware) =>
|
||||
getDefaultMiddleware().prepend(createMainListeners().middleware),
|
||||
});
|
||||
|
||||
export const persistor = persistStore(store);
|
||||
|
|
|
@ -1,32 +1,12 @@
|
|||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
import { ExtendedProviderStatus, ProviderStatus } from "models/apiModel";
|
||||
import { GetSwapInfoResponse } from "models/tauriModel";
|
||||
import { CliLog } from "../../models/cliModel";
|
||||
import {
|
||||
MoneroRecoveryResponse,
|
||||
RpcProcessStateType,
|
||||
} from "../../models/rpcModel";
|
||||
GetSwapInfoResponse,
|
||||
TauriContextStatusEvent,
|
||||
} from "models/tauriModel";
|
||||
import { MoneroRecoveryResponse } from "../../models/rpcModel";
|
||||
import { GetSwapInfoResponseExt } from "models/tauriModelExt";
|
||||
|
||||
type Process =
|
||||
| {
|
||||
type: RpcProcessStateType.STARTED;
|
||||
logs: (CliLog | string)[];
|
||||
}
|
||||
| {
|
||||
type: RpcProcessStateType.LISTENING_FOR_CONNECTIONS;
|
||||
logs: (CliLog | string)[];
|
||||
address: string;
|
||||
}
|
||||
| {
|
||||
type: RpcProcessStateType.EXITED;
|
||||
logs: (CliLog | string)[];
|
||||
exitCode: number | null;
|
||||
}
|
||||
| {
|
||||
type: RpcProcessStateType.NOT_STARTED;
|
||||
};
|
||||
|
||||
interface State {
|
||||
balance: number | null;
|
||||
withdrawTxId: string | null;
|
||||
|
@ -48,15 +28,13 @@ interface State {
|
|||
}
|
||||
|
||||
export interface RPCSlice {
|
||||
process: Process;
|
||||
status: TauriContextStatusEvent | null;
|
||||
state: State;
|
||||
busyEndpoints: string[];
|
||||
}
|
||||
|
||||
const initialState: RPCSlice = {
|
||||
process: {
|
||||
type: RpcProcessStateType.NOT_STARTED,
|
||||
},
|
||||
status: null,
|
||||
state: {
|
||||
balance: null,
|
||||
withdrawTxId: null,
|
||||
|
@ -77,35 +55,11 @@ export const rpcSlice = createSlice({
|
|||
name: "rpc",
|
||||
initialState,
|
||||
reducers: {
|
||||
rpcInitiate(slice) {
|
||||
slice.process = {
|
||||
type: RpcProcessStateType.STARTED,
|
||||
logs: [],
|
||||
};
|
||||
},
|
||||
rpcProcessExited(
|
||||
contextStatusEventReceived(
|
||||
slice,
|
||||
action: PayloadAction<{
|
||||
exitCode: number | null;
|
||||
exitSignal: NodeJS.Signals | null;
|
||||
}>,
|
||||
action: PayloadAction<TauriContextStatusEvent>,
|
||||
) {
|
||||
if (
|
||||
slice.process.type === RpcProcessStateType.STARTED ||
|
||||
slice.process.type === RpcProcessStateType.LISTENING_FOR_CONNECTIONS
|
||||
) {
|
||||
slice.process = {
|
||||
type: RpcProcessStateType.EXITED,
|
||||
logs: slice.process.logs,
|
||||
exitCode: action.payload.exitCode,
|
||||
};
|
||||
slice.state.moneroWalletRpc = {
|
||||
updateState: false,
|
||||
};
|
||||
slice.state.moneroWallet = {
|
||||
isSyncing: false,
|
||||
};
|
||||
}
|
||||
slice.status = action.payload;
|
||||
},
|
||||
rpcSetBalance(slice, action: PayloadAction<number>) {
|
||||
slice.state.balance = action.payload;
|
||||
|
@ -156,8 +110,7 @@ export const rpcSlice = createSlice({
|
|||
});
|
||||
|
||||
export const {
|
||||
rpcProcessExited,
|
||||
rpcInitiate,
|
||||
contextStatusEventReceived,
|
||||
rpcSetBalance,
|
||||
rpcSetWithdrawTxId,
|
||||
rpcResetWithdrawTxId,
|
||||
|
|
|
@ -23,6 +23,10 @@ export function useIsSwapRunning() {
|
|||
);
|
||||
}
|
||||
|
||||
export function useIsContextAvailable() {
|
||||
return useAppSelector((state) => state.rpc.status?.type === "Available");
|
||||
}
|
||||
|
||||
export function useSwapInfo(swapId: string | null) {
|
||||
return useAppSelector((state) =>
|
||||
swapId ? (state.rpc.state.swapInfos[swapId] ?? null) : null,
|
||||
|
|
28
src-gui/src/store/middleware/storeListener.ts
Normal file
28
src-gui/src/store/middleware/storeListener.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { createListenerMiddleware } from "@reduxjs/toolkit";
|
||||
import { getAllSwapInfos, checkBitcoinBalance } from "renderer/rpc";
|
||||
import logger from "utils/logger";
|
||||
import { contextStatusEventReceived } from "store/features/rpcSlice";
|
||||
|
||||
export function createMainListeners() {
|
||||
const listener = createListenerMiddleware();
|
||||
|
||||
// Listener for when the Context becomes available
|
||||
// When the context becomes available, we check the bitcoin balance and fetch all swap infos
|
||||
listener.startListening({
|
||||
actionCreator: contextStatusEventReceived,
|
||||
effect: async (action) => {
|
||||
const status = action.payload;
|
||||
|
||||
// If the context is available, check the bitcoin balance and fetch all swap infos
|
||||
if (status.type === "Available") {
|
||||
logger.debug(
|
||||
"Context is available, checking bitcoin balance and history",
|
||||
);
|
||||
await checkBitcoinBalance();
|
||||
await getAllSwapInfos();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return listener;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue