feat(cli+tauri): Send logs from host to guest (#90)

* feat(tauri): send logs from cli to tauri

---------

Co-authored-by: binarybaron <binarybaron@unstoppableswap.net>
Co-authored-by: binarybaron <86064887+binarybaron@users.noreply.github.com>
This commit is contained in:
Einliterflasche 2024-09-26 13:09:46 +02:00 committed by GitHub
parent 21608ce4f7
commit 7b79ad6abe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 206 additions and 136 deletions

View file

@ -37,7 +37,7 @@ export function parseCliLogString(log: string): CliLog | string {
} else {
return log;
}
} catch (err) {
} catch {
return log;
}
}

View file

@ -1,7 +1,6 @@
import {
Avatar,
Button,
CircularProgress,
Dialog,
DialogActions,
DialogContent,
@ -15,14 +14,9 @@ import {
import AddIcon from "@material-ui/icons/Add";
import SearchIcon from "@material-ui/icons/Search";
import { ExtendedProviderStatus } from "models/apiModel";
import { RpcMethod } from "models/rpcModel";
import { useState } from "react";
import { setSelectedProvider } from "store/features/providersSlice";
import {
useAllProviders,
useAppDispatch,
useIsRpcEndpointBusy,
} from "store/hooks";
import { useAllProviders, useAppDispatch } from "store/hooks";
import ListSellersDialog from "../listSellers/ListSellersDialog";
import ProviderInfo from "./ProviderInfo";
import ProviderSubmitDialog from "./ProviderSubmitDialog";
@ -65,13 +59,11 @@ export function ProviderSubmitDialogOpenButton() {
export function ListSellersDialogOpenButton() {
const [open, setOpen] = useState(false);
const running = useIsRpcEndpointBusy(RpcMethod.LIST_SELLERS);
return (
<ListItem
autoFocus
button
disabled={running}
onClick={() => {
// Prevents background from being clicked and reopening dialog
if (!open) {
@ -81,7 +73,9 @@ export function ListSellersDialogOpenButton() {
>
<ListSellersDialog open={open} onClose={() => setOpen(false)} />
<ListItemAvatar>
<Avatar>{running ? <CircularProgress /> : <SearchIcon />}</Avatar>
<Avatar>
<SearchIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="Discover providers by connecting to a rendezvous point" />
</ListItem>

View file

@ -3,7 +3,7 @@ import FolderOpenIcon from "@material-ui/icons/FolderOpen";
import PlayArrowIcon from "@material-ui/icons/PlayArrow";
import StopIcon from "@material-ui/icons/Stop";
import PromiseInvokeButton from "renderer/components/PromiseInvokeButton";
import { useIsContextAvailable } from "store/hooks";
import { useAppSelector, useIsContextAvailable } from "store/hooks";
import InfoBox from "../../modal/swap/InfoBox";
import CliLogsBox from "../../other/RenderedCliLog";
@ -18,17 +18,16 @@ const useStyles = makeStyles((theme) => ({
export default function RpcControlBox() {
const isRunning = useIsContextAvailable();
const classes = useStyles();
const logs = useAppSelector((s) => s.rpc.logs);
return (
<InfoBox
title={`Daemon Controller`}
mainContent={
isRunning ? (
<CliLogsBox
label="Swap Daemon Logs (current session only)"
logs={[]}
/>
) : null
<CliLogsBox
label="Swap Daemon Logs (current session only)"
logs={logs}
/>
}
additionalContent={
<Box className={classes.actionsOuter}>

View file

@ -3,7 +3,7 @@ import SendIcon from "@material-ui/icons/Send";
import { RpcMethod } from "models/rpcModel";
import { useState } from "react";
import { SatsAmount } from "renderer/components/other/Units";
import { useAppSelector, useIsRpcEndpointBusy } from "store/hooks";
import { useAppSelector } from "store/hooks";
import BitcoinIcon from "../../icons/BitcoinIcon";
import InfoBox from "../../modal/swap/InfoBox";
import WithdrawDialog from "../../modal/wallet/WithdrawDialog";
@ -20,7 +20,6 @@ const useStyles = makeStyles((theme) => ({
export default function WithdrawWidget() {
const classes = useStyles();
const walletBalance = useAppSelector((state) => state.rpc.state.balance);
const checkingBalance = useIsRpcEndpointBusy(RpcMethod.GET_BTC_BALANCE);
const [showDialog, setShowDialog] = useState(false);
function onShowDialog() {
@ -50,7 +49,7 @@ export default function WithdrawWidget() {
size="large"
onClick={onShowDialog}
disabled={
walletBalance === null || checkingBalance || walletBalance <= 0
walletBalance === null || walletBalance <= 0
}
>
Withdraw

View file

@ -5,6 +5,7 @@ import {
BalanceResponse,
BuyXmrArgs,
BuyXmrResponse,
CliLogEmittedEvent,
GetLogsArgs,
GetLogsResponse,
GetSwapInfoResponse,
@ -20,6 +21,7 @@ import {
} from "models/tauriModel";
import {
contextStatusEventReceived,
receivedCliLog,
rpcSetBalance,
rpcSetSwapInfo,
} from "store/features/rpcSlice";
@ -47,6 +49,11 @@ export async function initEventListeners() {
console.log("Received context init progress event", event.payload);
store.dispatch(contextStatusEventReceived(event.payload));
});
listen<CliLogEmittedEvent>("cli-log-emitted", (event) => {
console.log("Received cli log event", event.payload);
store.dispatch(receivedCliLog(event.payload))
})
}
async function invoke<ARGS, RESPONSE>(

View file

@ -1,11 +1,14 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { ExtendedProviderStatus, ProviderStatus } from "models/apiModel";
import {
CliLogEmittedEvent,
GetSwapInfoResponse,
TauriContextStatusEvent,
} from "models/tauriModel";
import { MoneroRecoveryResponse } from "../../models/rpcModel";
import { GetSwapInfoResponseExt } from "models/tauriModelExt";
import { getLogsAndStringsFromRawFileString } from "utils/parseUtils";
import { CliLog } from "models/cliModel";
interface State {
balance: number | null;
@ -27,7 +30,7 @@ interface State {
export interface RPCSlice {
status: TauriContextStatusEvent | null;
state: State;
busyEndpoints: string[];
logs: (CliLog | string)[];
}
const initialState: RPCSlice = {
@ -42,13 +45,18 @@ const initialState: RPCSlice = {
updateState: false,
},
},
busyEndpoints: [],
logs: [],
};
export const rpcSlice = createSlice({
name: "rpc",
initialState,
reducers: {
receivedCliLog(slice, action: PayloadAction<CliLogEmittedEvent>) {
const buffer = action.payload.buffer;
const logs = getLogsAndStringsFromRawFileString(buffer);
slice.logs = slice.logs.concat(logs);
},
contextStatusEventReceived(
slice,
action: PayloadAction<TauriContextStatusEvent>,
@ -74,17 +82,6 @@ export const rpcSlice = createSlice({
slice.state.swapInfos[action.payload.swap_id] =
action.payload as GetSwapInfoResponseExt;
},
rpcSetEndpointBusy(slice, action: PayloadAction<string>) {
if (!slice.busyEndpoints.includes(action.payload)) {
slice.busyEndpoints.push(action.payload);
}
},
rpcSetEndpointFree(slice, action: PayloadAction<string>) {
const index = slice.busyEndpoints.indexOf(action.payload);
if (index >= 0) {
slice.busyEndpoints.splice(index);
}
},
rpcSetMoneroRecoveryKeys(
slice,
action: PayloadAction<[string, MoneroRecoveryResponse]>,
@ -105,11 +102,10 @@ export const rpcSlice = createSlice({
export const {
contextStatusEventReceived,
receivedCliLog,
rpcSetBalance,
rpcSetWithdrawTxId,
rpcResetWithdrawTxId,
rpcSetEndpointBusy,
rpcSetEndpointFree,
rpcSetRendezvousDiscoveredProviders,
rpcSetSwapInfo,
rpcSetMoneroRecoveryKeys,

View file

@ -42,10 +42,6 @@ export function useActiveSwapInfo() {
return useSwapInfo(swapId);
}
export function useIsRpcEndpointBusy(method: string) {
return useAppSelector((state) => state.rpc.busyEndpoints.includes(method));
}
export function useAllProviders() {
return useAppSelector((state) => {
const registryProviders = state.providers.registry.providers || [];

View file

@ -1,4 +1,4 @@
import { CliLog } from "models/cliModel";
import { CliLog, parseCliLogString } from "models/cliModel";
import { Multiaddr } from "multiaddr";
/*
@ -55,19 +55,7 @@ export function getLinesOfString(data: string): string[] {
export function getLogsAndStringsFromRawFileString(
rawFileData: string,
): (CliLog | string)[] {
return getLinesOfString(rawFileData).map((line) => {
try {
return JSON.parse(line);
} catch {
return line;
}
});
}
export function getLogsFromRawFileString(rawFileData: string): CliLog[] {
// TODO: Reimplement this using Tauri
return [];
return getLogsAndStringsFromRawFileString(rawFileData).filter(isCliLog);
return getLinesOfString(rawFileData).map(parseCliLogString);
}
export function logsToRawString(logs: (CliLog | string)[]): string {