Skip to content

Commit

Permalink
feat: show error when QR scanning is not supported in browser (#768)
Browse files Browse the repository at this point in the history
* feat: show error when QR scanning is not supported in browser

* chore: add missing strings

---------

Co-authored-by: Kilian <[email protected]>
  • Loading branch information
michael1011 and kilrau authored Dec 6, 2024
1 parent ef48ad4 commit e64874d
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 29 deletions.
7 changes: 7 additions & 0 deletions src/i18n/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ const dict = {
will_receive: "Will receive",
refund_available_in: "Refund will be available in {{ blocks }} blocks",
no_wallet_connected: "No wallet connected",
qr_scan_supported: "This browser doesn't support refunds via QR",
},
de: {
language: "Deutsch",
Expand Down Expand Up @@ -462,6 +463,8 @@ const dict = {
will_receive: "Sie erhalten",
refund_available_in: "Rückerstattung möglich in {{ blocks }} Blöcken",
no_wallet_connected: "Kein Wallet verbunden",
qr_scan_supported:
"Dieser Browser unterstützt keine Erstattungen über QR",
},
es: {
language: "Español",
Expand Down Expand Up @@ -695,6 +698,8 @@ const dict = {
will_receive: "Recibirá",
refund_available_in: "Reembolso disponible en {{ blocks }} bloques",
no_wallet_connected: "No hay monedero conectado",
qr_scan_supported:
"Este navegador no admite devoluciones a través de QR",
},
zh: {
language: "中文",
Expand Down Expand Up @@ -902,6 +907,7 @@ const dict = {
will_receive: "将收到",
refund_available_in: "退款将分 {{ blocks }} 区块提供",
no_wallet_connected: "未连接钱包",
qr_scan_supported: "此浏览器不支持通过 QR 退款",
},
ja: {
language: "日本語",
Expand Down Expand Up @@ -1134,6 +1140,7 @@ const dict = {
will_receive: "受信予定",
refund_available_in: "返金は {{ blocks }} つのブロックに分かれる",
no_wallet_connected: "財布はつながっていない!",
qr_scan_supported: "このブラウザはQRによる払い戻しに対応していません。",
},
};

Expand Down
74 changes: 45 additions & 29 deletions src/pages/Refund.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,26 @@ import {
LogRefundData,
scanLogsForPossibleRefunds,
} from "../utils/contractLogs";
import { qrScanProbe } from "../utils/qrScanProbe";
import { validateRefundFile } from "../utils/refundFile";
import { SomeSwap } from "../utils/swapCreator";
import ErrorWasm from "./ErrorWasm";

enum RefundError {
InvalidData,
QrScanNotSupported,
}

const Refund = () => {
const navigate = useNavigate();
const { getSwap, getSwaps, updateSwapStatus, wasmSupported, t } =
useGlobalContext();
const { signer, providers, getEtherSwap } = useWeb3Signer();

const [swapFound, setSwapFound] = createSignal(null);
const [refundInvalid, setRefundInvalid] = createSignal(false);
const [refundInvalid, setRefundInvalid] = createSignal<
RefundError | undefined
>(undefined);
const [refundJson, setRefundJson] = createSignal(null);
const [refundTxId, setRefundTxId] = createSignal<string>("");

Expand All @@ -52,47 +60,47 @@ const Refund = () => {
}

setRefundJson(data);
setRefundInvalid(false);
setRefundInvalid(undefined);
} catch (e) {
log.warn("Refund json validation failed", e);
setRefundInvalid(true);
setRefundInvalid(RefundError.InvalidData);
input.setCustomValidity(t("invalid_refund_file"));
}
};

const uploadChange = (e: Event) => {
const uploadChange = async (e: Event) => {
const input = e.currentTarget as HTMLInputElement;
const inputFile = input.files[0];
input.setCustomValidity("");
setRefundJson(null);
setSwapFound(null);
setRefundInvalid(false);
setRefundInvalid(undefined);

if (["image/png", "image/jpg", "image/jpeg"].includes(inputFile.type)) {
QrScanner.scanImage(inputFile, { returnDetailedScanResult: true })
.then(
async (result) =>
await checkRefundJsonKeys(
input,
JSON.parse(result.data),
),
)
.catch((e) => {
log.error("invalid QR code upload", e);
setRefundInvalid(true);
input.setCustomValidity(t("invalid_refund_file"));
if (!(await qrScanProbe())) {
setRefundInvalid(RefundError.QrScanNotSupported);
return;
}

try {
const res = await QrScanner.scanImage(inputFile, {
returnDetailedScanResult: true,
});
await checkRefundJsonKeys(input, JSON.parse(res.data));
} catch (e) {
log.error("invalid QR code upload", e);
setRefundInvalid(RefundError.InvalidData);
input.setCustomValidity(t("invalid_refund_file"));
}
} else {
inputFile
.text()
.then(async (result) => {
await checkRefundJsonKeys(input, JSON.parse(result));
})
.catch((e) => {
log.error("invalid file upload", e);
setRefundInvalid(true);
input.setCustomValidity(t("invalid_refund_file"));
});
try {
const data = await inputFile.text();
await checkRefundJsonKeys(input, JSON.parse(data));
} catch (e) {
log.error("invalid file upload", e);
setRefundInvalid(RefundError.InvalidData);
input.setCustomValidity(t("invalid_refund_file"));
}
}
};

Expand Down Expand Up @@ -249,10 +257,18 @@ const Refund = () => {
{t("open_swap")}
</button>
</Show>
<Show when={refundInvalid()}>
<Show when={refundInvalid() !== undefined}>
<hr />
<button class="btn" disabled={true}>
{t("invalid_refund_file")}
{(() => {
switch (refundInvalid()) {
case RefundError.InvalidData:
return t("invalid_refund_file");

case RefundError.QrScanNotSupported:
return t("qr_scan_supported");
}
})()}
</button>
</Show>
<Show when={refundTxId() === "" && refundJson() !== null}>
Expand Down
19 changes: 19 additions & 0 deletions src/utils/qrScanProbe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import log from "loglevel";
import QrScanner from "qr-scanner";

import { formatError } from "./errors";

const image =
"data:image/png;base64,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";

export const qrScanProbe = async () => {
try {
const res = await QrScanner.scanImage(new URL(image), {
returnDetailedScanResult: true,
});
return res.data === "21";
} catch (e) {
log.warn(`QR code scanning probe failed: ${formatError(e)}`);
return false;
}
};

0 comments on commit e64874d

Please sign in to comment.