mirror of
https://github.com/aljazceru/mutiny-web.git
synced 2026-01-06 15:54:23 +01:00
translation pass 2
This commit is contained in:
@@ -3,6 +3,7 @@ import { Card, VStack } from "~/components/layout";
|
||||
import { useMegaStore } from "~/state/megaStore";
|
||||
import { satsToUsd } from "~/utils/conversions";
|
||||
import { AmountEditable } from "./AmountEditable";
|
||||
import { useI18n } from "~/i18n/context";
|
||||
|
||||
const noop = () => {
|
||||
// do nothing
|
||||
@@ -25,6 +26,7 @@ export const InlineAmount: ParentComponent<{
|
||||
sign?: string;
|
||||
fiat?: boolean;
|
||||
}> = (props) => {
|
||||
const i18n = useI18n();
|
||||
const prettyPrint = createMemo(() => {
|
||||
const parsed = Number(props.amount);
|
||||
if (isNaN(parsed)) {
|
||||
@@ -39,12 +41,15 @@ export const InlineAmount: ParentComponent<{
|
||||
{props.sign ? `${props.sign} ` : ""}
|
||||
{props.fiat ? "$" : ""}
|
||||
{prettyPrint()}{" "}
|
||||
<span class="text-sm">{props.fiat ? "USD" : "SATS"}</span>
|
||||
<span class="text-sm">
|
||||
{props.fiat ? i18n.t("common.usd") : i18n.t("common.sats")}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
function USDShower(props: { amountSats: string; fee?: string }) {
|
||||
const i18n = useI18n();
|
||||
const [state, _] = useMegaStore();
|
||||
const amountInUsd = () =>
|
||||
satsToUsd(state.price, add(props.amountSats, props.fee), true);
|
||||
@@ -54,7 +59,7 @@ function USDShower(props: { amountSats: string; fee?: string }) {
|
||||
<KeyValue gray key="">
|
||||
<div class="self-end">
|
||||
~{amountInUsd()}
|
||||
<span class="text-sm">USD</span>
|
||||
<span class="text-sm">{i18n.t("common.usd")}</span>
|
||||
</div>
|
||||
</KeyValue>
|
||||
</Show>
|
||||
|
||||
@@ -69,13 +69,19 @@ function SingleDigitButton(props: {
|
||||
onClear: () => void;
|
||||
fiat: boolean;
|
||||
}) {
|
||||
const i18n = useI18n();
|
||||
let holdTimer: number;
|
||||
const holdThreshold = 500;
|
||||
|
||||
function onHold() {
|
||||
holdTimer = setTimeout(() => {
|
||||
props.onClear();
|
||||
}, holdThreshold);
|
||||
if (
|
||||
props.character === "DEL" ||
|
||||
props.character === i18n.t("char.del")
|
||||
) {
|
||||
holdTimer = setTimeout(() => {
|
||||
props.onClear();
|
||||
}, holdThreshold);
|
||||
}
|
||||
}
|
||||
|
||||
function endHold() {
|
||||
@@ -130,7 +136,7 @@ function BigScalingText(props: { text: string; fiat: boolean }) {
|
||||
>
|
||||
{props.text}
|
||||
<span class="text-xl">
|
||||
{props.fiat ? "USD" : `${i18n.t("common.sats")}`}
|
||||
{props.fiat ? i18n.t("common.usd") : i18n.t("common.sats")}
|
||||
</span>
|
||||
</h1>
|
||||
);
|
||||
@@ -142,7 +148,7 @@ function SmallSubtleAmount(props: { text: string; fiat: boolean }) {
|
||||
<h2 class="flex flex-row items-end text-xl font-light text-neutral-400">
|
||||
~{props.text}
|
||||
<span class="text-base">
|
||||
{props.fiat ? "USD" : `${i18n.t("common.sats")}`}
|
||||
{props.fiat ? i18n.t("common.usd") : `${i18n.t("common.sats")}`}
|
||||
</span>
|
||||
<img
|
||||
class={"pl-[4px] pb-[4px] hover:cursor-pointer"}
|
||||
@@ -214,7 +220,7 @@ export const AmountEditable: ParentComponent<{
|
||||
"9",
|
||||
".",
|
||||
"0",
|
||||
`${i18n.t("char.del")}`
|
||||
i18n.t("char.del")
|
||||
];
|
||||
|
||||
const displaySats = () => toDisplayHandleNaN(localSats(), false);
|
||||
@@ -243,9 +249,13 @@ export const AmountEditable: ParentComponent<{
|
||||
if ((state.balance?.lightning || 0n) === 0n) {
|
||||
const network = state.mutiny_wallet?.get_network() as Network;
|
||||
if (network === "bitcoin") {
|
||||
return "Your first lightning receive needs to be 50,000 sats or greater. A setup fee will be deducted from the requested amount.";
|
||||
return i18n.t("receive.amount_editable.receive_too_small", {
|
||||
amount: "50,000"
|
||||
});
|
||||
} else {
|
||||
return i18n.t("amount_editable_first_payment_10k_or_greater");
|
||||
return i18n.t("receive.amount_editable.receive_too_small", {
|
||||
amount: "10,000"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,7 +265,7 @@ export const AmountEditable: ParentComponent<{
|
||||
}
|
||||
|
||||
if (parsed > (inboundCapacity() || 0)) {
|
||||
return "A lightning setup fee will be charged if paid over lightning.";
|
||||
return i18n.t("receive.amount_editable.setup_fee_lightning");
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@@ -269,10 +279,10 @@ export const AmountEditable: ParentComponent<{
|
||||
|
||||
if (parsed >= 2099999997690000) {
|
||||
// If over 21 million bitcoin, warn that too much
|
||||
return i18n.t("more_than_21m");
|
||||
return i18n.t("receive.amount_editable.more_than_21m");
|
||||
} else if (parsed >= 4000000) {
|
||||
// If over 4 million sats, warn that it's a beta bro
|
||||
return i18n.t("too_big_for_beta");
|
||||
return i18n.t("receive.amount_editable.too_big_for_beta");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -285,7 +295,7 @@ export const AmountEditable: ParentComponent<{
|
||||
|
||||
let sane;
|
||||
|
||||
if (character === "DEL") {
|
||||
if (character === "DEL" || character === i18n.t("char.del")) {
|
||||
if (localValue().length <= 1) {
|
||||
sane = "0";
|
||||
} else {
|
||||
@@ -424,7 +434,7 @@ export const AmountEditable: ParentComponent<{
|
||||
when={localSats() !== "0"}
|
||||
fallback={
|
||||
<div class="inline-block font-semibold">
|
||||
{i18n.t("set_amount")}
|
||||
{i18n.t("receive.amount_editable.set_amount")}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
@@ -540,7 +550,7 @@ export const AmountEditable: ParentComponent<{
|
||||
}}
|
||||
class="py-2 px-4 rounded-lg bg-white/10"
|
||||
>
|
||||
MAX
|
||||
{i18n.t("receive.amount_editable.max")}
|
||||
</button>
|
||||
</Show>
|
||||
</div>
|
||||
@@ -561,7 +571,7 @@ export const AmountEditable: ParentComponent<{
|
||||
class="w-full flex-none"
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
{i18n.t("set_amount")}
|
||||
{i18n.t("receive.amount_editable.set_amount")}
|
||||
</Button>
|
||||
</div>
|
||||
</Dialog.Content>
|
||||
|
||||
@@ -3,10 +3,12 @@ import { createSignal } from "solid-js";
|
||||
import { ConfirmDialog } from "~/components/Dialog";
|
||||
import { Button } from "~/components/layout";
|
||||
import { showToast } from "~/components/Toaster";
|
||||
import { useI18n } from "~/i18n/context";
|
||||
import { useMegaStore } from "~/state/megaStore";
|
||||
import eify from "~/utils/eify";
|
||||
|
||||
export function DeleteEverything(props: { emergency?: boolean }) {
|
||||
const i18n = useI18n();
|
||||
const [state, actions] = useMegaStore();
|
||||
|
||||
async function confirmReset() {
|
||||
@@ -34,7 +36,14 @@ export function DeleteEverything(props: { emergency?: boolean }) {
|
||||
await MutinyWallet.import_json("{}");
|
||||
}
|
||||
|
||||
showToast({ title: "Deleted", description: `Deleted all data` });
|
||||
showToast({
|
||||
title: i18n.t(
|
||||
"settings.emergency_kit.delete_everything.deleted"
|
||||
),
|
||||
description: i18n.t(
|
||||
"settings.emergency_kit.delete_everything.deleted_description"
|
||||
)
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
window.location.href = "/";
|
||||
@@ -50,14 +59,16 @@ export function DeleteEverything(props: { emergency?: boolean }) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={confirmReset}>Delete Everything</Button>
|
||||
<Button onClick={confirmReset}>
|
||||
{i18n.t("settings.emergency_kit.delete_everything.delete")}
|
||||
</Button>
|
||||
<ConfirmDialog
|
||||
loading={confirmLoading()}
|
||||
open={confirmOpen()}
|
||||
onConfirm={resetNode}
|
||||
onCancel={() => setConfirmOpen(false)}
|
||||
>
|
||||
This will delete your node's state. This can't be undone!
|
||||
{i18n.t("settings.emergency_kit.delete_everything.confirm")}
|
||||
</ConfirmDialog>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -15,8 +15,10 @@ import { ConfirmDialog } from "./Dialog";
|
||||
import initMutinyWallet, { MutinyWallet } from "@mutinywallet/mutiny-wasm";
|
||||
import { InfoBox } from "./InfoBox";
|
||||
import { TextField } from "./layout/TextField";
|
||||
import { useI18n } from "~/i18n/context";
|
||||
|
||||
export function ImportExport(props: { emergency?: boolean }) {
|
||||
const i18n = useI18n();
|
||||
const [state, _] = useMegaStore();
|
||||
|
||||
const [error, setError] = createSignal<Error>();
|
||||
@@ -44,7 +46,11 @@ export function ImportExport(props: { emergency?: boolean }) {
|
||||
try {
|
||||
setError(undefined);
|
||||
if (!password()) {
|
||||
throw new Error("Password is required");
|
||||
throw new Error(
|
||||
i18n.t(
|
||||
"settings.emergency_kit.import_export.error_password"
|
||||
)
|
||||
);
|
||||
}
|
||||
const json = await MutinyWallet.export_json(password());
|
||||
downloadTextFile(json || "", "mutiny-state.json");
|
||||
@@ -77,11 +83,23 @@ export function ImportExport(props: { emergency?: boolean }) {
|
||||
if (result) {
|
||||
resolve(result);
|
||||
} else {
|
||||
reject(new Error("No text found in file"));
|
||||
reject(
|
||||
new Error(
|
||||
i18n.t(
|
||||
"settings.emergency_kit.import_export.error_no_text"
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
fileReader.onerror = (_e) =>
|
||||
reject(new Error("File read error"));
|
||||
reject(
|
||||
new Error(
|
||||
i18n.t(
|
||||
"settings.emergency_kit.import_export.error_read_file"
|
||||
)
|
||||
)
|
||||
);
|
||||
fileReader.readAsText(file, "UTF-8");
|
||||
});
|
||||
|
||||
@@ -130,25 +148,35 @@ export function ImportExport(props: { emergency?: boolean }) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<InnerCard title="Export wallet state">
|
||||
<InnerCard
|
||||
title={i18n.t("settings.emergency_kit.import_export.title")}
|
||||
>
|
||||
<NiceP>
|
||||
You can export your entire Mutiny Wallet state to a file and
|
||||
import it into a new browser. It usually works!
|
||||
{i18n.t("settings.emergency_kit.import_export.tip")}
|
||||
</NiceP>
|
||||
<NiceP>
|
||||
<strong>Important caveats:</strong> after exporting don't do
|
||||
any operations in the original browser. If you do, you'll
|
||||
need to export again. After a successful import, a best
|
||||
practice is to clear the state of the original browser just
|
||||
to make sure you don't create conflicts.
|
||||
<strong>
|
||||
{i18n.t(
|
||||
"settings.emergency_kit.import_export.caveat_header"
|
||||
)}
|
||||
</strong>{" "}
|
||||
{i18n.t("settings.emergency_kit.import_export.caveat")}
|
||||
</NiceP>
|
||||
<div />
|
||||
<Show when={error()}>
|
||||
<InfoBox accent="red">{error()?.message}</InfoBox>
|
||||
</Show>
|
||||
<VStack>
|
||||
<Button onClick={handleSave}>Save State As File</Button>
|
||||
<Button onClick={uploadFile}>Import State From File</Button>
|
||||
<Button onClick={handleSave}>
|
||||
{i18n.t(
|
||||
"settings.emergency_kit.import_export.save_state"
|
||||
)}
|
||||
</Button>
|
||||
<Button onClick={uploadFile}>
|
||||
{i18n.t(
|
||||
"settings.emergency_kit.import_export.import_state"
|
||||
)}
|
||||
</Button>
|
||||
</VStack>
|
||||
</InnerCard>
|
||||
<ConfirmDialog
|
||||
@@ -157,11 +185,14 @@ export function ImportExport(props: { emergency?: boolean }) {
|
||||
onConfirm={importJson}
|
||||
onCancel={() => setConfirmOpen(false)}
|
||||
>
|
||||
Do you want to replace your state with {files()[0].name}?
|
||||
{i18n.t("settings.emergency_kit.import_export.confirm_replace")}{" "}
|
||||
{files()[0].name}?
|
||||
</ConfirmDialog>
|
||||
{/* TODO: this is pretty redundant with the DecryptDialog, could make a shared component */}
|
||||
<SimpleDialog
|
||||
title="Enter your password to decrypt"
|
||||
title={i18n.t(
|
||||
"settings.emergency_kit.import_export.confirm_replace"
|
||||
)}
|
||||
open={exportDecrypt()}
|
||||
>
|
||||
<form onSubmit={savePassword}>
|
||||
@@ -180,7 +211,9 @@ export function ImportExport(props: { emergency?: boolean }) {
|
||||
<InfoBox accent="red">{error()?.message}</InfoBox>
|
||||
</Show>
|
||||
<Button intent="blue" onClick={savePassword}>
|
||||
Decrypt Wallet
|
||||
{i18n.t(
|
||||
"settings.emergency_kit.import_export.decrypt_wallet"
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { MutinyWallet } from "@mutinywallet/mutiny-wasm";
|
||||
import { Button, InnerCard, NiceP, VStack } from "~/components/layout";
|
||||
import { useI18n } from "~/i18n/context";
|
||||
import { downloadTextFile } from "~/utils/download";
|
||||
|
||||
export function Logs() {
|
||||
const i18n = useI18n();
|
||||
async function handleSave() {
|
||||
try {
|
||||
const logs = await MutinyWallet.get_logs();
|
||||
@@ -18,11 +20,13 @@ export function Logs() {
|
||||
}
|
||||
|
||||
return (
|
||||
<InnerCard title="Download debug logs">
|
||||
<InnerCard title={i18n.t("settings.emergency_kit.logs.title")}>
|
||||
<VStack>
|
||||
<NiceP>Something screwy going on? Check out the logs!</NiceP>
|
||||
<NiceP>
|
||||
{i18n.t("settings.emergency_kit.logs.something_screwy")}
|
||||
</NiceP>
|
||||
<Button intent="green" onClick={handleSave}>
|
||||
Download Logs
|
||||
{i18n.t("settings.emergency_kit.logs.download_logs")}
|
||||
</Button>
|
||||
</VStack>
|
||||
</InnerCard>
|
||||
|
||||
@@ -15,7 +15,7 @@ export function FeesModal(props: { icon?: boolean }) {
|
||||
props.icon ? (
|
||||
<img src={help} alt="help" class="w-4 h-4 cursor-pointer" />
|
||||
) : (
|
||||
i18n.t("why?")
|
||||
i18n.t("why")
|
||||
)
|
||||
}
|
||||
>
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { onCleanup, onMount } from "solid-js";
|
||||
import { BarcodeScanner, BarcodeFormat, CameraPermissionState, CameraPermissionType, CameraPluginPermissions, PermissionStates } from '@mutinywallet/barcode-scanner';
|
||||
import {
|
||||
BarcodeScanner,
|
||||
BarcodeFormat,
|
||||
CameraPermissionState,
|
||||
CameraPermissionType,
|
||||
CameraPluginPermissions,
|
||||
PermissionStates
|
||||
} from "@mutinywallet/barcode-scanner";
|
||||
import QrScanner from "qr-scanner";
|
||||
|
||||
export default function Scanner(props: { onResult: (result: string) => void }) {
|
||||
@@ -12,7 +19,8 @@ export default function Scanner(props: { onResult: (result: string) => void }) {
|
||||
|
||||
const startScan = async () => {
|
||||
// Check camera permission
|
||||
const permissions: PermissionStates = await BarcodeScanner.checkPermissions();
|
||||
const permissions: PermissionStates =
|
||||
await BarcodeScanner.checkPermissions();
|
||||
if (permissions.camera === "granted") {
|
||||
const callback = (result: ScanResult, err?: any) => {
|
||||
if (err) {
|
||||
@@ -24,10 +32,14 @@ export default function Scanner(props: { onResult: (result: string) => void }) {
|
||||
handleResult({ data: result.content }); // pass the raw scanned content
|
||||
}
|
||||
};
|
||||
await BarcodeScanner.start({ targetedFormats: [BarcodeFormat.QR_CODE] }, callback);
|
||||
await BarcodeScanner.start(
|
||||
{ targetedFormats: [BarcodeFormat.QR_CODE] },
|
||||
callback
|
||||
);
|
||||
} else if (permissions.camera === "prompt") {
|
||||
// Request permission if it has not been asked before
|
||||
const requestedPermissions: PermissionStates = await BarcodeScanner.requestPermissions();
|
||||
const requestedPermissions: PermissionStates =
|
||||
await BarcodeScanner.requestPermissions();
|
||||
if (requestedPermissions.camera === "granted") {
|
||||
// If user grants permission, start the scan
|
||||
await startScan();
|
||||
@@ -35,7 +47,7 @@ export default function Scanner(props: { onResult: (result: string) => void }) {
|
||||
} else if (permissions.camera === "denied") {
|
||||
// Handle the scenario when user denies the permission
|
||||
// Maybe show a user friendly message here
|
||||
console.log('Camera permission was denied');
|
||||
console.log("Camera permission was denied");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { For, Match, Switch, createMemo, createSignal } from "solid-js";
|
||||
import { useCopy } from "~/utils/useCopy";
|
||||
import copyIcon from "~/assets/icons/copy.svg";
|
||||
import { useI18n } from "~/i18n/context";
|
||||
|
||||
export function SeedWords(props: {
|
||||
words: string;
|
||||
setHasSeen?: (hasSeen: boolean) => void;
|
||||
}) {
|
||||
const i18n = useI18n();
|
||||
const [shouldShow, setShouldShow] = createSignal(false);
|
||||
const [copy, copied] = useCopy({ copiedTimeout: 1000 });
|
||||
|
||||
@@ -30,7 +32,9 @@ export function SeedWords(props: {
|
||||
class="cursor-pointer flex w-full justify-center"
|
||||
onClick={toggleShow}
|
||||
>
|
||||
<code class="text-red">TAP TO REVEAL SEED WORDS</code>
|
||||
<code class="text-red">
|
||||
{i18n.t("settings.backup.seed_words.reveal")}
|
||||
</code>
|
||||
</div>
|
||||
</Match>
|
||||
|
||||
@@ -40,7 +44,9 @@ export function SeedWords(props: {
|
||||
class="cursor-pointer flex w-full justify-center"
|
||||
onClick={toggleShow}
|
||||
>
|
||||
<code class="text-red">HIDE</code>
|
||||
<code class="text-red">
|
||||
{i18n.t("settings.backup.seed_words.hide")}
|
||||
</code>
|
||||
</div>
|
||||
<ol class="overflow-hidden columns-2 w-full list-decimal list-inside">
|
||||
<For each={splitWords()}>
|
||||
@@ -59,8 +65,12 @@ export function SeedWords(props: {
|
||||
<div class="flex items-center gap-2">
|
||||
<span>
|
||||
{copied()
|
||||
? "Copied!"
|
||||
: "Dangerously Copy to Clipboard"}
|
||||
? i18n.t(
|
||||
"settings.backup.seed_words.copied"
|
||||
)
|
||||
: i18n.t(
|
||||
"settings.backup.seed_words.copy"
|
||||
)}
|
||||
</span>
|
||||
<img
|
||||
src={copyIcon}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { Back } from "~/assets/svg/Back";
|
||||
import { useI18n } from "~/i18n/context";
|
||||
|
||||
export function BackButton(props: {
|
||||
onClick: () => void;
|
||||
title?: string;
|
||||
showOnDesktop?: boolean;
|
||||
}) {
|
||||
const i18n = useI18n();
|
||||
return (
|
||||
<button
|
||||
onClick={() => props.onClick()}
|
||||
@@ -12,7 +14,7 @@ export function BackButton(props: {
|
||||
classList={{ "md:!flex": props.showOnDesktop }}
|
||||
>
|
||||
<Back />
|
||||
{props.title ? props.title : "Home"}
|
||||
{props.title ? props.title : i18n.t("common.home")}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { A } from "solid-start";
|
||||
import { Back } from "~/assets/svg/Back";
|
||||
import { useI18n } from "~/i18n/context";
|
||||
|
||||
export function BackLink(props: { href?: string; title?: string }) {
|
||||
const i18n = useI18n();
|
||||
return (
|
||||
<A
|
||||
href={props.href ? props.href : "/"}
|
||||
class="text-m-red active:text-m-red/80 text-xl font-semibold no-underline md:hidden flex items-center"
|
||||
>
|
||||
<Back />
|
||||
{props.title ? props.title : "Home"}
|
||||
{props.title ? props.title : i18n.t("common.home")}
|
||||
</A>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { useLocation, useNavigate } from "solid-start";
|
||||
import { BackButton } from "./BackButton";
|
||||
import { useI18n } from "~/i18n/context";
|
||||
|
||||
type StateWithPrevious = {
|
||||
previous?: string;
|
||||
};
|
||||
|
||||
export function BackPop() {
|
||||
const i18n = useI18n();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
@@ -15,7 +17,7 @@ export function BackPop() {
|
||||
|
||||
return (
|
||||
<BackButton
|
||||
title="Back"
|
||||
title={i18n.t("common.back")}
|
||||
onClick={() => navigate(backPath())}
|
||||
showOnDesktop
|
||||
/>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Progress } from "@kobalte/core";
|
||||
import { SmallHeader } from ".";
|
||||
import { useI18n } from "~/i18n/context";
|
||||
|
||||
export default function formatNumber(num: number) {
|
||||
const map = [
|
||||
@@ -21,19 +22,24 @@ export default function formatNumber(num: number) {
|
||||
}
|
||||
|
||||
export function ProgressBar(props: { value: number; max: number }) {
|
||||
const i18n = useI18n();
|
||||
return (
|
||||
<Progress.Root
|
||||
value={props.value}
|
||||
minValue={0}
|
||||
maxValue={props.max}
|
||||
getValueLabel={({ value, max }) =>
|
||||
`${formatNumber(value)} of ${formatNumber(max)} sats sent`
|
||||
`${formatNumber(value)} ${i18n.t(
|
||||
"send.progress_bar.of"
|
||||
)} ${formatNumber(max)} ${i18n.t(
|
||||
"send.progress_bar.sats_sent"
|
||||
)}`
|
||||
}
|
||||
class="w-full flex flex-col gap-2"
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
<Progress.Label>
|
||||
<SmallHeader>Sending...</SmallHeader>
|
||||
<SmallHeader>{i18n.t("send.sending")}</SmallHeader>
|
||||
</Progress.Label>
|
||||
<Progress.ValueLabel class="text-sm font-semibold uppercase" />
|
||||
</div>
|
||||
|
||||
@@ -25,6 +25,7 @@ import { A } from "solid-start";
|
||||
import down from "~/assets/icons/down.svg";
|
||||
import { DecryptDialog } from "../DecryptDialog";
|
||||
import { LoadingIndicator } from "~/components/LoadingIndicator";
|
||||
import { useI18n } from "~/i18n/context";
|
||||
|
||||
export { Button, ButtonLink, Linkify };
|
||||
|
||||
@@ -138,6 +139,7 @@ export const DefaultMain: ParentComponent = (props) => {
|
||||
};
|
||||
|
||||
export const FullscreenLoader = () => {
|
||||
const i18n = useI18n();
|
||||
const [waitedTooLong, setWaitedTooLong] = createSignal(false);
|
||||
|
||||
setTimeout(() => {
|
||||
@@ -149,10 +151,9 @@ export const FullscreenLoader = () => {
|
||||
<LoadingSpinner wide />
|
||||
<Show when={waitedTooLong()}>
|
||||
<p class="max-w-[20rem] text-neutral-400">
|
||||
Stuck on this screen? Try reloading. If that doesn't work,
|
||||
check out the{" "}
|
||||
{i18n.t("error.load_time.stuck")}{" "}
|
||||
<A class="text-white" href="/emergencykit">
|
||||
emergency kit.
|
||||
{i18n.t("error.load_time.emergency_link")}
|
||||
</A>
|
||||
</p>
|
||||
</Show>
|
||||
|
||||
Reference in New Issue
Block a user