mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-08-22 21:15:21 -04:00
Merge changes from legacy GUI, allow daemon logs to be attached to feedback (#115)
This PR applies all remaining changes from https://github.com/UnstoppableSwap/unstoppableswap-gui/pull/210 - Added checkbox option to attach daemon logs when submitting feedback - Added "Outdated" chip with warning icon for providers running outdated asb versions - Updated `BitcoinPunishedPage` to display different messages for BtcPunished and CooperativeRedeemRejected states (including reason for failed cooperative redeem) - Added "Attempt recovery" button for swaps in BtcPunished state - Modified `getBitcoinTxExplorerUrl` to use mempool.space instead of blockchair.com - Added `useResumeableSwapsCountExcludingPunished` hook to count resumable swaps excluding punished ones, use it for the badge and alert - Updated `sortProviderList` function to filter out incompatible providers before sorting - Added `TauriSwapProgressEventExt` type to extract specific event types from TauriSwapProgressEvent
This commit is contained in:
parent
639f540876
commit
2bffe40a37
12 changed files with 116 additions and 50 deletions
|
@ -1,10 +1,10 @@
|
|||
import { Button } from "@material-ui/core";
|
||||
import Alert from "@material-ui/lab/Alert";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useResumeableSwapsCount } from "store/hooks";
|
||||
import { useResumeableSwapsCountExcludingPunished } from "store/hooks";
|
||||
|
||||
export default function UnfinishedSwapsAlert() {
|
||||
const resumableSwapsCount = useResumeableSwapsCount();
|
||||
const resumableSwapsCount = useResumeableSwapsCountExcludingPunished();
|
||||
const navigate = useNavigate();
|
||||
|
||||
if (resumableSwapsCount > 0) {
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
import {
|
||||
Box,
|
||||
Button,
|
||||
Checkbox,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogTitle,
|
||||
FormControl,
|
||||
FormControlLabel,
|
||||
MenuItem,
|
||||
Paper,
|
||||
Select,
|
||||
TextField,
|
||||
} from "@material-ui/core";
|
||||
|
@ -21,7 +25,7 @@ import LoadingButton from "../../other/LoadingButton";
|
|||
import { PiconeroAmount } from "../../other/Units";
|
||||
import { getLogsOfSwap } from "renderer/rpc";
|
||||
|
||||
async function submitFeedback(body: string, swapId: string | number) {
|
||||
async function submitFeedback(body: string, swapId: string | number, submitDaemonLogs: boolean) {
|
||||
let attachedBody = "";
|
||||
|
||||
if (swapId !== 0 && typeof swapId === "string") {
|
||||
|
@ -39,6 +43,13 @@ async function submitFeedback(body: string, swapId: string | number) {
|
|||
.join("\n====\n")}`;
|
||||
}
|
||||
|
||||
if (submitDaemonLogs) {
|
||||
const logs = store.getState().rpc?.logs ?? [];
|
||||
attachedBody += `\n\nDaemon Logs: ${logs
|
||||
.map((l) => JSON.stringify(l))
|
||||
.join("\n====\n")}`;
|
||||
}
|
||||
|
||||
await submitFeedbackViaHttp(body, attachedBody);
|
||||
}
|
||||
|
||||
|
@ -66,7 +77,7 @@ function SwapSelectDropDown({
|
|||
variant="outlined"
|
||||
onChange={(e) => setSelectedSwap(e.target.value as string)}
|
||||
>
|
||||
<MenuItem value={0}>Do not attach logs</MenuItem>
|
||||
<MenuItem value={0}>Do not attach a swap</MenuItem>
|
||||
{swaps.map((swap) => (
|
||||
<MenuItem value={swap.swap_id} key={swap.swap_id}>
|
||||
Swap <TruncatedText>{swap.swap_id}</TruncatedText> from{" "}
|
||||
|
@ -96,6 +107,7 @@ export default function FeedbackDialog({
|
|||
const [selectedAttachedSwap, setSelectedAttachedSwap] = useState<
|
||||
string | number
|
||||
>(currentSwapId?.swap_id || 0);
|
||||
const [attachDaemonLogs, setAttachDaemonLogs] = useState(true);
|
||||
|
||||
const bodyTooLong = bodyText.length > MAX_FEEDBACK_LENGTH;
|
||||
|
||||
|
@ -106,9 +118,9 @@ export default function FeedbackDialog({
|
|||
<DialogContentText>
|
||||
Got something to say? Drop us a message below. If you had an issue
|
||||
with a specific swap, select it from the dropdown to attach the logs.
|
||||
It will help us figure out what went wrong. Hit that submit button
|
||||
when you are ready. We appreciate you taking the time to share your
|
||||
thoughts!
|
||||
It will help us figure out what went wrong.
|
||||
<br />
|
||||
We appreciate you taking the time to share your thoughts! Every feedback is read by a core developer!
|
||||
</DialogContentText>
|
||||
<Box
|
||||
style={{
|
||||
|
@ -124,7 +136,7 @@ export default function FeedbackDialog({
|
|||
label={
|
||||
bodyTooLong
|
||||
? `Text is too long (${bodyText.length}/${MAX_FEEDBACK_LENGTH})`
|
||||
: "Feedback"
|
||||
: "Message"
|
||||
}
|
||||
multiline
|
||||
minRows={4}
|
||||
|
@ -136,6 +148,18 @@ export default function FeedbackDialog({
|
|||
selectedSwap={selectedAttachedSwap}
|
||||
setSelectedSwap={setSelectedAttachedSwap}
|
||||
/>
|
||||
<Paper variant="outlined" style={{ padding: "0.5rem" }}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
color="primary"
|
||||
checked={attachDaemonLogs}
|
||||
onChange={(e) => setAttachDaemonLogs(e.target.checked)}
|
||||
/>
|
||||
}
|
||||
label="Attach daemon logs"
|
||||
/>
|
||||
</Paper>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
|
@ -150,7 +174,7 @@ export default function FeedbackDialog({
|
|||
|
||||
try {
|
||||
setPending(true);
|
||||
await submitFeedback(bodyText, selectedAttachedSwap);
|
||||
await submitFeedback(bodyText, selectedAttachedSwap, attachDaemonLogs);
|
||||
enqueueSnackbar("Feedback submitted successfully!", {
|
||||
variant: "success",
|
||||
});
|
||||
|
|
|
@ -7,6 +7,8 @@ import {
|
|||
SatsAmount,
|
||||
} from "renderer/components/other/Units";
|
||||
import { satsToBtc, secondsToDays } from "utils/conversionUtils";
|
||||
import { isProviderOutdated } from 'utils/multiAddrUtils';
|
||||
import WarningIcon from '@material-ui/icons/Warning';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
content: {
|
||||
|
@ -29,6 +31,7 @@ export default function ProviderInfo({
|
|||
provider: ExtendedProviderStatus;
|
||||
}) {
|
||||
const classes = useStyles();
|
||||
const isOutdated = isProviderOutdated(provider);
|
||||
|
||||
return (
|
||||
<Box className={classes.content}>
|
||||
|
@ -70,6 +73,11 @@ export default function ProviderInfo({
|
|||
<Chip label="Recommended" icon={<VerifiedUser />} color="primary" />
|
||||
</Tooltip>
|
||||
)}
|
||||
{isOutdated && (
|
||||
<Tooltip title="This provider is running an outdated version of the software. Outdated providers may be unreliable and cause swaps to take longer to complete or fail entirely.">
|
||||
<Chip label="Outdated" icon={<WarningIcon />} color="primary" />
|
||||
</Tooltip>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Box } from "@material-ui/core";
|
||||
import { SwapSlice, SwapState } from "models/storeModel";
|
||||
import { SwapState } from "models/storeModel";
|
||||
import CircularProgressWithSubtitle from "../CircularProgressWithSubtitle";
|
||||
import BitcoinPunishedPage from "./done/BitcoinPunishedPage";
|
||||
import BitcoinRefundedPage from "./done/BitcoinRefundedPage";
|
||||
|
@ -52,7 +52,7 @@ export default function SwapStatePage({ state }: { state: SwapState | null }) {
|
|||
case "BtcRefunded":
|
||||
return <BitcoinRefundedPage {...state.curr.content} />;
|
||||
case "BtcPunished":
|
||||
return <BitcoinPunishedPage />;
|
||||
return <BitcoinPunishedPage state={state.curr} />;
|
||||
case "AttemptingCooperativeRedeem":
|
||||
return (
|
||||
<CircularProgressWithSubtitle description="Attempting to redeem the Monero with the help of the other party" />
|
||||
|
@ -62,7 +62,7 @@ export default function SwapStatePage({ state }: { state: SwapState | null }) {
|
|||
<CircularProgressWithSubtitle description="The other party is cooperating with us to redeem the Monero..." />
|
||||
);
|
||||
case "CooperativeRedeemRejected":
|
||||
return <BitcoinPunishedPage />;
|
||||
return <BitcoinPunishedPage state={state.curr} />;
|
||||
case "Released":
|
||||
return <ProcessExitedPage prevState={state.prev} swapId={state.swapId} />;
|
||||
default:
|
||||
|
|
|
@ -1,13 +1,27 @@
|
|||
import { Box, DialogContentText } from "@material-ui/core";
|
||||
import FeedbackInfoBox from "../../../../pages/help/FeedbackInfoBox";
|
||||
import { Box, DialogContentText } from '@material-ui/core';
|
||||
import FeedbackInfoBox from '../../../../pages/help/FeedbackInfoBox';
|
||||
import { TauriSwapProgressEventExt } from 'models/tauriModelExt';
|
||||
|
||||
export default function BitcoinPunishedPage() {
|
||||
export default function BitcoinPunishedPage({
|
||||
state,
|
||||
}: {
|
||||
state: TauriSwapProgressEventExt<"BtcPunished"> | TauriSwapProgressEventExt<"CooperativeRedeemRejected">
|
||||
}) {
|
||||
return (
|
||||
<Box>
|
||||
<DialogContentText>
|
||||
Unfortunately, the swap was not successful, and you've incurred a
|
||||
penalty because the swap was not refunded in time. Both the Bitcoin and
|
||||
Monero are irretrievable.
|
||||
Unfortunately, the swap was unsuccessful. Since you did not refund in
|
||||
time, the Bitcoin has been lost. However, with the cooperation of the
|
||||
other party, you might still be able to redeem the Monero, although this
|
||||
is not guaranteed.{' '}
|
||||
{state.type === "CooperativeRedeemRejected" && (
|
||||
<>
|
||||
<br />
|
||||
We tried to redeem the Monero with the other party's help, but it
|
||||
was unsuccessful (reason: {state.content.reason}). Attempting again at a
|
||||
later time might yield success. <br />
|
||||
</>
|
||||
)}
|
||||
</DialogContentText>
|
||||
<FeedbackInfoBox />
|
||||
</Box>
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { Badge } from "@material-ui/core";
|
||||
import { useResumeableSwapsCount } from "store/hooks";
|
||||
import { useResumeableSwapsCountExcludingPunished } from "store/hooks";
|
||||
|
||||
export default function UnfinishedSwapsBadge({
|
||||
children,
|
||||
}: {
|
||||
children: JSX.Element;
|
||||
}) {
|
||||
const resumableSwapsCount = useResumeableSwapsCount();
|
||||
const resumableSwapsCount = useResumeableSwapsCountExcludingPunished();
|
||||
|
||||
if (resumableSwapsCount > 0) {
|
||||
return (
|
||||
|
|
|
@ -16,6 +16,7 @@ import { resumeSwap } from "renderer/rpc";
|
|||
|
||||
export function SwapResumeButton({
|
||||
swap,
|
||||
children,
|
||||
...props
|
||||
}: ButtonProps & { swap: GetSwapInfoResponse }) {
|
||||
return (
|
||||
|
@ -27,7 +28,7 @@ export function SwapResumeButton({
|
|||
onInvoke={() => resumeSwap(swap.swap_id)}
|
||||
{...props}
|
||||
>
|
||||
Resume
|
||||
{ children }
|
||||
</PromiseInvokeButton>
|
||||
);
|
||||
}
|
||||
|
@ -75,15 +76,13 @@ export default function HistoryRowActions(swap: GetSwapInfoResponse) {
|
|||
);
|
||||
}
|
||||
|
||||
// TODO: Display a button here to attempt a cooperative redeem
|
||||
// See this PR: https://github.com/UnstoppableSwap/unstoppableswap-gui/pull/212
|
||||
if (swap.state_name === BobStateName.BtcPunished) {
|
||||
return (
|
||||
<Tooltip title="This swap is completed. You have been punished.">
|
||||
<ErrorIcon style={{ color: red[500] }} />
|
||||
<Tooltip title="You have been punished. You can attempt to recover the Monero with the help of the other party but that is not guaranteed to work">
|
||||
<SwapResumeButton swap={swap} size="small">Attempt recovery</SwapResumeButton>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
return <SwapResumeButton swap={swap} />;
|
||||
return <SwapResumeButton swap={swap}>Resume</SwapResumeButton>;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue