diff --git a/README.md b/README.md index 95fa75ac3c..9e7e176887 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Alby is open-source and currently in alpha stage. Our goal is to create the best We have a channel on the [bitcoin.design](https://bitcoin.design/) Slack community [#lightning-browser-extension](https://bitcoindesign.slack.com/archives/C02591ADXM2) and a [Telegram group](https://t.me/getAlby). Come and join us! -We also do a bi-weekly call on Thursday at [13:00 UTC](https://everytimezone.com/s/436cf0d2) on [Jitsi](https://meet.fulmo.org/AlbyCommunityCall) +We also do a bi-weekly call on Thursday at [15:00 UTC](https://everytimezone.com/s/436cf0d2) on [Jitsi](https://meet.fulmo.org/AlbyCommunityCall) ## Browser Support diff --git a/jest.setup.js b/jest.setup.js index 8dbe4dbe47..f3ec823769 100644 --- a/jest.setup.js +++ b/jest.setup.js @@ -7,6 +7,11 @@ import "@testing-library/jest-dom"; // https://github.com/mswjs/examples/tree/master/examples/rest-react import { server } from "./tests/unit/helpers/server"; +import { TextEncoder, TextDecoder } from 'util' +global.TextEncoder = TextEncoder +global.TextDecoder = TextDecoder + + // fix "This script should only be loaded in a browser extension." e.g. https://github.com/mozilla/webextension-polyfill/issues/218 if (!chrome.runtime.id) chrome.runtime.id = "history-delete"; diff --git a/package.json b/package.json index 2fe5f6763f..d6405400ed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lightning-browser-extension", - "version": "1.22.1", + "version": "1.24.0", "description": "Lightning browser extension", "private": true, "repository": "https://github.com/bumi/lightning-browser-extension.git", @@ -37,32 +37,35 @@ "dependencies": { "@bitcoin-design/bitcoin-icons-react": "^0.1.9", "@headlessui/react": "^1.7.7", - "@noble/secp256k1": "^1.7.0", + "lnc-web": "git+https://github.com/bumi/lnc-web.git#remove-window-dependency", + "@noble/secp256k1": "^1.7.1", "@tailwindcss/forms": "^0.5.3", "@tailwindcss/line-clamp": "^0.4.2", "@vespaiach/axios-fetch-adapter": "^0.3.0", + "avvvatars-react": "^0.4.2", "axios": "^0.27.2", "bech32": "^2.0.0", "bolt11": "^1.4.0", "crypto-js": "^4.1.1", "dayjs": "^1.11.7", - "dexie": "^3.2.2", + "dexie": "^3.2.3", "elliptic": "^6.5.4", - "html5-qrcode": "^2.3.1", - "i18next": "^22.4.6", + "html5-qrcode": "^2.3.4", + "i18next": "^22.4.9", "i18next-browser-languagedetector": "^7.0.1", - "lnmessage": "^0.0.14", + "lnmessage": "^0.0.18", "lodash.merge": "^4.6.2", "lodash.pick": "^4.4.0", + "lodash.snakecase": "^4.1.1", "pubsub-js": "^1.9.4", "react": "^18.2.0", "react-confetti": "^6.1.0", "react-dom": "^18.2.0", - "react-i18next": "^12.1.1", + "react-i18next": "^12.1.4", "react-loading-skeleton": "^3.1.0", "react-modal": "^3.16.1", - "react-qr-code": "^2.0.8", - "react-router-dom": "^6.6.1", + "react-qr-code": "^2.0.11", + "react-router-dom": "^6.7.0", "react-toastify": "^9.1.1", "stream": "^0.0.2", "tailwindcss": "^3.2.4", @@ -71,28 +74,29 @@ "zustand": "^3.7.2" }, "devDependencies": { - "@commitlint/cli": "^17.3.0", - "@commitlint/config-conventional": "^17.3.0", + "@commitlint/cli": "^17.4.1", + "@commitlint/config-conventional": "^17.4.2", "@jest/types": "^29.3.1", - "@playwright/test": "^1.29.1", - "@swc/core": "^1.3.24", + "@playwright/test": "^1.29.2", + "@swc/core": "^1.3.27", "@swc/jest": "^0.2.24", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^14.4.3", "@trivago/prettier-plugin-sort-imports": "^4.0.0", - "@types/chrome": "^0.0.206", + "@types/chrome": "^0.0.210", "@types/crypto-js": "^4.1.1", "@types/elliptic": "^6.4.14", "@types/lodash.merge": "^4.6.7", "@types/lodash.pick": "^4.4.0", + "@types/lodash.snakecase": "^4.1.1", "@types/pubsub-js": "^1.8.3", "@types/react-dom": "^18.0.10", "@types/react-modal": "^3.13.1", "@types/uuid": "^9.0.0", - "@types/webextension-polyfill": "^0.9.2", - "@typescript-eslint/eslint-plugin": "^5.45.1", - "@typescript-eslint/parser": "^5.45.1", + "@types/webextension-polyfill": "^0.10.0", + "@typescript-eslint/eslint-plugin": "^5.48.0", + "@typescript-eslint/parser": "^5.48.0", "autoprefixer": "^10.4.13", "buffer": "^6.0.3", "clean-webpack-plugin": "^4.0.0", @@ -102,28 +106,28 @@ "css-loader": "^6.7.3", "css-minimizer-webpack-plugin": "^4.2.2", "del-cli": "^5.0.0", - "eslint": "^8.30.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-react": "^7.31.11", + "eslint": "^8.32.0", + "eslint-config-prettier": "^8.6.0", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-react": "^7.32.1", "eslint-plugin-react-hooks": "^4.6.0", - "fake-indexeddb": "^3.1.8", + "fake-indexeddb": "^4.0.1", "filemanager-webpack-plugin": "^8.0.0", "html-webpack-plugin": "^5.5.0", - "husky": "^8.0.2", + "husky": "^8.0.3", "jest": "^29.3.1", "jest-environment-jsdom": "^29.3.1", - "jest-webextension-mock": "^3.8.7", + "jest-webextension-mock": "^3.8.8", "lint-staged": "^13.1.0", "mini-css-extract-plugin": "^2.7.2", - "msw": "^0.49.2", - "postcss": "^8.4.20", + "msw": "^1.0.0", + "postcss": "^8.4.21", "postcss-cli": "^10.1.0", "postcss-loader": "^7.0.2", "pptr-testing-library": "^0.7.0", - "prettier": "^2.8.1", + "prettier": "^2.8.3", "process": "^0.11.10", - "puppeteer": "^19.4.1", + "puppeteer": "^19.5.2", "sass": "^1.57.1", "sass-loader": "^13.2.0", "stream-browserify": "^3.0.0", diff --git a/src/app/components/AccountMenu/index.tsx b/src/app/components/AccountMenu/index.tsx index 913f14984c..618fa2b802 100644 --- a/src/app/components/AccountMenu/index.tsx +++ b/src/app/components/AccountMenu/index.tsx @@ -1,11 +1,11 @@ import { AddressBookIcon, CaretDownIcon, + CheckIcon, PlusIcon, } from "@bitcoin-design/bitcoin-icons-react/filled"; -import { CheckIcon } from "@bitcoin-design/bitcoin-icons-react/filled"; -import { WalletIcon } from "@bitcoin-design/bitcoin-icons-react/outline"; -import { useState, useEffect } from "react"; +import Avvvatars from "avvvatars-react"; +import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import Skeleton from "react-loading-skeleton"; import { useNavigate } from "react-router-dom"; @@ -73,10 +73,10 @@ function AccountMenu({ showOptions = true }: Props) { } return ( -
-

- -

+
+
+ +
{t("screen_reader")} - + {t("title")} {Object.keys(accounts).map((accountId) => { @@ -126,14 +126,16 @@ function AccountMenu({ showOptions = true }: Props) { disabled={loading} title={account.name} > - - +
+ +
+ {account.name}  {accountId === authAccount?.id && ( diff --git a/src/app/components/AllowanceMenu/index.tsx b/src/app/components/AllowanceMenu/index.tsx index 81bfe5aa43..b46dd66963 100644 --- a/src/app/components/AllowanceMenu/index.tsx +++ b/src/app/components/AllowanceMenu/index.tsx @@ -2,14 +2,14 @@ import { GearIcon } from "@bitcoin-design/bitcoin-icons-react/filled"; import { CrossIcon } from "@bitcoin-design/bitcoin-icons-react/outline"; import Setting from "@components/Setting"; import Toggle from "@components/form/Toggle"; -import { useState, useEffect } from "react"; import type { FormEvent } from "react"; +import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import Modal from "react-modal"; import { toast } from "react-toastify"; import { useSettings } from "~/app/context/SettingsContext"; import msg from "~/common/lib/msg"; -import type { Allowance } from "~/types"; +import type { Allowance, Permission } from "~/types"; import Button from "../Button"; import Menu from "../Menu"; @@ -33,8 +33,47 @@ function AllowanceMenu({ allowance, onEdit, onDelete }: Props) { const [budget, setBudget] = useState(""); const [lnurlAuth, setLnurlAuth] = useState(false); const [fiatAmount, setFiatAmount] = useState(""); + + const [originalPermissions, setOriginalPermissions] = useState< + Permission[] | null + >(null); + const [permissions, setPermissions] = useState(null); + const [isLoadingPermissions, setIsLoadingPermissions] = useState(true); + const { t } = useTranslation("components", { keyPrefix: "allowance_menu" }); const { t: tCommon } = useTranslation("common"); + const { t: tPermissions } = useTranslation("permissions"); + + const hasPermissions = !isLoadingPermissions && !!permissions?.length; + + const enableSubmit = + parseInt(budget) !== allowance.totalBudget || + lnurlAuth !== allowance.lnurlAuth || + getChangedPermissionsIds().length; + + useEffect(() => { + const fetchPermissions = async () => { + try { + const permissionResponse = await msg.request<{ + permissions: Permission[]; + }>("listPermissions", { + id: allowance.id, + }); + + const permissions: Permission[] = permissionResponse.permissions; + + setOriginalPermissions(permissions); + setPermissions(permissions); + } catch (e) { + console.error(e); + if (e instanceof Error) toast.error(e.message); + } finally { + setIsLoadingPermissions(false); + } + }; + + !permissions && fetchPermissions(); + }, [allowance.id, permissions]); useEffect(() => { if (budget !== "" && showFiat) { @@ -65,15 +104,39 @@ function AllowanceMenu({ allowance, onEdit, onDelete }: Props) { setIsOpen(false); } + function getChangedPermissionsIds(): number[] { + if (!permissions || !originalPermissions) return []; + const ids = permissions + .filter((prm, i) => prm.enabled !== originalPermissions[i].enabled) + .map((prm) => prm.id); + return ids; + } + async function updateAllowance() { - await msg.request("updateAllowance", { - id: allowance.id, - totalBudget: parseInt(budget), - lnurlAuth, - }); - - onEdit && onEdit(); - closeModal(); + try { + await msg.request("updateAllowance", { + id: allowance.id, + totalBudget: parseInt(budget), + lnurlAuth, + }); + + const changedIds = getChangedPermissionsIds(); + + if (changedIds.length) { + await msg.request("deletePermissionsById", { + ids: changedIds, + }); + } + + /* DB is updated, let´s update the original permissions + to the updated permission in local state too */ + setOriginalPermissions(permissions); + + onEdit && onEdit(); + closeModal(); + } catch (e) { + console.error(e); + } } return ( @@ -115,6 +178,7 @@ function AllowanceMenu({ allowance, onEdit, onDelete }: Props) { contentLabel={t("edit_allowance.screen_reader")} overlayClassName="bg-black bg-opacity-25 fixed inset-0 flex justify-center items-center p-5" className="rounded-lg bg-white w-full max-w-lg" + style={{ content: { maxHeight: "90vh" } }} >

@@ -131,7 +195,10 @@ function AllowanceMenu({ allowance, onEdit, onDelete }: Props) { updateAllowance(); }} > -
+
setBudget(e.target.value)} />
- - setLnurlAuth(!lnurlAuth)} - /> - + + setLnurlAuth(!lnurlAuth)} + /> + +
+ + {hasPermissions && ( +
+

+ {t("edit_permissions")} +

+
+ {permissions.map((permission) => ( + <> + lnd.getinfo + nostr/nip04decrypt --> nostr.nip04decrypt */ + > + { + setPermissions( + permissions.map((prm) => + prm.id === permission.id + ? { ...prm, enabled: !prm.enabled } + : prm + ) + ); + }} + /> + + + ))} +
+
+ )}
@@ -161,10 +277,7 @@ function AllowanceMenu({ allowance, onEdit, onDelete }: Props) { type="submit" label={tCommon("actions.save")} primary - disabled={ - parseInt(budget) === allowance.totalBudget && - lnurlAuth === allowance.lnurlAuth - } + disabled={!enableSubmit} />
diff --git a/src/app/components/BalanceCard/index.test.tsx b/src/app/components/BalanceCard/index.test.tsx deleted file mode 100644 index 43775d6494..0000000000 --- a/src/app/components/BalanceCard/index.test.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { render, screen } from "@testing-library/react"; -import { MemoryRouter } from "react-router-dom"; - -import type { Props } from "./index"; -import BalanceCard from "./index"; - -const props: Props = { - alias: "100", - crypto: "200", - fiat: "300", -}; - -describe("ConfirmPayment", () => { - test("render", async () => { - render( - - - - ); - - expect(screen.getByText("100")).toBeInTheDocument(); - expect(screen.getByText("200")).toBeInTheDocument(); - expect(screen.getByText("300")).toBeInTheDocument(); - }); -}); diff --git a/src/app/components/BalanceCard/index.tsx b/src/app/components/BalanceCard/index.tsx deleted file mode 100644 index eb2f736aac..0000000000 --- a/src/app/components/BalanceCard/index.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import Skeleton from "react-loading-skeleton"; - -export type Props = { - alias: string; - crypto: string; - fiat: string; -}; - -const skeletonStyle = { - opacity: 0.5, -}; - -function BalanceCard({ alias, crypto, fiat }: Props) { - return ( -
-

- {alias || } -

-

- {crypto || } -

-

- {fiat || } -

-
- ); -} - -export default BalanceCard; diff --git a/src/app/components/Button/index.tsx b/src/app/components/Button/index.tsx index 34b5900012..71e26a0766 100644 --- a/src/app/components/Button/index.tsx +++ b/src/app/components/Button/index.tsx @@ -31,6 +31,7 @@ const Button = forwardRef( outline = false, loading = false, flex = false, + className, }: Props, ref: Ref ) => { @@ -54,7 +55,8 @@ const Button = forwardRef( "hover:bg-gray-50 dark:hover:bg-surface-16dp", disabled ? "cursor-default opacity-60" : "cursor-pointer", flex && "flex-1", - "inline-flex justify-center items-center font-medium rounded-md shadow focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-orange-bitcoin transition duration-150" + "inline-flex justify-center items-center font-medium rounded-md shadow focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-orange-bitcoin transition duration-150", + !!className && className )} onClick={onClick} disabled={disabled} diff --git a/src/app/components/Card/index.tsx b/src/app/components/Card/index.tsx index 5f1eeb7436..fdc8e2d096 100644 --- a/src/app/components/Card/index.tsx +++ b/src/app/components/Card/index.tsx @@ -14,14 +14,16 @@ export default function Card({ currency, }: Props) { return ( -
+

{alias}

{satoshis}

-

- {fiat} {currency} -

+ {fiat && currency && ( +

+ {fiat} {currency} +

+ )}
); } diff --git a/src/app/components/CloseableCard/index.test.tsx b/src/app/components/CloseableCard/index.test.tsx new file mode 100644 index 0000000000..0bc0452546 --- /dev/null +++ b/src/app/components/CloseableCard/index.test.tsx @@ -0,0 +1,23 @@ +import { render, screen } from "@testing-library/react"; +import { MemoryRouter } from "react-router-dom"; + +import type { Props } from "./index"; +import CloseableCard from "./index"; + +const props: Props = { + title: "Card Title", + description: "Card description", + handleClose: () => ({}), +}; + +describe("CloseableCard", () => { + test("render label", async () => { + render( + + + + ); + + expect(await screen.findByText("Card Title")).toBeInTheDocument(); + }); +}); diff --git a/src/app/components/CloseableCard/index.tsx b/src/app/components/CloseableCard/index.tsx new file mode 100644 index 0000000000..8568444d5e --- /dev/null +++ b/src/app/components/CloseableCard/index.tsx @@ -0,0 +1,44 @@ +import { CrossIcon } from "@bitcoin-design/bitcoin-icons-react/filled"; + +export type Props = { + title: string; + description: string | JSX.Element | (JSX.Element | string)[]; + buttons?: JSX.Element[]; + handleClose: () => void; +}; + +export default function CloseableCard({ + title, + description, + buttons, + handleClose, +}: Props) { + const uiDescription = Array.isArray(description) ? ( +
    + {description.map((text, index) => ( +
  1. {text}
  2. + ))} +
+ ) : ( +

+ {description} +

+ ); + return ( +
+ +

{title}

+ + {uiDescription} + + {!!buttons?.length && ( +
{buttons}
+ )} +
+ ); +} diff --git a/src/app/components/ConnectorForm/index.tsx b/src/app/components/ConnectorForm/index.tsx index efeab321d4..8f874f9f60 100644 --- a/src/app/components/ConnectorForm/index.tsx +++ b/src/app/components/ConnectorForm/index.tsx @@ -29,17 +29,48 @@ function ConnectorForm({ const { t: tCommon } = useTranslation("common"); const navigate = useNavigate(); + const media = ( +
+ {video ? ( +
+ +
+ ) : ( + <> +
+ Alby + Alby +
+ + )} +
+ ); + return (
-
+
{typeof title === "string" ? ( -

{title}

+

{title}

) : ( title )} +
{media}
{description && ( -
+
{typeof description === "string" ? (

{description}

) : ( @@ -47,42 +78,17 @@ function ConnectorForm({ )}
)} -
{children}
-
-
-
- {video ? ( -
- -
- ) : ( - <> - Alby - Alby - - )} -
+
{children}
+
{media}
-
+
diff --git a/src/app/components/ConnectorPath/index.tsx b/src/app/components/ConnectorPath/index.tsx index 9fdf1af5d1..5ed65cdbe5 100644 --- a/src/app/components/ConnectorPath/index.tsx +++ b/src/app/components/ConnectorPath/index.tsx @@ -7,13 +7,15 @@ type Props = { function ConnectorPath({ title, description, content, actions }: Props) { return ( -
+

{title}

-

{description}

-
+

+ {description} +

+
{content}
-
{actions}
+
{actions}
); } diff --git a/src/app/components/Header/index.tsx b/src/app/components/Header/index.tsx index 8733c54636..912def1737 100644 --- a/src/app/components/Header/index.tsx +++ b/src/app/components/Header/index.tsx @@ -6,10 +6,12 @@ type Props = { function Header({ title, headerLeft, headerRight }: Props) { return ( -
-
{headerLeft}
-

{title}

-
{headerRight}
+
+
+
{headerLeft}
+

{title}

+
{headerRight}
+
); } diff --git a/src/app/components/LinkButton/index.tsx b/src/app/components/LinkButton/index.tsx index eeea65f5ca..d8d19a7d05 100644 --- a/src/app/components/LinkButton/index.tsx +++ b/src/app/components/LinkButton/index.tsx @@ -9,8 +9,8 @@ type Props = { export default function LinkButton({ to, title, logo }: Props) { return ( -
-
+
+
logo
diff --git a/src/app/components/Menu/MenuList.tsx b/src/app/components/Menu/MenuList.tsx index a59b368a31..c5749c8bfd 100644 --- a/src/app/components/Menu/MenuList.tsx +++ b/src/app/components/Menu/MenuList.tsx @@ -1,12 +1,14 @@ import { Menu, Transition } from "@headlessui/react"; import { Fragment } from "react"; +import { classNames } from "~/app/utils"; type Props = { position?: string; children: React.ReactNode; + fullWidth?: boolean; }; -function List({ position = "left", children }: Props) { +function List({ position = "left", fullWidth, children }: Props) { return ( {children} diff --git a/src/app/components/Navbar/Navbar.tsx b/src/app/components/Navbar/Navbar.tsx index 7e7168f0c9..3478de7107 100644 --- a/src/app/components/Navbar/Navbar.tsx +++ b/src/app/components/Navbar/Navbar.tsx @@ -8,10 +8,14 @@ type Props = { export default function Navbar({ children }: Props) { return (
-
+
+
+ + {children && ( + + )} +
- {children && } -
); diff --git a/src/app/components/Navbar/NavbarLink.tsx b/src/app/components/Navbar/NavbarLink.tsx index 954add7dd5..c61ce78441 100644 --- a/src/app/components/Navbar/NavbarLink.tsx +++ b/src/app/components/Navbar/NavbarLink.tsx @@ -1,4 +1,5 @@ import { NavLink } from "react-router-dom"; +import { classNames } from "~/app/utils"; type Props = { children: React.ReactNode; @@ -12,10 +13,12 @@ function NavbarLink({ children, end = false, href }: Props) { end={end} to={href} className={({ isActive }) => - "block px-1 font-semibold transition-colors duration-200" + - (isActive - ? " text-orange-bitcoin hover:text-orange-bitcoin dark:text-orange-bitcoin" - : " text-gray-500 dark:text-neutral-400 hover:text-gray-700") + classNames( + "block font-semibold px-1 text-md", + isActive + ? " text-gray-900 dark:text-gray-100" + : " text-gray-400 dark:text-gray-400" + ) } > {children} diff --git a/src/app/components/PublisherCard/index.tsx b/src/app/components/PublisherCard/index.tsx index 7c1b387ac0..5247ef78f1 100644 --- a/src/app/components/PublisherCard/index.tsx +++ b/src/app/components/PublisherCard/index.tsx @@ -38,7 +38,7 @@ export default function PublisherCard({ > {image && ( @@ -71,7 +71,7 @@ export default function PublisherCard({ href={`https://${url}`} title={url} target="_blank" - className="text-gray-500 dark:text-gray-400 overflow-hidden mb-2 text-ellipsis whitespace-nowrap" + className="text-gray-500 dark:text-gray-400 overflow-hidden mb-2 text-ellipsis whitespace-nowrap leading-1" rel="noreferrer noopener" > {url} diff --git a/src/app/components/PublishersTable/index.tsx b/src/app/components/PublishersTable/index.tsx index df72462eed..25852a1046 100644 --- a/src/app/components/PublishersTable/index.tsx +++ b/src/app/components/PublishersTable/index.tsx @@ -38,7 +38,7 @@ export default function PublishersTable({
{publisher.host} { @@ -66,11 +66,14 @@ export default function PublishersTable({
{publisher.host} • {publisher.paymentsCount}{" "} - {tComponents("payments")}{" "} + {tComponents("payments")} {publisher.paymentsAmount > 0 && ( - - {getFormattedSats(publisher.paymentsAmount)} - + <> + {" • "} + + {getFormattedSats(publisher.paymentsAmount)} + + )}
diff --git a/src/app/components/Steps/index.tsx b/src/app/components/Steps/index.tsx deleted file mode 100644 index 8ccf8c704c..0000000000 --- a/src/app/components/Steps/index.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { Link } from "react-router-dom"; - -export interface Step { - id: string; - href?: string; - status: "upcoming" | "current" | "complete"; -} - -type Props = { - steps: Step[]; -}; - -export default function Steps({ steps }: Props) { - function createStep(step: Step) { - let outerStyles = ""; - let innerStyles = ""; - switch (step.status) { - case "complete": - outerStyles = - "group pl-4 py-2 flex flex-col border-l-4 border-orange-bitcoin md:pl-0 md:pt-4 md:pb-0 md:border-l-0 md:border-t-4"; - innerStyles = - "text-xs text-black dark:text-white font-500 tracking-wide uppercase"; - break; - case "current": - outerStyles = - "pl-4 py-2 flex flex-col border-l-4 border-orange-bitcoin md:pl-0 md:pt-4 md:pb-0 md:border-l-0 md:border-t-4"; - innerStyles = - "text-xs text-black dark:text-white font-bold tracking-wide uppercase"; - break; - default: - outerStyles = - "group pl-4 py-2 flex flex-col border-l-4 border-gray-200 md:pl-0 md:pt-4 md:pb-0 md:border-l-0 md:border-t-4"; - innerStyles = - "text-xs text-gray-500 font-normal tracking-wide uppercase"; - if (step.href) { - outerStyles += " hover:border-gray-300"; - innerStyles += " group-hover:text-gray-700"; - } - break; - } - if (step.href) { - return ( - - {step.id} - - ); - } - return ( -
- {step.id} -
- ); - } - - return ( - - ); -} diff --git a/src/app/components/Tips/index.test.tsx b/src/app/components/Tips/index.test.tsx new file mode 100644 index 0000000000..35a87ae5bf --- /dev/null +++ b/src/app/components/Tips/index.test.tsx @@ -0,0 +1,32 @@ +import Tips from "@components/Tips"; +import { render, screen } from "@testing-library/react"; +import { I18nextProvider } from "react-i18next"; +import { MemoryRouter } from "react-router-dom"; +import i18n from "~/../tests/unit/helpers/i18n"; +import { TIPS } from "~/common/constants"; + +jest.mock("~/app/hooks/useTips", () => ({ + useTips: () => ({ + tips: [TIPS.TOP_UP_WALLET, TIPS.PIN, TIPS.DEMO], + }), +})); + +describe("Tips", () => { + test("should have 3 tips", async () => { + render( + + + + + + ); + + expect( + await screen.findByText("⚡️ Top up your wallet") + ).toBeInTheDocument(); + expect( + await screen.findByText("📌 Pin your Alby extension") + ).toBeInTheDocument(); + expect(await screen.findByText("🕹️ Try out Alby Demo")).toBeInTheDocument(); + }); +}); diff --git a/src/app/components/Tips/index.tsx b/src/app/components/Tips/index.tsx new file mode 100644 index 0000000000..593933bbe2 --- /dev/null +++ b/src/app/components/Tips/index.tsx @@ -0,0 +1,113 @@ +import Button from "@components/Button"; +import CloseableCard from "@components/CloseableCard"; +import { Fragment } from "react"; +import { Trans, useTranslation } from "react-i18next"; +import { useNavigate } from "react-router-dom"; +import { useTips } from "~/app/hooks/useTips"; +import { TIPS } from "~/common/constants"; + +export default function Tips() { + const { t } = useTranslation("translation", { + keyPrefix: "discover.tips", + }); + + const navigate = useNavigate(); + + const { tips, closeTip } = useTips(); + + function hasTip(id: TIPS) { + return tips.includes(id); + } + + const tipElements = [] as JSX.Element[]; + + if (hasTip(TIPS.TOP_UP_WALLET)) { + tipElements.push( + closeTip(TIPS.TOP_UP_WALLET)} + title={t("top_up_wallet.title")} + description={t("top_up_wallet.description")} + buttons={[ + +
+ + {exportLoading && ( +
+ + {t("export.waiting")} +
+ )} + {!exportLoading && ( +
+ {lndHubData.lnAddress && ( +
+

+ {t("export.your_ln_address")} +

+ {lndHubData.lnAddress &&

{lndHubData.lnAddress}

} +
+ )} +
+
+

+ {t("export.tip_mobile")} +

+

{t("export.scan_qr")}

+
+
+ +
+
+
+ +
+
+ )} + +
+ +
+
+
{ + e.preventDefault(); + updateAccountName({ + id: account.id, + name: accountName, + }); + const updatedAccount = account; + updatedAccount.name = accountName; + setAccount(updatedAccount); + }} + className="my-4 flex justify-between items-end" + > +
+ { + setAccountName(event.target.value); + }} + /> +
+
+
+
+
+
+

+ {t("nostr.title")} +

+

+ + {t("nostr.title")} + {" "} + {t("nostr.hint")} +

+
+
+
+ + {t("nostr.private_key.title")} + +

+ , + ]} + /> +

+
+
+
+
+
{ + e.preventDefault(); + saveNostrPrivateKey(nostrPrivateKey); + }} + className="mb-4 flex justify-between items-end" + > +
+ { + setNostrPrivateKey(event.target.value); + }} + endAdornment={ + + } + /> +
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+ + ⛔️ Danger Zone + +
+
+
+ +
+
+
+
+ + +
+

+ {t("nostr.generate_keys.title")} +

+ +
+
+ , + ]} + /> +
+
+
+
+
+
+
+ +
+ ); +} + +export default AccountScreen; diff --git a/src/app/screens/Accounts/index.test.tsx b/src/app/screens/Accounts/index.test.tsx index cb9887287a..8e8742c47d 100644 --- a/src/app/screens/Accounts/index.test.tsx +++ b/src/app/screens/Accounts/index.test.tsx @@ -1,5 +1,7 @@ import { render, screen } from "@testing-library/react"; +import { I18nextProvider } from "react-i18next"; import { MemoryRouter } from "react-router-dom"; +import i18n from "~/../tests/unit/helpers/i18n"; import { AccountsProvider } from "../../context/AccountsContext"; import Accounts from "./index"; @@ -8,9 +10,11 @@ describe("Accounts", () => { test("render", async () => { render( - - - + + + + + ); expect(await screen.findByText("Accounts")).toBeInTheDocument(); diff --git a/src/app/screens/Accounts/index.tsx b/src/app/screens/Accounts/index.tsx index 7bcc74fb05..f54b469980 100644 --- a/src/app/screens/Accounts/index.tsx +++ b/src/app/screens/Accounts/index.tsx @@ -1,113 +1,21 @@ import { - EllipsisIcon, + CaretRightIcon, PlusIcon, - WalletIcon, } from "@bitcoin-design/bitcoin-icons-react/filled"; -import { CrossIcon } from "@bitcoin-design/bitcoin-icons-react/outline"; import Button from "@components/Button"; import Container from "@components/Container"; -import Loading from "@components/Loading"; -import Menu from "@components/Menu"; -import TextField from "@components/form/TextField"; -import type { FormEvent } from "react"; -import { useState } from "react"; +import Avvvatars from "avvvatars-react"; import { useTranslation } from "react-i18next"; -import Modal from "react-modal"; -import QRCode from "react-qr-code"; import { useNavigate } from "react-router-dom"; -import { useAccount } from "~/app/context/AccountContext"; import { useAccounts } from "~/app/context/AccountsContext"; -import api from "~/common/lib/api"; -import msg from "~/common/lib/msg"; -import type { Account } from "~/types"; - -type AccountAction = Omit; function AccountsScreen() { - const auth = useAccount(); - const { accounts, getAccounts } = useAccounts(); + const { accounts } = useAccounts(); const navigate = useNavigate(); - const [currentAccountId, setCurrentAccountId] = useState(""); - const [editModalIsOpen, setEditModalIsOpen] = useState(false); - const [exportModalIsOpen, setExportModalIsOpen] = useState(false); - const [newAccountName, setNewAccountName] = useState(""); - const [lndHubData, setLndHubData] = useState({ - login: "", - password: "", - url: "", - lnAddress: "", - }); - const [loading, setLoading] = useState(false); const { t } = useTranslation("translation", { keyPrefix: "accounts", }); - const { t: tCommon } = useTranslation("common"); - - function closeEditModal() { - setEditModalIsOpen(false); - } - - function closeExportModal() { - setExportModalIsOpen(false); - } - - async function selectAccount(accountId: string) { - auth.setAccountId(accountId); - await api.selectAccount(accountId); - auth.fetchAccountInfo({ accountId }); - } - - async function updateAccountName({ id, name }: AccountAction) { - await msg.request("editAccount", { - name, - id, - }); - - auth.fetchAccountInfo(); // Update active account name - getAccounts(); // update all accounts - closeEditModal(); - } - - async function exportAccount({ id, name }: AccountAction) { - setLoading(true); - /** - * @HACK - * @headless-ui/menu restores focus after closing a menu, to the button that opened it. - * By slightly delaying opening the modal, react-modal's focus management won't be overruled. - * {@link https://github.com/tailwindlabs/headlessui/issues/259} - */ - setTimeout(() => { - setExportModalIsOpen(true); - }, 50); - setLndHubData( - await msg.request("accountDecryptedDetails", { - name, - id, - }) - ); - setLoading(false); - } - - async function removeAccount({ id, name }: AccountAction) { - if (window.confirm(t("remove.confirm", { name }))) { - let nextAccountId; - let accountIds = Object.keys(accounts); - if (auth.account?.id === id && accountIds.length > 1) { - nextAccountId = accountIds.filter((accountId) => accountId !== id)[0]; - } - - await api.removeAccount(id); - accountIds = accountIds.filter((accountId) => accountId !== id); - - if (accountIds.length > 0) { - getAccounts(); - if (nextAccountId) selectAccount(nextAccountId); - } else { - window.close(); - } - } - } return ( @@ -122,20 +30,28 @@ function AccountsScreen() { />
-
+
- + {Object.keys(accounts).map((accountId) => { const account = accounts[accountId]; return ( - + navigate(`/accounts/${accountId}`)} + > - ); })}
-
- +
+
-

+

{account.name}

@@ -144,175 +60,14 @@ function AccountsScreen() {

- - - - - - - { - setCurrentAccountId(accountId); - setNewAccountName(account.name); - /** - * @HACK - * @headless-ui/menu restores focus after closing a menu, to the button that opened it. - * By slightly delaying opening the modal, react-modal's focus management won't be overruled. - * {@link https://github.com/tailwindlabs/headlessui/issues/259} - */ - setTimeout(() => { - setEditModalIsOpen(true); - }, 50); - }} - > - {tCommon("actions.edit")} - - - {account.connector === "lndhub" && ( - - exportAccount({ - id: accountId, - name: account.name, - }) - } - > - {tCommon("actions.export")} - - )} - - - removeAccount({ - id: accountId, - name: account.name, - }) - } - > - {tCommon("actions.remove")} - - - + +
- - -
-

- {t("edit.title")} -

- -
- -
{ - e.preventDefault(); - updateAccountName({ - id: currentAccountId, - name: newAccountName, - }); - }} - > -
-
- setNewAccountName(e.target.value)} - value={newAccountName} - /> -
-
- -
-
-
-
- - -
-

- {t("export.title")} -

- -
- - {loading && ( -
- - {t("export.waiting")} -
- )} - {!loading && ( -
- {lndHubData.lnAddress && ( -
-

- {t("export.your_ln_address")} -

- {lndHubData.lnAddress &&

{lndHubData.lnAddress}

} -
- )} -
-
-

- {t("export.tip_mobile")} -

-

{t("export.scan_qr")}

-
-
- -
-
-
- -
-
- )} -
); diff --git a/src/app/screens/Discover/index.tsx b/src/app/screens/Discover/index.tsx index 0be68e0178..eb2b7fab8a 100644 --- a/src/app/screens/Discover/index.tsx +++ b/src/app/screens/Discover/index.tsx @@ -1,39 +1,54 @@ import Container from "@components/Container"; import { useTranslation } from "react-i18next"; +import Tips from "~/app/components/Tips"; +import { useTips } from "~/app/hooks/useTips"; import websites from "./websites.json"; function Discover() { - const { t } = useTranslation("translation", { - keyPrefix: "discover", - }); + const { tips } = useTips(); + const { t } = useTranslation("translation"); return ( + {tips.length > 0 && ( + <> +

+ {t("discover.tips.title")} +

+

+ {t("discover.tips.description")} +

+
+ +
+ + )} +

- {t("title")} + {t("discover.title")}

- {t("description")} + {t("discover.description")}

{websites.map(({ title, items }) => ( -
-

- {t(`list.${title as "trading"}`)} +
+

+ {t(`discover.list.${title as "trading"}`)}

-
+
{items.map(({ title, subtitle, logo, url }) => ( -
+
image
diff --git a/src/app/screens/Discover/websites.json b/src/app/screens/Discover/websites.json index 6b8d6ce12c..3871da8cce 100644 --- a/src/app/screens/Discover/websites.json +++ b/src/app/screens/Discover/websites.json @@ -5,19 +5,19 @@ { "title": "LNMarkets", "subtitle": "Instant Bitcoin derivatives trading", - "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/lnmarketes.svg", + "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/lnmarkets.png", "url": "https://lnmarkets.com/" }, { "title": "Kollider", "subtitle": "Instant Bitcoin derivatives trading", - "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/kollider_thumbnail.svg", + "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/kollider_thumbnail.png", "url": "https://kollider.xyz/" }, { "title": "LOFT", "subtitle": "Trade investment products in Bitcoin", - "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/loft-trade_thumbnail.svg", + "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/loft-trade_thumbnail.png", "url": "https://loft.trade/" } ] @@ -28,13 +28,13 @@ { "title": "Lightning Poker", "subtitle": "Play Poker with Bitcoin", - "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/lightning-poker_thumbnail.svg", + "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/lightning-poker_thumbnail.png", "url": "https://lightning-poker.com/" }, { "title": "LNBlackJack", "subtitle": "Play Blackjack with Bitcoin", - "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/lnblackjack_thumbnail.svg", + "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/lnblackjack_thumbnail.png", "url": "https://www.lnblackjack.com/" }, { @@ -46,7 +46,7 @@ { "title": "LNflip", "subtitle": "Play coin flip matches against other players", - "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/lnflip_thumbnail.svg", + "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/lnflip_thumbnail.png", "url": "https://www.lnflip.com/" } ] @@ -57,25 +57,25 @@ { "title": "Podverse", "subtitle": "Podcast player with bitcoin payments", - "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/podverse_thumbnail.svg", + "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/podverse_thumbnail.png", "url": "https://podverse.fm/" }, { "title": "Stacker.News", "subtitle": "Lightning powered Bitcoin news site", - "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/stacker-news_thumbnail.svg", + "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/stacker-news_thumbnail.png", "url": "https://stacker.news/" }, { "title": "Wavlake", "subtitle": "Lightning powered music player", - "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/wavlake.svg", + "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/wavlake.png", "url": "https://www.wavlake.com/" }, { "title": "Y'alls", "subtitle": "Articles about the Lightning Network", - "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/yalls.svg", + "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/yalls.png", "url": "https://yalls.org/" } ] @@ -92,7 +92,7 @@ { "title": "Lightning Network Stores", "subtitle": "Collection of stores and websites accepting bitcoin", - "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/lightning-network-stores_thumbnail.svg", + "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/lightning-network-stores_thumbnail.png", "url": "https://lightningnetworkstores.com/" }, { @@ -103,6 +103,41 @@ } ] }, + { + "title": "nostr", + "items": [ + { + "title": "Astral", + "subtitle": "A Twitter-like client with chat and one-click payments.", + "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/astral_thumbnail.png", + "url": "https://astral.ninja/" + }, + { + "title": "emon.chat", + "subtitle": "Instant messaging client with built in lightning payments.", + "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/emon_thumbnail.svg", + "url": "https://emon.chat/" + }, + { + "title": "Hamstr", + "subtitle": "A twitter-style nostr web client", + "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/hamstr-to.png", + "url": "https://hamstr.to/home" + }, + { + "title": "Snort", + "subtitle": "Fast nostr web client.", + "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/snort_thumbnail.png", + "url": "https://snort.social" + }, + { + "title": "Yo, Sup?", + "subtitle": "The blue bird experience for Nostr.", + "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/yosup_thumbnail.svg", + "url": "https://yosup.app/" + } + ] + }, { "title": "miscellaneous", "items": [ @@ -121,31 +156,31 @@ { "title": "Geyser", "subtitle": "Crowdfunding projects with Bitcoin Lightning", - "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/geyser_thumbnail.svg", + "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/geyser_thumbnail.png", "url": "https://geyser.fund/" }, { "title": "Amboss", "subtitle": "Lightning Network node explorer", - "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/amboss-space_thumbnail.svg", + "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/amboss-space_thumbnail.png", "url": "https://amboss.space/" }, { "title": "Lightsats", "subtitle": "Onboard people to bitcoin by sending them tips", - "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/lightsats_thumbnail.svg", + "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/lightsats_thumbnail.png", "url": "https://lightsats.com/" }, { "title": "Sats for Likes", "subtitle": "Earn sats for accomplishing tasks", - "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/sats4likes.svg", + "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/sats4likes.png", "url": "https://www.sats4likes.com/" }, { "title": "Vida", "subtitle": "Earn sats anytime someone wants to connect and chatt", - "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/vida_thumbnail.svg", + "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/vida_thumbnail.png", "url": "https://vida.page/" }, { @@ -153,6 +188,12 @@ "subtitle": "Get booked and paid in Bitcoin", "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/lncal_thumbnail.svg", "url": "https://lncal.com/" + }, + { + "title": "channel.ninja", + "subtitle": "Get recommended channel partners for your lightning node", + "logo": "https://cdn.getalby-assets.com/alby-extension-website-screen/channelninja.png", + "url": "https://channel.ninja/" } ] } diff --git a/src/app/screens/Home/DefaultView/index.tsx b/src/app/screens/Home/DefaultView/index.tsx index 869709977f..1816ee098d 100644 --- a/src/app/screens/Home/DefaultView/index.tsx +++ b/src/app/screens/Home/DefaultView/index.tsx @@ -1,6 +1,6 @@ import { - SendIcon, ReceiveIcon, + SendIcon, } from "@bitcoin-design/bitcoin-icons-react/filled"; import Button from "@components/Button"; import Loading from "@components/Loading"; @@ -8,8 +8,7 @@ import TransactionsTable from "@components/TransactionsTable"; import { Tab } from "@headlessui/react"; import dayjs from "dayjs"; import relativeTime from "dayjs/plugin/relativeTime"; -import { FC } from "react"; -import { useState, useEffect } from "react"; +import { FC, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { toast } from "react-toastify"; @@ -181,6 +180,7 @@ const DefaultView: FC = (props) => { navigate("/send"); }} /> +
diff --git a/src/app/screens/Nostr/ConfirmGetPublicKey.tsx b/src/app/screens/Nostr/ConfirmGetPublicKey.tsx index 75a2531e2d..14b186819c 100644 --- a/src/app/screens/Nostr/ConfirmGetPublicKey.tsx +++ b/src/app/screens/Nostr/ConfirmGetPublicKey.tsx @@ -16,6 +16,7 @@ function NostrConfirmGetPublicKey() { keyPrefix: "nostr", }); const { t: tCommon } = useTranslation("common"); + const { t: tPermissions } = useTranslation("permissions"); const navState = useNavigationState(); const origin = navState.origin as OriginData; const [loading, setLoading] = useState(false); @@ -66,7 +67,9 @@ function NostrConfirmGetPublicKey() {

{t("allow")}

-

{t("read_public_key")}

+

+ {tPermissions("nostr.getpublickey")} +

@@ -85,7 +88,7 @@ function NostrConfirmGetPublicKey() { htmlFor="remember_permission" className="cursor-pointer ml-2 block text-sm text-gray-900 font-medium dark:text-white" > - {t("confirm_sign_message.remember.label")} + {tCommon("actions.remember")}
diff --git a/src/app/screens/Nostr/ConfirmSignMessage.tsx b/src/app/screens/Nostr/ConfirmSignMessage.tsx index 817c2782c2..0d63ae2b0d 100644 --- a/src/app/screens/Nostr/ConfirmSignMessage.tsx +++ b/src/app/screens/Nostr/ConfirmSignMessage.tsx @@ -78,7 +78,7 @@ function ConfirmSignMessage() { url={origin.host} />
@@ -94,7 +94,7 @@ function ConfirmSignMessage() { htmlFor="remember_permission" className="cursor-pointer ml-2 block text-sm text-gray-900 font-medium dark:text-white" > - {t("confirm_sign_message.remember.label")} + {tCommon("actions.remember")}
diff --git a/src/app/screens/Onboard/Intro/features.tsx b/src/app/screens/Onboard/Intro/features.tsx deleted file mode 100644 index 3f2dc9aa46..0000000000 --- a/src/app/screens/Onboard/Intro/features.tsx +++ /dev/null @@ -1,31 +0,0 @@ -type Feature = { - name: string; - description: string; - icon: (props: React.ComponentProps<"svg">) => JSX.Element; -}; - -type Props = { - features: Feature[]; -}; - -export default function Features({ features }: Props) { - return ( -
- {features.map((feature) => ( -
-
-
-
-

- {feature.name} -

-
-
- {feature.description} -
-
- ))} -
- ); -} diff --git a/src/app/screens/Onboard/Intro/index.tsx b/src/app/screens/Onboard/Intro/index.tsx deleted file mode 100644 index da867eddd8..0000000000 --- a/src/app/screens/Onboard/Intro/index.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { - CodeIcon, - KeyIcon, - LightningIcon, - ShieldIcon, -} from "@bitcoin-design/bitcoin-icons-react/filled"; -import Button from "@components/Button"; -import { useTranslation } from "react-i18next"; -import { useNavigate } from "react-router-dom"; -import i18n from "~/i18n/i18nConfig"; - -import Features from "./features"; - -function getFeatures() { - return [ - { - name: i18n.t("translation:welcome.intro.send.title"), - description: i18n.t("translation:welcome.intro.send.description"), - icon: LightningIcon, - }, - { - name: i18n.t("translation:welcome.intro.paywall.title"), - description: i18n.t("translation:welcome.intro.paywall.description"), - icon: KeyIcon, - }, - { - name: i18n.t("translation:welcome.intro.privacy.title"), - description: i18n.t("translation:welcome.intro.privacy.description"), - icon: ShieldIcon, - }, - { - name: i18n.t("translation:welcome.intro.foss.title"), - description: i18n.t("translation:welcome.intro.foss.description"), - icon: CodeIcon, - }, - ]; -} - -let features = getFeatures(); - -export default function Intro() { - const navigate = useNavigate(); - const { t } = useTranslation(); - - i18n.on("languageChanged", () => { - features = getFeatures(); - }); - - return ( -
-
-
-
- Alby - Alby -
-
-
- -
-
-
-
-
- ); -} diff --git a/src/app/screens/Onboard/SetPassword/index.tsx b/src/app/screens/Onboard/SetPassword/index.tsx index 7335c0dcaa..f6be080030 100644 --- a/src/app/screens/Onboard/SetPassword/index.tsx +++ b/src/app/screens/Onboard/SetPassword/index.tsx @@ -32,15 +32,28 @@ export default function SetPassword() { } } + const unlockScreenshot = ( + Unlock screen + ); + return (
-
+
-

{t("title")}

+

+ {t("title")} +

+
+ {unlockScreenshot} +

{t("description")}

-
+
-
-
- Unlock screen -
+
+ {unlockScreenshot}
@@ -67,6 +74,7 @@ export default function SetPassword() { !formData.password || formData.password !== formData.passwordConfirmation } + className="max-sm:w-full" />
diff --git a/src/app/screens/Onboard/TestConnection/index.tsx b/src/app/screens/Onboard/TestConnection/index.tsx index 7f1946a9ce..63cb126c34 100644 --- a/src/app/screens/Onboard/TestConnection/index.tsx +++ b/src/app/screens/Onboard/TestConnection/index.tsx @@ -1,30 +1,19 @@ import Button from "@components/Button"; -import Card from "@components/Card"; import Loading from "@components/Loading"; import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; -import { useSettings } from "~/app/context/SettingsContext"; import api from "~/common/lib/api"; import msg from "~/common/lib/msg"; -import type { AccountInfo } from "~/types"; +import utils from "~/common/lib/utils"; export default function TestConnection() { - const [accountInfo, setAccountInfo] = useState<{ - alias: AccountInfo["alias"]; - name: AccountInfo["name"]; - balance: AccountInfo["balance"]; - currency: AccountInfo["currency"]; - }>(); const [errorMessage, setErrorMessage] = useState(""); const [loading, setLoading] = useState(false); - const { getFormattedInCurrency } = useSettings(); - const { t } = useTranslation("translation", { keyPrefix: "welcome.test_connection", }); - const { t: tCommon } = useTranslation("common"); const navigate = useNavigate(); @@ -38,15 +27,14 @@ export default function TestConnection() { const timer = setTimeout(() => { setErrorMessage(t("connection_taking_long")); }, 45000); + try { const response = await api.getAccountInfo(); - const name = response.name; - const { alias } = response.info; - const { balance: resBalance, currency } = response.balance; - const balance = - typeof resBalance === "number" ? resBalance : parseInt(resBalance); - - setAccountInfo({ alias, balance, name, currency }); + if (response.name && response.info.alias) { + utils.redirectPage("options.html#/discover"); + } else { + setErrorMessage(t("connection_error")); + } } catch (e) { const message = e instanceof Error ? `(${e.message})` : ""; console.error(message); @@ -63,7 +51,7 @@ export default function TestConnection() { }, []); return ( -
+
{errorMessage && ( @@ -90,41 +78,6 @@ export default function TestConnection() {
)} - {accountInfo && accountInfo.alias && ( -
- )} - {loading && (
diff --git a/src/app/screens/Options/TestConnection/index.tsx b/src/app/screens/Options/TestConnection/index.tsx index c9a46118dd..7d0a0be4ba 100644 --- a/src/app/screens/Options/TestConnection/index.tsx +++ b/src/app/screens/Options/TestConnection/index.tsx @@ -75,7 +75,7 @@ export default function TestConnection() { return (
-
+
{errorMessage && ( @@ -118,7 +118,7 @@ export default function TestConnection() {

{t("ready")}

-
+
- -
); diff --git a/src/app/screens/Publisher.tsx b/src/app/screens/Publishers/Show/index.tsx similarity index 100% rename from src/app/screens/Publisher.tsx rename to src/app/screens/Publishers/Show/index.tsx diff --git a/src/app/screens/Publishers/index.test.tsx b/src/app/screens/Publishers/index.test.tsx index 9650f76c85..00309f7155 100644 --- a/src/app/screens/Publishers/index.test.tsx +++ b/src/app/screens/Publishers/index.test.tsx @@ -54,7 +54,7 @@ describe("Publishers", () => { ); - expect(await screen.findByText("Your ⚡️ Websites")).toBeInTheDocument(); + expect(await screen.findByText("Your ⚡ Websites")).toBeInTheDocument(); expect(await screen.findByText("DALL·E 2")).toBeInTheDocument(); expect(await screen.findByText("ACTIVE")).toBeInTheDocument(); expect(await screen.findByText("LOGIN")).toBeInTheDocument(); @@ -80,7 +80,7 @@ describe("Publishers", () => { ); - expect(await screen.findByText("Your ⚡️ Websites")).toBeInTheDocument(); + expect(await screen.findByText("Your ⚡ Websites")).toBeInTheDocument(); expect( await screen.findByText( "It looks like you haven't used Alby in any websites yet." diff --git a/src/app/screens/Settings.tsx b/src/app/screens/Settings/index.tsx similarity index 76% rename from src/app/screens/Settings.tsx rename to src/app/screens/Settings/index.tsx index 6321a0b716..0c14b95d31 100644 --- a/src/app/screens/Settings.tsx +++ b/src/app/screens/Settings/index.tsx @@ -1,8 +1,4 @@ -import { - CrossIcon, - HiddenIcon, - VisibleIcon, -} from "@bitcoin-design/bitcoin-icons-react/outline"; +import { CrossIcon } from "@bitcoin-design/bitcoin-icons-react/outline"; import Button from "@components/Button"; import Container from "@components/Container"; import LocaleSwitcher from "@components/LocaleSwitcher/LocaleSwitcher"; @@ -10,18 +6,17 @@ import PasswordForm from "@components/PasswordForm"; import Setting from "@components/Setting"; import Input from "@components/form/Input"; import Select from "@components/form/Select"; -import TextField from "@components/form/TextField"; import Toggle from "@components/form/Toggle"; import { Html5Qrcode } from "html5-qrcode"; import type { FormEvent } from "react"; -import { useState, useEffect } from "react"; -import { useTranslation, Trans } from "react-i18next"; +import { useState } from "react"; +import { Trans, useTranslation } from "react-i18next"; import Modal from "react-modal"; +import { useNavigate } from "react-router-dom"; import { toast } from "react-toastify"; import { useSettings } from "~/app/context/SettingsContext"; import { CURRENCIES } from "~/common/constants"; import msg from "~/common/lib/msg"; -import nostrlib from "~/common/lib/nostr"; const initialFormData = { password: "", @@ -31,24 +26,11 @@ const initialFormData = { function Settings() { const { t } = useTranslation("translation", { keyPrefix: "settings" }); const { isLoading, settings, updateSetting } = useSettings(); + const navigate = useNavigate(); const [modalIsOpen, setModalIsOpen] = useState(false); const [formData, setFormData] = useState(initialFormData); - const [nostrPrivateKey, setNostrPrivateKey] = useState(""); - const [nostrPrivateKeyVisible, setNostrPrivateKeyVisible] = useState(false); - - const getPrivateKeyFromStorage = async () => { - const priv = (await msg.request("nostr/getPrivateKey")) as string; - if (priv) { - setNostrPrivateKey(nostrlib.hexToNip19(priv, "nsec")); - } - }; - - useEffect(() => { - getPrivateKeyFromStorage().catch(console.error); - }, []); - const [cameraPermissionsGranted, setCameraPermissionsGranted] = useState(false); @@ -56,26 +38,6 @@ function Settings() { setModalIsOpen(false); } - async function saveNostrPrivateKey(nostrPrivateKey: string) { - const result = await msg.request("nostr/getPrivateKey"); - const currentPrivateKey = result as unknown as string; - - if (nostrPrivateKey === currentPrivateKey) return; - - if (currentPrivateKey && !confirm(t("nostr.private_key.warning"))) { - return; - } - - await msg.request("nostr/setPrivateKey", { - privateKey: nostrlib.normalizeToHex(nostrPrivateKey), - }); - - saveSetting({ - nostrEnabled: !!nostrPrivateKey, - }); - toast.success(t("nostr.private_key.success")); - } - async function updateAccountPassword(password: string) { await msg.request("changePassword", { password: formData.password, @@ -289,7 +251,7 @@ function Settings() { {t("personal_data.description")}

-
+
{!isLoading && (
@@ -370,14 +332,9 @@ function Settings() {
-
-
- - 🧪 Alby Lab - -
-
-

{t("nostr.title")}

+

+ {t("nostr.title")} +

, - ]} - /> - } + subtitle={t("nostr.private_key.subtitle")} > -

diff --git a/src/app/screens/connectors/ChooseConnector/index.tsx b/src/app/screens/connectors/ChooseConnector/index.tsx index 38c2a3819e..b9416efa3e 100644 --- a/src/app/screens/connectors/ChooseConnector/index.tsx +++ b/src/app/screens/connectors/ChooseConnector/index.tsx @@ -13,17 +13,19 @@ export default function ChooseConnector({ title, description }: Props) { connectorRoutes = getConnectorRoutes(); }); return ( -
+
-
-

{title}

+
+

+ {title} +

{description && (

{description}

)}
-
+
{connectorRoutes.map(({ path, title, logo }) => ( ))} diff --git a/src/app/screens/connectors/ChooseConnectorPath/index.tsx b/src/app/screens/connectors/ChooseConnectorPath/index.tsx index 5f6f427593..d26a5c546e 100644 --- a/src/app/screens/connectors/ChooseConnectorPath/index.tsx +++ b/src/app/screens/connectors/ChooseConnectorPath/index.tsx @@ -22,7 +22,7 @@ export default function ChooseConnectorPath({ title, description }: Props) { }); const { t: tCommon } = useTranslation("common"); return ( -
+

{title}

@@ -35,7 +35,7 @@ export default function ChooseConnectorPath({ title, description }: Props) { )}
-
+
+
{connectorRoutes.slice(0, 7).map(({ path, title, logo }) => ( ) { + setFormData({ + ...formData, + [event.target.name]: event.target.value.trim(), + }); + } + + async function handleSubmit(event: React.FormEvent) { + event.preventDefault(); + setLoading(true); + const { pairingPhrase } = formData; + const account = { + name: "LND", + config: { + pairingPhrase, + }, + connector: "lnc", + }; + + try { + const validation = { valid: true, error: "" }; // opening and closing a connection to fast causes some problems. I've seen "channel occupied errors" await utils.call("validateAccount", account); + + if (validation.valid) { + const addResult = await msg.request("addAccount", account); + if (addResult.accountId) { + await msg.request("selectAccount", { + id: addResult.accountId, + }); + navigate("/test-connection"); + } + } else { + console.error(validation); + toast.error( + + ); + } + } catch (e) { + console.error(e); + let message = "LNC connection failed"; + if (e instanceof Error) { + message += `\n\n${e.message}`; + } + toast.error(message); + } + setLoading(false); + } + + return ( + +
+ +
+
+ ); +} diff --git a/src/app/screens/connectors/ConnectMyNode/index.tsx b/src/app/screens/connectors/ConnectMyNode/index.tsx index 8d19e4ee30..d2d89b29d2 100644 --- a/src/app/screens/connectors/ConnectMyNode/index.tsx +++ b/src/app/screens/connectors/ConnectMyNode/index.tsx @@ -3,7 +3,7 @@ import ConnectorForm from "@components/ConnectorForm"; import TextField from "@components/form/TextField"; import ConnectionErrorToast from "@components/toasts/ConnectionErrorToast"; import { useState } from "react"; -import { useTranslation, Trans } from "react-i18next"; +import { Trans, useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { toast } from "react-toastify"; import msg from "~/common/lib/msg"; diff --git a/src/app/screens/connectors/ConnectRaspiBlitz/index.tsx b/src/app/screens/connectors/ConnectRaspiBlitz/index.tsx index 5c9c158fe1..5eaad41e21 100644 --- a/src/app/screens/connectors/ConnectRaspiBlitz/index.tsx +++ b/src/app/screens/connectors/ConnectRaspiBlitz/index.tsx @@ -3,7 +3,7 @@ import ConnectorForm from "@components/ConnectorForm"; import TextField from "@components/form/TextField"; import ConnectionErrorToast from "@components/toasts/ConnectionErrorToast"; import { useState } from "react"; -import { useTranslation, Trans } from "react-i18next"; +import { Trans, useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { toast } from "react-toastify"; import msg from "~/common/lib/msg"; diff --git a/src/app/screens/connectors/ConnectStart9/index.tsx b/src/app/screens/connectors/ConnectStart9/index.tsx index 45c1106ef4..02c346b248 100644 --- a/src/app/screens/connectors/ConnectStart9/index.tsx +++ b/src/app/screens/connectors/ConnectStart9/index.tsx @@ -3,7 +3,7 @@ import ConnectorForm from "@components/ConnectorForm"; import TextField from "@components/form/TextField"; import ConnectionErrorToast from "@components/toasts/ConnectionErrorToast"; import { useState } from "react"; -import { useTranslation, Trans } from "react-i18next"; +import { Trans, useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { toast } from "react-toastify"; import msg from "~/common/lib/msg"; diff --git a/src/app/screens/connectors/ConnectUmbrel/index.tsx b/src/app/screens/connectors/ConnectUmbrel/index.tsx index f27fa59922..ee703d51e5 100644 --- a/src/app/screens/connectors/ConnectUmbrel/index.tsx +++ b/src/app/screens/connectors/ConnectUmbrel/index.tsx @@ -3,7 +3,7 @@ import ConnectorForm from "@components/ConnectorForm"; import TextField from "@components/form/TextField"; import ConnectionErrorToast from "@components/toasts/ConnectionErrorToast"; import { useState } from "react"; -import { useTranslation, Trans } from "react-i18next"; +import { Trans, useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { toast } from "react-toastify"; import msg from "~/common/lib/msg"; diff --git a/src/app/styles/_fonts.scss b/src/app/styles/_fonts.scss deleted file mode 100644 index 52ca8dcf6c..0000000000 --- a/src/app/styles/_fonts.scss +++ /dev/null @@ -1 +0,0 @@ -@import url("https://fonts.googleapis.com/css?family=Nunito:400,600"); diff --git a/src/app/utils/index.ts b/src/app/utils/index.ts index 6e743580e4..636da29eac 100644 --- a/src/app/utils/index.ts +++ b/src/app/utils/index.ts @@ -1,4 +1,5 @@ import api from "~/common/lib/api"; +import { BrowserType } from "~/types"; export function classNames(...classes: (string | boolean)[]) { return classes.filter(Boolean).join(" "); @@ -28,3 +29,16 @@ export function getTheme() { } }); } + +export function getBrowserType(): BrowserType | null { + if (!chrome?.runtime) return null; + const url = chrome.runtime.getURL(""); + if (url.startsWith("moz-extension://")) return "firefox"; + if (url.startsWith("chrome-extension://")) return "chrome"; + + return null; +} + +export function isAlbyAccount(alias = "") { + return alias === "🐝 getalby.com"; +} diff --git a/src/common/constants.ts b/src/common/constants.ts index 343ce81395..2dc736e484 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -161,3 +161,10 @@ export enum CURRENCIES { ZMW = "ZMW", ZWL = "ZWL", } + +export enum TIPS { + TOP_UP_WALLET = "top_up_wallet", + PIN = "pin", + DEMO = "demo", + ADDRESS = "address", +} diff --git a/src/common/lib/api.ts b/src/common/lib/api.ts index 246b604ee5..0c77c2a1bd 100644 --- a/src/common/lib/api.ts +++ b/src/common/lib/api.ts @@ -6,6 +6,7 @@ import { MakeInvoiceResponse, } from "~/extension/background-script/connectors/connector.interface"; import type { + Account, AccountInfo, Accounts, Allowance, @@ -33,6 +34,10 @@ export interface AccountInfoRes { name: string; } +export interface GetAccountRes + extends Pick { + nostrEnabled: boolean; +} interface StatusRes { configured: boolean; unlocked: boolean; @@ -92,6 +97,7 @@ export const swrGetAccountInfo = async ( }); }; export const getAccounts = () => msg.request("getAccounts"); +export const getAccount = () => msg.request("getAccount"); export const updateAllowance = () => msg.request("updateAllowance"); export const selectAccount = (id: string) => msg.request("selectAccount", { id }); @@ -130,6 +136,7 @@ export const getCurrencyRate = async () => msg.request<{ rate: number }>("getCurrencyRate"); export default { + getAccount, getAccountInfo, getAccounts, getInfo, diff --git a/src/common/lib/utils.ts b/src/common/lib/utils.ts index 6d1d0adc89..a84a27e60a 100644 --- a/src/common/lib/utils.ts +++ b/src/common/lib/utils.ts @@ -83,6 +83,7 @@ const utils = { left: left, }) .then((window) => { + let closeWindow = true; // by default we call remove.window (except the browser forces this prompt to open in a tab) let tabId: number | undefined; if (window.tabs) { tabId = window.tabs[0].id; @@ -92,6 +93,7 @@ const utils = { // Find the currently active tab to validate messages if (window.tabs && window.tabs?.length > 1) { tabId = window.tabs?.find((x) => x.active)?.id; + closeWindow = false; // we'll only remove the tab and not the window further down } // this interval hightlights the popup in the taskbar @@ -116,21 +118,30 @@ const utils = { responseMessage && responseMessage.response && sender.tab && - sender.tab.id === tabId + sender.tab.id === tabId && + sender.tab.windowId ) { clearInterval(focusInterval); browser.tabs.onRemoved.removeListener(onRemovedListener); - if (sender.tab.id) { - return browser.tabs.remove(sender.tab.id).then(() => { - // in the future actual "remove" (closing prompt) will be moved to component for i.e. budget flow - // https://github.com/getAlby/lightning-browser-extension/issues/1197 - if (responseMessage.error) { - return reject(new Error(responseMessage.error)); - } else { - return resolve(responseMessage); - } - }); + // if the window was opened as tab we remove the tab + // otherwise if a window was opened we have to remove the window. + // Opera fails to close the window with tabs.remove - it fails with: "Tabs cannot be edited right now (user may be dragging a tab)" + let closePromise; + if (closeWindow) { + closePromise = browser.windows.remove(sender.tab.windowId); + } else { + closePromise = browser.tabs.remove(sender.tab.id as number); // as number only for TS - we check for sender.tab.id in the if above } + + return closePromise.then(() => { + // in the future actual "remove" (closing prompt) will be moved to component for i.e. budget flow + // https://github.com/getAlby/lightning-browser-extension/issues/1197 + if (responseMessage.error) { + return reject(new Error(responseMessage.error)); + } else { + return resolve(responseMessage); + } + }); } }; diff --git a/src/extension/background-script/actions/accounts/__tests__/add.test.ts b/src/extension/background-script/actions/accounts/__tests__/add.test.ts index 321539251d..1b6f108575 100644 --- a/src/extension/background-script/actions/accounts/__tests__/add.test.ts +++ b/src/extension/background-script/actions/accounts/__tests__/add.test.ts @@ -27,6 +27,7 @@ const message: MessageAccountAdd = { connector: "lnd", config: "123456config", name: "purple", + nostrPrivateKey: "123456nostr", }, origin: { internal: true }, prompt: true, @@ -57,6 +58,7 @@ describe("add account to account-list", () => { connector: "lnd", config: "secret-config-string-42", name: "purple", + nostrPrivateKey: "123456nostr", }, }, }); @@ -77,11 +79,13 @@ describe("add account to account-list", () => { config: "abc", connector: "lnd", name: "BLUE", + nostrPrivateKey: "123", }, "666": { config: "xyz", connector: "lnd", name: "GREEN", + nostrPrivateKey: "123", }, }, }; @@ -102,9 +106,20 @@ describe("add account to account-list", () => { connector: "lnd", config: "secret-config-string-42", name: "purple", + nostrPrivateKey: "123456nostr", + }, + "666": { + config: "xyz", + connector: "lnd", + name: "GREEN", + nostrPrivateKey: "123", + }, + "888": { + config: "abc", + connector: "lnd", + name: "BLUE", + nostrPrivateKey: "123", }, - "666": { config: "xyz", connector: "lnd", name: "GREEN" }, - "888": { config: "abc", connector: "lnd", name: "BLUE" }, }, }); diff --git a/src/extension/background-script/actions/accounts/__tests__/get.test.ts b/src/extension/background-script/actions/accounts/__tests__/get.test.ts new file mode 100644 index 0000000000..e70944e78c --- /dev/null +++ b/src/extension/background-script/actions/accounts/__tests__/get.test.ts @@ -0,0 +1,87 @@ +import type { GetAccountRes } from "~/common/lib/api"; +import state from "~/extension/background-script/state"; +import type { MessageAccountGet } from "~/types"; + +import getAccount from "../get"; + +jest.mock("~/extension/background-script/state"); + +const mockState = { + getConnector: () => ({ + getInfo: () => Promise.resolve({ data: { alias: "getalby.com" } }), + getBalance: () => Promise.resolve({ data: { balance: 0 } }), + }), + getAccount: () => ({ + config: + "U2FsdGVkX19YMFK/8YpN5XQbMsmbVmlOJgpZCIRlt25K6ur4EPp4XdRUQC7+ep/m1k8d2yy69QfuGpsgn2SZOv4DQaPsdYTTwjj0mibQG/dkJ9OCp88zXuMpconrmRu5w4uZWEvdg7p5GQfIYJCvTPLUq+1zH3iH0xX7GhlrlQ8=", + connector: "lndhub", + id: "1e1e8ea6-493e-480b-9855-303d37506e97", + name: "Alby", + }), + currentAccountId: "1e1e8ea6-493e-480b-9855-303d37506e97", + accounts: { + "8b7f1dc6-ab87-4c6c-bca5-19fa8632731e": { + config: "config-123-456", + connector: "lndhub", + id: "8b7f1dc6-ab87-4c6c-bca5-19fa8632731e", + name: "Alby", + nostrPrivateKey: "nostr-123-456", + }, + "1e1e8ea6-493e-480b-9855-303d37506e97": { + config: "config-123-456", + connector: "lndhub", + id: "1e1e8ea6-493e-480b-9855-303d37506e97", + name: "Alby", + }, + }, +}; + +describe("account info", () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + test("get current account info", async () => { + const message: MessageAccountGet = { + application: "LBE", + origin: { internal: true }, + prompt: true, + action: "getAccount", + }; + + state.getState = jest.fn().mockReturnValue(mockState); + + const result: GetAccountRes = { + id: "1e1e8ea6-493e-480b-9855-303d37506e97", + name: "Alby", + connector: "lndhub", + nostrEnabled: false, + }; + + expect(await getAccount(message)).toStrictEqual({ + data: result, + }); + }); + test("get account info by id", async () => { + const message: MessageAccountGet = { + application: "LBE", + origin: { internal: true }, + prompt: true, + args: { id: "8b7f1dc6-ab87-4c6c-bca5-19fa8632731e" }, + action: "getAccount", + }; + + state.getState = jest.fn().mockReturnValue(mockState); + + const result: GetAccountRes = { + id: "8b7f1dc6-ab87-4c6c-bca5-19fa8632731e", + name: "Alby", + connector: "lndhub", + nostrEnabled: true, + }; + + expect(await getAccount(message)).toStrictEqual({ + data: result, + }); + }); +}); diff --git a/src/extension/background-script/actions/accounts/get.ts b/src/extension/background-script/actions/accounts/get.ts new file mode 100644 index 0000000000..d19cb2f238 --- /dev/null +++ b/src/extension/background-script/actions/accounts/get.ts @@ -0,0 +1,29 @@ +import state from "~/extension/background-script/state"; +import type { MessageAccountGet } from "~/types"; + +const get = async (message: MessageAccountGet) => { + const id = message?.args?.id || state.getState().currentAccountId; + + if (!id) + return { + error: "No account selected.", + }; + + const accounts = state.getState().accounts; + const account = accounts[id]; + + if (!account) return; + + const result = { + id: account.id, + connector: account.connector, + name: account.name, + nostrEnabled: !!account.nostrPrivateKey, + }; + + return { + data: result, + }; +}; + +export default get; diff --git a/src/extension/background-script/actions/accounts/index.ts b/src/extension/background-script/actions/accounts/index.ts index 29142f1621..e41ef2f8f9 100644 --- a/src/extension/background-script/actions/accounts/index.ts +++ b/src/extension/background-script/actions/accounts/index.ts @@ -2,10 +2,22 @@ import add from "./add"; import all from "./all"; import decryptedDetails from "./decryptedDetails"; import edit from "./edit"; +import get from "./get"; import info from "./info"; import lock from "./lock"; import remove from "./remove"; import select from "./select"; import unlock from "./unlock"; -export { all, unlock, lock, add, edit, select, info, remove, decryptedDetails }; +export { + all, + unlock, + lock, + add, + edit, + select, + info, + remove, + get, + decryptedDetails, +}; diff --git a/src/extension/background-script/actions/accounts/select.ts b/src/extension/background-script/actions/accounts/select.ts index 9d3970bebc..f64e55505b 100644 --- a/src/extension/background-script/actions/accounts/select.ts +++ b/src/extension/background-script/actions/accounts/select.ts @@ -14,14 +14,16 @@ const select = async (message: MessageAccountSelect) => { state.setState({ account, + nostr: null, // reset memoized nostr instance connector: null, // reset memoized connector currentAccountId: accountId, }); - await state.getState().saveToStorage(); - // init connector this also memoizes the connector in the state object await state.getState().getConnector(); + // save the current account id once the connector is loaded + await state.getState().saveToStorage(); + return { data: { unlocked: true }, }; diff --git a/src/extension/background-script/actions/ln/__tests__/request.test.ts b/src/extension/background-script/actions/ln/__tests__/request.test.ts index ab065a9c6f..9e8c059503 100644 --- a/src/extension/background-script/actions/ln/__tests__/request.test.ts +++ b/src/extension/background-script/actions/ln/__tests__/request.test.ts @@ -43,7 +43,7 @@ const permissionInDB = { allowanceId: allowanceInDB.id, createdAt: "1487076708000", host: allowanceInDB.host, - method: "webln/makeinvoice", + method: "webln/lnd/makeinvoice", blocked: false, enabled: true, }; @@ -59,6 +59,10 @@ const message: MessageGenericRequest = { const requestResponse = { data: [] }; const fullConnector = { + // hacky fix because Jest doesn't return constructor name + constructor: { + name: "lnd", + }, requestMethod: jest.fn(() => Promise.resolve(requestResponse)), supportedMethods: [ // saved and compared in lowercase @@ -197,7 +201,7 @@ describe("ln request", () => { args: { requestPermission: { method: message.args.method.toLowerCase(), - description: "object.makeinvoice", // jest doesn't give back the constructor's name? + description: "lnd.makeinvoice", }, }, origin: message.origin, @@ -224,7 +228,7 @@ describe("ln request", () => { args: { requestPermission: { method: message.args.method.toLowerCase(), - description: "object.makeinvoice", // jest doesn't give back the constructor's name? + description: "lnd.makeinvoice", }, }, origin: message.origin, @@ -256,7 +260,7 @@ describe("ln request", () => { expect(await db.permissions.toArray()).toHaveLength(1); expect( - await db.permissions.get({ method: "webln/getinfo" }) + await db.permissions.get({ method: "webln/lnd/getinfo" }) ).toBeUndefined(); const result = await request(messageWithGetInfo); @@ -271,11 +275,11 @@ describe("ln request", () => { expect(await db.permissions.toArray()).toHaveLength(2); const addedPermission = await db.permissions.get({ - method: "webln/getinfo", + method: "webln/lnd/getinfo", }); expect(addedPermission).toEqual( expect.objectContaining({ - method: "webln/getinfo", + method: "webln/lnd/getinfo", enabled: true, allowanceId: allowanceInDB.id, host: allowanceInDB.host, @@ -303,7 +307,7 @@ describe("ln request", () => { expect(await db.permissions.toArray()).toHaveLength(1); expect( - await db.permissions.get({ method: "webln/sendpayment" }) + await db.permissions.get({ method: "webln/lnd/sendpayment" }) ).toBeUndefined(); const result = await request(messageWithOtherPermission); @@ -317,7 +321,7 @@ describe("ln request", () => { expect(await db.permissions.toArray()).toHaveLength(1); expect( - await db.permissions.get({ method: "webln/sendpayment" }) + await db.permissions.get({ method: "webln/lnd/sendpayment" }) ).toBeUndefined(); expect(result).toStrictEqual(requestResponse); diff --git a/src/extension/background-script/actions/ln/request.ts b/src/extension/background-script/actions/ln/request.ts index 73ed55757d..ab6ffe4602 100644 --- a/src/extension/background-script/actions/ln/request.ts +++ b/src/extension/background-script/actions/ln/request.ts @@ -43,8 +43,9 @@ const request = async ( throw new Error("Could not find an allowance for this host"); } + const connectorName = connector.constructor.name.toLowerCase(); // prefix method with webln to prevent potential naming conflicts (e.g. with nostr calls that also use the permissions) - const weblnMethod = `${WEBLN_PREFIX}${methodInLowerCase}`; + const weblnMethod = `${WEBLN_PREFIX}${connectorName}/${methodInLowerCase}`; const permission = await db.permissions .where("host") @@ -64,6 +65,7 @@ const request = async ( ); return response; } else { + // throws an error if the user rejects const promptResponse = await utils.openPrompt<{ enabled: boolean; blocked: boolean; @@ -71,7 +73,7 @@ const request = async ( args: { requestPermission: { method: methodInLowerCase, - description: `${connector.constructor.name.toLowerCase()}.${methodInLowerCase}`, + description: `${connectorName}.${methodInLowerCase}`, }, }, origin, diff --git a/src/extension/background-script/actions/nostr/decryptOrPrompt.ts b/src/extension/background-script/actions/nostr/decryptOrPrompt.ts index 969ba23bde..4f520b17a6 100644 --- a/src/extension/background-script/actions/nostr/decryptOrPrompt.ts +++ b/src/extension/background-script/actions/nostr/decryptOrPrompt.ts @@ -3,7 +3,7 @@ import state from "~/extension/background-script/state"; import i18n from "~/i18n/i18nConfig"; import { MessageDecryptGet, PermissionMethodNostr } from "~/types"; -import { hasPermissionFor, addPermissionFor } from "./helpers"; +import { addPermissionFor, hasPermissionFor } from "./helpers"; const decryptOrPrompt = async (message: MessageDecryptGet) => { if (!("host" in message.origin)) { @@ -18,10 +18,10 @@ const decryptOrPrompt = async (message: MessageDecryptGet) => { ); if (hasPermission) { - const response = state - .getState() - .getNostr() - .decrypt(message.args.peer, message.args.ciphertext); + const response = (await state.getState().getNostr()).decrypt( + message.args.peer, + message.args.ciphertext + ); return { data: response }; } else { @@ -32,7 +32,7 @@ const decryptOrPrompt = async (message: MessageDecryptGet) => { ...message, action: "public/nostr/confirm", args: { - description: i18n.t("translation:nostr.permissions.decrypt"), + description: i18n.t("permissions:nostr.nip04decrypt"), }, }); @@ -44,10 +44,10 @@ const decryptOrPrompt = async (message: MessageDecryptGet) => { ); } if (promptResponse.data.confirm) { - const response = state - .getState() - .getNostr() - .decrypt(message.args.peer, message.args.ciphertext); + const response = (await state.getState().getNostr()).decrypt( + message.args.peer, + message.args.ciphertext + ); return { data: response }; } else { diff --git a/src/extension/background-script/actions/nostr/encryptOrPrompt.ts b/src/extension/background-script/actions/nostr/encryptOrPrompt.ts index c2c913db27..048e90030d 100644 --- a/src/extension/background-script/actions/nostr/encryptOrPrompt.ts +++ b/src/extension/background-script/actions/nostr/encryptOrPrompt.ts @@ -3,7 +3,7 @@ import state from "~/extension/background-script/state"; import i18n from "~/i18n/i18nConfig"; import { MessageEncryptGet, PermissionMethodNostr } from "~/types"; -import { hasPermissionFor, addPermissionFor } from "./helpers"; +import { addPermissionFor, hasPermissionFor } from "./helpers"; const encryptOrPrompt = async (message: MessageEncryptGet) => { if (!("host" in message.origin)) { @@ -18,10 +18,10 @@ const encryptOrPrompt = async (message: MessageEncryptGet) => { ); if (hasPermission) { - const response = state - .getState() - .getNostr() - .encrypt(message.args.peer, message.args.plaintext); + const response = (await state.getState().getNostr()).encrypt( + message.args.peer, + message.args.plaintext + ); return { data: response }; } else { @@ -32,7 +32,7 @@ const encryptOrPrompt = async (message: MessageEncryptGet) => { ...message, action: "public/nostr/confirm", args: { - description: i18n.t("translation:nostr.permissions.encrypt"), + description: i18n.t("permissions:nostr.nip04encrypt"), }, }); @@ -44,10 +44,10 @@ const encryptOrPrompt = async (message: MessageEncryptGet) => { ); } if (promptResponse.data.confirm) { - const response = state - .getState() - .getNostr() - .encrypt(message.args.peer, message.args.plaintext); + const response = (await state.getState().getNostr()).encrypt( + message.args.peer, + message.args.plaintext + ); return { data: response }; } else { diff --git a/src/extension/background-script/actions/nostr/generatePrivateKey.ts b/src/extension/background-script/actions/nostr/generatePrivateKey.ts index 753bbd8fb4..a7aac8ec25 100644 --- a/src/extension/background-script/actions/nostr/generatePrivateKey.ts +++ b/src/extension/background-script/actions/nostr/generatePrivateKey.ts @@ -1,10 +1,33 @@ import * as secp256k1 from "@noble/secp256k1"; import Hex from "crypto-js/enc-hex"; import sha512 from "crypto-js/sha512"; +import type { MessagePrivateKeyGenerate } from "~/types"; import state from "../../state"; -const generatePrivateKey = async () => { +const generatePrivateKey = async (message: MessagePrivateKeyGenerate) => { + const type = message?.args?.type; + + const privateKey = + type === "random" ? generateRandomKey() : await deriveKey(); + + if (privateKey) + return { + data: { + privateKey: secp256k1.utils.bytesToHex(privateKey), + }, + }; + else + return { + error: "Error generating private key.", + }; +}; + +const generateRandomKey = () => { + return secp256k1.utils.randomPrivateKey(); +}; + +const deriveKey = async () => { const connector = await state.getState().getConnector(); try { const response = await connector.signMessage({ @@ -23,12 +46,7 @@ const generatePrivateKey = async () => { // Use SHA-512 to provide enough key material for secp256k1 (> 40 bytes) const hash = sha512(keymaterial).toString(Hex); const privateKey = secp256k1.utils.hashToPrivateKey(hash); - - return { - data: { - privateKey: secp256k1.utils.bytesToHex(privateKey), - }, - }; + return privateKey; } catch (e) { console.error(e); } diff --git a/src/extension/background-script/actions/nostr/getPrivateKey.ts b/src/extension/background-script/actions/nostr/getPrivateKey.ts index 54b4f930f8..65596780de 100644 --- a/src/extension/background-script/actions/nostr/getPrivateKey.ts +++ b/src/extension/background-script/actions/nostr/getPrivateKey.ts @@ -1,9 +1,35 @@ +import { decryptData } from "~/common/lib/crypto"; +import type { MessagePrivateKeyGet } from "~/types"; + import state from "../../state"; -const getPrivateKey = async () => { - const privateKey = await state.getState().getNostr().getPrivateKey(); +const getPrivateKey = async (message: MessagePrivateKeyGet) => { + const id = message?.args?.id; + + if (!id) { + return { + data: (await state.getState().getNostr()).privateKey, + }; + } + + const accounts = state.getState().accounts; + if (Object.keys(accounts).includes(id)) { + const password = await state.getState().password(); + if (!password) { + return { + error: "Password is missing.", + }; + } + const account = accounts[id]; + if (!account.nostrPrivateKey) return { data: null }; + const privateKey = decryptData(account.nostrPrivateKey, password); + return { + data: privateKey, + }; + } + return { - data: privateKey, + error: "Account does not exist.", }; }; diff --git a/src/extension/background-script/actions/nostr/getPublicKeyOrPrompt.ts b/src/extension/background-script/actions/nostr/getPublicKeyOrPrompt.ts index 07dd116e6a..1bb2465349 100644 --- a/src/extension/background-script/actions/nostr/getPublicKeyOrPrompt.ts +++ b/src/extension/background-script/actions/nostr/getPublicKeyOrPrompt.ts @@ -3,7 +3,7 @@ import type { MessagePublicKeyGet } from "~/types"; import { PermissionMethodNostr } from "~/types"; import state from "../../state"; -import { hasPermissionFor, addPermissionFor } from "./helpers"; +import { addPermissionFor, hasPermissionFor } from "./helpers"; const getPublicKeyOrPrompt = async (message: MessagePublicKeyGet) => { if (!("host" in message.origin)) { @@ -18,7 +18,7 @@ const getPublicKeyOrPrompt = async (message: MessagePublicKeyGet) => { ); if (hasPermission) { - const publicKey = await state.getState().getNostr().getPublicKey(); + const publicKey = (await state.getState().getNostr()).getPublicKey(); return { data: publicKey }; } else { const promptResponse = await utils.openPrompt<{ @@ -39,7 +39,7 @@ const getPublicKeyOrPrompt = async (message: MessagePublicKeyGet) => { if (promptResponse.data.confirm) { // Normally `openPrompt` would throw already, but to make sure we got a confirm from the user we check this here - const publicKey = await state.getState().getNostr().getPublicKey(); + const publicKey = (await state.getState().getNostr()).getPublicKey(); return { data: publicKey }; } else { return { error: "User rejected" }; diff --git a/src/extension/background-script/actions/nostr/helpers.ts b/src/extension/background-script/actions/nostr/helpers.ts index 64a077f0e1..da322079db 100644 --- a/src/extension/background-script/actions/nostr/helpers.ts +++ b/src/extension/background-script/actions/nostr/helpers.ts @@ -45,13 +45,18 @@ export async function addPermissionFor(method: string, host: string) { return !!permissionIsAdded && (await db.saveToStorage()); } -export function validateEvent(event: Event) { - if (event.id !== getEventHash(event)) return false; +// from: https://github.com/nbd-wtf/nostr-tools/blob/160987472fd4922dd80c75648ca8939dd2d96cc0/event.ts#L61 +// to avoid the additional dependency +export function validateEvent(event: Event): boolean { if (typeof event.content !== "string") return false; if (typeof event.created_at !== "number") return false; + // ignore these checks because if the pubkey is not set we add it to the event. same for the ID. + // if (typeof event.pubkey !== "string") return false; + // if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false; if (!Array.isArray(event.tags)) return false; - for (const tag of event.tags) { + for (let i = 0; i < event.tags.length; i++) { + const tag = event.tags[i]; if (!Array.isArray(tag)) return false; for (let j = 0; j < tag.length; j++) { if (typeof tag[j] === "object") return false; @@ -61,12 +66,12 @@ export function validateEvent(event: Event) { return true; } -export async function signEvent(event: Event, key: string) { - const signedEvent = await secp256k1.schnorr.sign(getEventHash(event), key); - return secp256k1.utils.bytesToHex(signedEvent); -} +// from: https://github.com/nbd-wtf/nostr-tools/blob/160987472fd4922dd80c75648ca8939dd2d96cc0/event.ts#L42 +// to avoid the additional dependency +export function serializeEvent(evt: Event): string { + if (!validateEvent(evt)) + throw new Error("can't serialize event with wrong or missing properties"); -export function serializeEvent(evt: Event) { return JSON.stringify([ 0, evt.pubkey, @@ -77,6 +82,11 @@ export function serializeEvent(evt: Event) { ]); } +export async function signEvent(event: Event, key: string) { + const signedEvent = await secp256k1.schnorr.sign(getEventHash(event), key); + return secp256k1.utils.bytesToHex(signedEvent); +} + export function getEventHash(event: Event): string { return sha256(serializeEvent(event)).toString(Hex); } diff --git a/src/extension/background-script/actions/nostr/setPrivateKey.ts b/src/extension/background-script/actions/nostr/setPrivateKey.ts index c7fa543bc7..d22feb7b74 100644 --- a/src/extension/background-script/actions/nostr/setPrivateKey.ts +++ b/src/extension/background-script/actions/nostr/setPrivateKey.ts @@ -1,10 +1,37 @@ +import { encryptData } from "~/common/lib/crypto"; import type { MessagePrivateKeySet } from "~/types"; import state from "../../state"; const setPrivateKey = async (message: MessagePrivateKeySet) => { - await state.getState().getNostr().setPrivateKey(message.args.privateKey); - return {}; + const id = message.args?.id || state.getState().currentAccountId; + + const password = await state.getState().password(); + if (!password) { + return { + error: "Password is missing.", + }; + } + const privateKey = message.args.privateKey; + const accounts = state.getState().accounts; + + if (id && Object.keys(accounts).includes(id)) { + const account = accounts[id]; + account.nostrPrivateKey = privateKey + ? encryptData(privateKey, password) + : null; + accounts[id] = account; + state.setState({ accounts }); + await state.getState().saveToStorage(); + return { + data: { + accountId: id, + }, + }; + } + return { + error: "No account selected.", + }; }; export default setPrivateKey; diff --git a/src/extension/background-script/actions/nostr/signEventOrPrompt.ts b/src/extension/background-script/actions/nostr/signEventOrPrompt.ts index 64188f4193..50556d4ed2 100644 --- a/src/extension/background-script/actions/nostr/signEventOrPrompt.ts +++ b/src/extension/background-script/actions/nostr/signEventOrPrompt.ts @@ -1,10 +1,8 @@ import utils from "~/common/lib/utils"; -import { MessageSignEvent } from "~/types"; -import { PermissionMethodNostr } from "~/types"; +import { MessageSignEvent, PermissionMethodNostr } from "~/types"; import state from "../../state"; -import { hasPermissionFor, addPermissionFor } from "./helpers"; -import { validateEvent } from "./helpers"; +import { addPermissionFor, hasPermissionFor, validateEvent } from "./helpers"; const signEventOrPrompt = async (message: MessageSignEvent) => { if (!("host" in message.origin)) { @@ -12,7 +10,14 @@ const signEventOrPrompt = async (message: MessageSignEvent) => { return; } - if (!validateEvent(message.args.event)) { + const nostr = await state.getState().getNostr(); + + // check event and add an ID and pubkey if not present + const event = message.args.event; + if (!event.pubkey) event.pubkey = nostr.getPublicKey(); + if (!event.id) event.id = nostr.getEventHash(event); + + if (!validateEvent(event)) { console.error("Invalid event"); return { error: "Invalid event.", @@ -42,10 +47,7 @@ const signEventOrPrompt = async (message: MessageSignEvent) => { } } - const signedEvent = await state - .getState() - .getNostr() - .signEvent(message.args.event); + const signedEvent = await nostr.signEvent(event); return { data: signedEvent }; } catch (e) { diff --git a/src/extension/background-script/actions/permissions/list.ts b/src/extension/background-script/actions/permissions/list.ts index 4d8336b347..77851ae6f2 100644 --- a/src/extension/background-script/actions/permissions/list.ts +++ b/src/extension/background-script/actions/permissions/list.ts @@ -4,8 +4,7 @@ import type { MessagePermissionsList, Permission } from "~/types"; const listByAllowance = async (message: MessagePermissionsList) => { const { id } = message.args; const dbPermissions = await db.permissions - .toCollection() - .filter((permission) => permission.allowanceId === id) + .where({ allowanceId: id }) .toArray(); const permissions: Permission[] = []; diff --git a/src/extension/background-script/actions/settings/changePassword.ts b/src/extension/background-script/actions/settings/changePassword.ts index d82139c261..85ed4e33ec 100644 --- a/src/extension/background-script/actions/settings/changePassword.ts +++ b/src/extension/background-script/actions/settings/changePassword.ts @@ -9,7 +9,6 @@ const changePassword = async (message: Message) => { if (!password) return { error: "Password is missing" }; const newPassword = message.args.password as string; const tmpAccounts = { ...accounts }; - const nostPrivateKey = await state.getState().getNostr().getPrivateKey(); for (const accountId in tmpAccounts) { const accountConfig = decryptData( @@ -17,12 +16,17 @@ const changePassword = async (message: Message) => { password ); tmpAccounts[accountId].config = encryptData(accountConfig, newPassword); + const accountNostrKey = decryptData( + accounts[accountId].nostrPrivateKey as string, + password + ); + tmpAccounts[accountId].nostrPrivateKey = encryptData( + accountNostrKey, + newPassword + ); } - await state.getState().password(newPassword); state.setState({ accounts: tmpAccounts }); - await state.getState().getNostr().setPrivateKey(nostPrivateKey); - // make sure we immediately persist the updated accounts await state.getState().saveToStorage(); diff --git a/src/extension/background-script/connectors/index.ts b/src/extension/background-script/connectors/index.ts index 6c86aa52db..4661490cc4 100644 --- a/src/extension/background-script/connectors/index.ts +++ b/src/extension/background-script/connectors/index.ts @@ -4,6 +4,7 @@ import Eclair from "./eclair"; import Galoy from "./galoy"; import Kollider from "./kollider"; import LnBits from "./lnbits"; +import Lnc from "./lnc"; import Lnd from "./lnd"; import LndHub from "./lndhub"; import NativeCitadel from "./nativecitadel"; @@ -25,6 +26,7 @@ const connectors = { lndhub: LndHub, nativelndhub: NativeLndHub, lnbits: LnBits, + lnc: Lnc, nativelnbits: NativeLnBits, galoy: Galoy, eclair: Eclair, diff --git a/src/extension/background-script/connectors/lnc.ts b/src/extension/background-script/connectors/lnc.ts new file mode 100644 index 0000000000..5d9a5ed02c --- /dev/null +++ b/src/extension/background-script/connectors/lnc.ts @@ -0,0 +1,385 @@ +import Base64 from "crypto-js/enc-base64"; +import Hex from "crypto-js/enc-hex"; +import UTF8 from "crypto-js/enc-utf8"; +import WordArray from "crypto-js/lib-typedarrays"; +import SHA256 from "crypto-js/sha256"; +import LNC, { CredentialStore } from "lnc-web"; +import snakeCase from "lodash.snakecase"; +import { encryptData } from "~/common/lib/crypto"; +import utils from "~/common/lib/utils"; + +import state from "../state"; +import Connector, { + CheckPaymentArgs, + CheckPaymentResponse, + ConnectorInvoice, + ConnectPeerResponse, + GetBalanceResponse, + GetInfoResponse, + GetInvoicesResponse, + KeysendArgs, + MakeInvoiceArgs, + MakeInvoiceResponse, + SendPaymentArgs, + SendPaymentResponse, + SignMessageArgs, + SignMessageResponse, +} from "./connector.interface"; + +interface Config { + pairingPhrase: string; + localKey?: string; + remoteKey?: string; + serverHost?: string; +} + +const methods: Record = { + addinvoice: "lnd.lightning.AddInvoice", + channelbalance: "lnd.lightning.ChannelBalance", + connectpeer: "lnd.lightning.ConnectPeer", + decodepayreq: "lnd.lightning.DecodePayReq", + disconnectpeer: "lnd.lightning.DisconnectPeer", + estimatefee: "lnd.lightning.EstimateFee", + getchaninfo: "lnd.lightning.GetChanInfo", + getinfo: "lnd.lightning.GetInfo", + getnetworkinfo: "lnd.lightning.GetNetworkInfo", + getnodeinfo: "lnd.lightning.GetNodeInfo", + gettransactions: "lnd.lightning.GetTransactions", + listchannels: "lnd.lightning.ListChannels", + listinvoices: "lnd.lightning.ListInvoices", + listpayments: "lnd.lightning.ListPayments", + listpeers: "lnd.lightning.ListPeers", + lookupinvoice: "lnd.lightning.LookupInvoice", + openchannel: "lnd.lightning.OpenChannelSync", + queryroutes: "lnd.lightning.QueryRoutes", + routermc: "lnd.router.QueryMissionControl", + sendtoroute: "lnd.lightning.SendToRouteSync", + verifymessage: "lnd.lightning.VerifyMessage", + walletbalance: "lnd.lightning.WalletBalance", +}; + +const DEFAULT_SERVER_HOST = "mailbox.terminal.lightning.today:443"; + +const snakeCaseObjectDeep = (value: FixMe): FixMe => { + if (Array.isArray(value)) { + return value.map(snakeCaseObjectDeep); + } + + if (value && typeof value === "object" && value.constructor === Object) { + const obj = {} as FixMe; + const keys = Object.keys(value); + const len = keys.length; + + for (let i = 0; i < len; i += 1) { + obj[snakeCase(keys[i])] = snakeCaseObjectDeep(value[keys[i]]); + } + + return obj; + } + + return value; +}; + +class LncCredentialStore implements CredentialStore { + config: Config; + + constructor(config: Config) { + this.config = config; + } + + get password() { + return ""; + } + + set localKey(value: string) { + this.config.localKey = value; + this._save(); + } + + get localKey() { + return this.config.localKey || ""; + } + + set remoteKey(value: string) { + this.config.remoteKey = value; + this._save(); + } + + get remoteKey() { + return this.config.remoteKey || ""; + } + + get pairingPhrase() { + return this.config.pairingPhrase; + } + + set serverHost(value: string) { + this.config.serverHost = value; + this._save(); + } + + get serverHost() { + return this.config.serverHost || DEFAULT_SERVER_HOST; + } + + get isPaired() { + return true; + } + + async clear() { + this.config.localKey = ""; + this.config.remoteKey = ""; + this._save(); + } + + private async _save() { + const accounts = state.getState().accounts; + const password = await state.getState().password(); + const currentAccountId = state.getState().currentAccountId as string; + accounts[currentAccountId].config = encryptData( + this.config, + password as string + ); + state.setState({ accounts }); + await state.getState().saveToStorage(); + return true; + } +} + +class Lnc implements Connector { + config: Config; + lnc: FixMe; + + constructor(config: Config) { + this.config = config; + this.lnc = new LNC({ + credentialStore: new LncCredentialStore(config), + }); + } + + async init(): Promise { + console.info("init LNC"); + await this.lnc.connect(); + } + + async unload() { + console.info("LNC disconnect"); + await this.lnc.disconnect(); + return new Promise((resolve) => { + // give lnc a bit time to disconnect. + // not sure what there happens, best would be to have disconnect() return a promise + setTimeout(() => { + // TODO: investigate garbage collection + delete this.lnc; + resolve(); + }, 1000); + }); + } + + get supportedMethods() { + return Object.keys(methods); + } + + async requestMethod( + method: string, + args: Record + ): Promise<{ data: unknown }> { + const lncCall = methods[method]; + if (!lncCall) { + throw new Error(`${method} is not supported`); + } + + const func = lncCall.split(".").reduce((obj: FixMe, prop: FixMe) => { + return obj[prop]; + }, this.lnc); + return func(args).then((data: FixMe) => { + return { data: snakeCaseObjectDeep(data) }; + }); + } + + getInfo(): Promise { + if (!this.lnc.isConnected) { + return Promise.reject(new Error("Account is still loading")); + } + return this.lnc.lnd.lightning.GetInfo().then((data: FixMe) => { + return { + data: { + alias: data.alias, + pubkey: data.identityPubkey, + color: data.color, + }, + }; + }); + } + + getBalance(): Promise { + if (!this.lnc.isConnected) { + return Promise.reject(new Error("Account is still loading")); + } + return this.lnc.lnd.lightning.ChannelBalance().then((data: FixMe) => { + return { + data: { + balance: data.balance, + }, + }; + }); + } + + async getInvoices(): Promise { + if (!this.lnc.isConnected) { + throw new Error("Account is still loading"); + } + const data = await this.lnc.lnd.lightning.ListInvoices({ reversed: true }); + + const invoices: ConnectorInvoice[] = data.invoices + .map((invoice: FixMe, index: number): ConnectorInvoice => { + const custom_records = + invoice.htlcs[0] && invoice.htlcs[0].custom_records; + + return { + custom_records, + id: `${invoice.payment_request}-${index}`, + memo: invoice.memo, + preimage: invoice.r_preimage, + settled: invoice.settled, + settleDate: parseInt(invoice.settle_date) * 1000, + totalAmount: invoice.value, + type: "received", + }; + }) + .reverse(); + + return { + data: { + invoices, + }, + }; + } + + // not yet implemented + async connectPeer(): Promise { + console.error( + `${this.constructor.name} does not implement the getInvoices call` + ); + throw new Error("Not yet supported with the currently used account."); + } + + async checkPayment(args: CheckPaymentArgs): Promise { + if (!this.lnc.isConnected) { + throw new Error("Account is still loading"); + } + return this.lnc.lnd.lightning + .LookupInvoice({ r_hash_str: args.paymentHash }) + .then((data: FixMe) => { + return { + data: { + paid: data.settled, + }, + }; + }); + } + + sendPayment(args: SendPaymentArgs): Promise { + if (!this.lnc.isConnected) { + return Promise.reject(new Error("Account is still loading")); + } + return this.lnc.lnd.lightning + .SendPaymentSync({ + payment_request: args.paymentRequest, + }) + .then((data: FixMe) => { + if (data.paymentError) { + throw new Error(data.paymentError); + } + return { + data: { + preimage: utils.base64ToHex(data.paymentPreimage), + paymentHash: utils.base64ToHex(data.paymentHash), + route: { + total_amt: data.paymentRoute.totalAmt, + total_fees: data.paymentRoute.totalFees, + }, + }, + }; + }); + } + async keysend(args: KeysendArgs): Promise { + if (!this.lnc.isConnected) { + throw new Error("Account is still loading"); + } + //See: https://gist.github.com/dellagustin/c3793308b75b6b0faf134e64db7dc915 + const dest_pubkey_hex = args.pubkey; + const dest_pubkey_base64 = Hex.parse(dest_pubkey_hex).toString(Base64); + + const preimage = WordArray.random(32); + const preimage_base64 = preimage.toString(Base64); + const hash = SHA256(preimage).toString(Base64); + + //base64 encode the record values + const records_base64: Record = {}; + for (const key in args.customRecords) { + records_base64[parseInt(key)] = UTF8.parse( + args.customRecords[key] + ).toString(Base64); + } + //mandatory record for keysend + records_base64[5482373484] = preimage_base64; + + return this.lnc.lnd.lightning + .SendPaymentSync({ + dest: dest_pubkey_base64, + amt: args.amount, + payment_hash: hash, + dest_custom_records: records_base64, + }) + .then((data: FixMe) => { + if (data.paymentError) { + throw new Error(data.paymentError); + } + return { + data: { + preimage: utils.base64ToHex(data.paymentPreimage), + paymentHash: utils.base64ToHex(data.paymentHash), + route: { + total_amt: data.paymentRoute.totalAmt, + total_fees: data.paymentRoute.totalFees, + }, + }, + }; + }); + } + + signMessage(args: SignMessageArgs): Promise { + if (!this.lnc.isConnected) { + return Promise.reject(new Error("Account is still loading")); + } + return this.lnc.lnd.lightning + .SignMessage({ msg: Base64.stringify(UTF8.parse(args.message)) }) + .then((data: FixMe) => { + return { + data: { + message: args.message, + signature: data.signature, + }, + }; + }); + } + + makeInvoice(args: MakeInvoiceArgs): Promise { + if (!this.lnc.isConnected) { + return Promise.reject(new Error("Account is still loading")); + } + return this.lnc.lnd.lightning + .AddInvoice({ memo: args.memo, value: args.amount }) + .then((data: FixMe) => { + return { + data: { + paymentRequest: data.paymentRequest, + rHash: utils.base64ToHex(data.rHash), + }, + }; + }); + } +} + +export default Lnc; diff --git a/src/extension/background-script/db.ts b/src/extension/background-script/db.ts index 1f35b16120..e64678a2cf 100644 --- a/src/extension/background-script/db.ts +++ b/src/extension/background-script/db.ts @@ -1,13 +1,29 @@ import Dexie from "dexie"; +import { IDBKeyRange, indexedDB } from "fake-indexeddb"; import browser from "webextension-polyfill"; import type { DbAllowance, - DbPayment, DbBlocklist, + DbPayment, DbPermission, } from "~/types"; -class DB extends Dexie { +export function isIndexedDbAvailable() { + if ("indexedDB" in globalThis) { + return new Promise((resolve) => { + const req = globalThis.indexedDB.open("LBE-AVAILABILITY-CHECK", 1); + req.onsuccess = () => { + resolve(true); + }; + req.onerror = () => { + resolve(false); + }; + }); + } + return Promise.resolve(false); +} + +export class DB extends Dexie { allowances: Dexie.Table; payments: Dexie.Table; blocklist: Dexie.Table; @@ -34,6 +50,19 @@ class DB extends Dexie { this.permissions = this.table("permissions"); } + async openWithInMemoryDB() { + console.info("Opening DB using fake indexedDB"); + // @ts-expect-error _options is inherited from Dexie + this._options.indexedDB = indexedDB; + // @ts-expect-error _options is inherited from Dexie + this._options.IDBKeyRange = IDBKeyRange; + // @ts-expect-error _options is inherited from Dexie + this._deps.indexedDB = indexedDB; + // @ts-expect-error _options is inherited from Dexie + this._deps.IDBKeyRange = IDBKeyRange; + return this.open(); + } + async saveToStorage() { const allowanceArray = await this.allowances.toArray(); const paymentsArray = await this.payments.toArray(); @@ -135,6 +164,6 @@ class DB extends Dexie { } } -const db = new DB(); +export const db = new DB(); export default db; diff --git a/src/extension/background-script/events/__test__/notifications.test.ts b/src/extension/background-script/events/__test__/notifications.test.ts index 63f1915675..549e43a858 100644 --- a/src/extension/background-script/events/__test__/notifications.test.ts +++ b/src/extension/background-script/events/__test__/notifications.test.ts @@ -1,8 +1,8 @@ import { CURRENCIES } from "~/common/constants"; import state from "~/extension/background-script/state"; import type { - PaymentNotificationData, AuthNotificationData, + PaymentNotificationData, SettingsStorage, } from "~/types"; @@ -31,6 +31,7 @@ const settings: SettingsStorage = { userName: "", websiteEnhancements: true, nostrEnabled: false, + closedTips: [], }; const mockState = { diff --git a/src/extension/background-script/index.ts b/src/extension/background-script/index.ts index dabceac764..c365cbbc55 100644 --- a/src/extension/background-script/index.ts +++ b/src/extension/background-script/index.ts @@ -3,7 +3,7 @@ import utils from "~/common/lib/utils"; import { ExtensionIcon, setIcon } from "./actions/setup/setIcon"; import connectors from "./connectors"; -import db from "./db"; +import { isIndexedDbAvailable, db } from "./db"; import * as events from "./events"; import migrate from "./migrations"; import { router } from "./router"; @@ -135,7 +135,14 @@ async function init() { await state.getState().init(); console.info("State loaded"); - await db.open(); + const dbAvailable = await isIndexedDbAvailable(); + if (dbAvailable) { + console.info("Using indexedDB"); + await db.open(); + } else { + console.info("Using in memory DB"); + await db.openWithInMemoryDB(); + } console.info("DB opened"); events.subscribe(); @@ -160,7 +167,7 @@ browser.runtime.onInstalled.addListener(handleInstalled); console.info("Welcome to Alby"); init().then(() => { - if (isFirstInstalled) { + if (isFirstInstalled && !state.getState().getAccount()) { utils.openUrl("welcome.html"); } if (isRecentlyUpdated) { diff --git a/src/extension/background-script/migrations/index.ts b/src/extension/background-script/migrations/index.ts index ea52c51279..81443b33d6 100644 --- a/src/extension/background-script/migrations/index.ts +++ b/src/extension/background-script/migrations/index.ts @@ -26,6 +26,12 @@ const setMigrated = (name: Migration): Promise => { }; const migrations = { + migratedeleteLegacyWeblnPermissions: async () => { + await db.permissions + .where("method") + .startsWithIgnoreCase("webln/") + .delete(); + }, migrateisUsingLegacyLnurlAuthKeySetting: async () => { const { settings } = state.getState(); const allowances = await db.allowances @@ -46,6 +52,19 @@ const migrations = { // state is saved with the setMigrated call } }, + migrateisUsingGlobalNostrKey: async () => { + const { nostrPrivateKey, accounts } = state.getState(); + + if (nostrPrivateKey) { + Object.values(accounts).map((account) => { + if (!account.nostrPrivateKey) account.nostrPrivateKey = nostrPrivateKey; + }); + + state.setState({ + accounts, + }); + } + }, }; const migrate = async () => { @@ -58,6 +77,16 @@ const migrate = async () => { await migrations["migrateisUsingLegacyLnurlAuthKeySetting"](); await setMigrated("migrateisUsingLegacyLnurlAuthKeySetting"); } + if (shouldMigrate("migrateisUsingGlobalNostrKey")) { + console.info("Running migration for: migrateisUsingGlobalNostrKey"); + await migrations["migrateisUsingGlobalNostrKey"](); + await setMigrated("migrateisUsingGlobalNostrKey"); + } + if (shouldMigrate("migratedeleteLegacyWeblnPermissions")) { + console.info("Running migration for: migratedeleteLegacyWeblnPermissions"); + await migrations["migratedeleteLegacyWeblnPermissions"](); + await setMigrated("migratedeleteLegacyWeblnPermissions"); + } }; export default migrate; diff --git a/src/extension/background-script/nostr/__test__/nostr.test.ts b/src/extension/background-script/nostr/__test__/nostr.test.ts index 06ffdcd1c6..33ec724609 100644 --- a/src/extension/background-script/nostr/__test__/nostr.test.ts +++ b/src/extension/background-script/nostr/__test__/nostr.test.ts @@ -12,18 +12,41 @@ const bob = { publicKey: "519f5ae2cd7d4b970c4edadb2efc947c9b803838de918d1c5bfd4b9c1a143b72", }; +const carol = { + privateKey: + "43a2d71f40dde6fb7588e7962a54b8bbd8dd4bd617a9a5c58b7bf0d8f3482f11", + publicKey: "a8c7d70a7d2e2826ce519a0a490fb953464c9d130235c321282983cd73be333f", +}; + describe("nostr", () => { test("encrypt & decrypt", async () => { - const nostr = new Nostr(); - nostr.getPrivateKey = jest.fn().mockReturnValue(alice.privateKey); + const aliceNostr = new Nostr(alice.privateKey); const message = "Secret message that is sent from Alice to Bob"; - const encrypted = await nostr.encrypt(bob.publicKey, message); + const encrypted = aliceNostr.encrypt(bob.publicKey, message); - nostr.getPrivateKey = jest.fn().mockReturnValue(bob.privateKey); + const bobNostr = new Nostr(bob.privateKey); - const decrypted = await nostr.decrypt(alice.publicKey, encrypted); + const decrypted = bobNostr.decrypt(alice.publicKey, encrypted); expect(decrypted).toMatch(message); }); + + test("Carol can't decrypt Alice's message for Bob", async () => { + const aliceNostr = new Nostr(alice.privateKey); + + const message = "Secret message that is sent from Alice to Bob"; + const encrypted = aliceNostr.encrypt(bob.publicKey, message); + + const carolNostr = new Nostr(carol.privateKey); + + let decrypted; + try { + decrypted = carolNostr.decrypt(alice.publicKey, encrypted); + } catch (e) { + decrypted = "error decrypting message"; + } + + expect(decrypted).not.toMatch(message); + }); }); diff --git a/src/extension/background-script/nostr/index.ts b/src/extension/background-script/nostr/index.ts index 4581258a30..bfcdcb286c 100644 --- a/src/extension/background-script/nostr/index.ts +++ b/src/extension/background-script/nostr/index.ts @@ -5,56 +5,33 @@ import { AES } from "crypto-js"; import Base64 from "crypto-js/enc-base64"; import Hex from "crypto-js/enc-hex"; import Utf8 from "crypto-js/enc-utf8"; -import { decryptData, encryptData } from "~/common/lib/crypto"; import { Event } from "~/extension/ln/nostr/types"; -import { signEvent } from "../actions/nostr/helpers"; -import state from "../state"; +import { getEventHash, signEvent } from "../actions/nostr/helpers"; class Nostr { - async getPrivateKey() { - const password = await state.getState().password(); - if (!password) throw new Error("Pasword is missing"); - const encryptedKey = state.getState().nostrPrivateKey as string; - if (encryptedKey) { - try { - return decryptData(encryptedKey, password); - } catch (e) { - console.error("Could not decrypt the Nostr key"); - console.error(e); - } - } + privateKey: string; - return null; + constructor(privateKey: string) { + this.privateKey = privateKey; } - async getPublicKey() { + getPublicKey() { const publicKey = secp256k1.schnorr.getPublicKey( - secp256k1.utils.hexToBytes(await this.getPrivateKey()) + secp256k1.utils.hexToBytes(this.privateKey) ); const publicKeyHex = secp256k1.utils.bytesToHex(publicKey); return publicKeyHex; } - async setPrivateKey(privateKey: string) { - const password = await state.getState().password(); - if (!password) throw new Error("Pasword is missing"); - - state.setState({ nostrPrivateKey: encryptData(privateKey, password) }); - await state.getState().saveToStorage(); - } - async signEvent(event: Event): Promise { - const signature = await signEvent(event, await this.getPrivateKey()); + const signature = await signEvent(event, this.privateKey); event.sig = signature; return event; } - async encrypt(pubkey: string, text: string) { - const key = secp256k1.getSharedSecret( - await this.getPrivateKey(), - "02" + pubkey - ); + encrypt(pubkey: string, text: string) { + const key = secp256k1.getSharedSecret(this.privateKey, "02" + pubkey); const normalizedKey = Buffer.from(key.slice(1, 33)); const hexNormalizedKey = secp256k1.utils.bytesToHex(normalizedKey); const hexKey = Hex.parse(hexNormalizedKey); @@ -70,10 +47,7 @@ class Nostr { async decrypt(pubkey: string, ciphertext: string) { const [cip, iv] = ciphertext.split("?iv="); - const key = secp256k1.getSharedSecret( - await this.getPrivateKey(), - "02" + pubkey - ); + const key = secp256k1.getSharedSecret(this.privateKey, "02" + pubkey); const normalizedKey = Buffer.from(key.slice(1, 33)); const hexNormalizedKey = secp256k1.utils.bytesToHex(normalizedKey); const hexKey = Hex.parse(hexNormalizedKey); @@ -84,6 +58,10 @@ class Nostr { return Utf8.stringify(decrypted); } + + getEventHash(event: Event) { + return getEventHash(event); + } } export default Nostr; diff --git a/src/extension/background-script/router.ts b/src/extension/background-script/router.ts index 5dba8ab46f..7cdf113039 100644 --- a/src/extension/background-script/router.ts +++ b/src/extension/background-script/router.ts @@ -38,6 +38,7 @@ const routes = { addAccount: accounts.add, editAccount: accounts.edit, getAccounts: accounts.all, + getAccount: accounts.get, removeAccount: accounts.remove, selectAccount: accounts.select, setPassword: setup.setPassword, diff --git a/src/extension/background-script/state.ts b/src/extension/background-script/state.ts index 204a1db32d..64bcaee5a3 100644 --- a/src/extension/background-script/state.ts +++ b/src/extension/background-script/state.ts @@ -25,7 +25,7 @@ interface State { password: (password?: string | null) => Promise; getAccount: () => Account | null; getConnector: () => Promise; - getNostr: () => Nostr; + getNostr: () => Promise; init: () => Promise; isUnlocked: () => Promise; lock: () => Promise; @@ -56,6 +56,7 @@ export const DEFAULT_SETTINGS: SettingsStorage = { exchange: "alby", debug: false, nostrEnabled: false, + closedTips: [], }; // these keys get synced from the state to the browser storage @@ -126,12 +127,18 @@ const state = createState((set, get) => ({ return connector; }, - getNostr: () => { + getNostr: async () => { if (get().nostr) { return get().nostr as Nostr; } + const currentAccountId = get().currentAccountId as string; + const account = get().accounts[currentAccountId]; + + const password = await get().password(); + if (!password) throw new Error("Password is not set"); + const privateKey = decryptData(account.nostrPrivateKey as string, password); - const nostr = new Nostr(); + const nostr = new Nostr(privateKey); set({ nostr: nostr }); return nostr; @@ -147,7 +154,7 @@ const state = createState((set, get) => ({ if (connector) { await connector.unload(); } - set({ connector: null, account: null }); + set({ connector: null, account: null, nostr: null }); }, isUnlocked: async () => { const password = await await get().password(); diff --git a/src/extension/content-script/onstart.ts b/src/extension/content-script/onstart.ts index b0154ae2fa..70e0a163cb 100644 --- a/src/extension/content-script/onstart.ts +++ b/src/extension/content-script/onstart.ts @@ -14,7 +14,7 @@ async function onstart() { injectScript(browser.runtime.getURL("js/inpageScriptWebLN.bundle.js")); // window.nostr - const nostrEnabled = (await api.getSettings()).nostrEnabled; + const nostrEnabled = (await api.getAccount()).nostrEnabled; if (nostrEnabled) { injectScript(browser.runtime.getURL("js/inpageScriptNostr.bundle.js")); } diff --git a/src/i18n/locales/cs/translation.json b/src/i18n/locales/cs/translation.json index cdfccac218..96f0d14b1e 100644 --- a/src/i18n/locales/cs/translation.json +++ b/src/i18n/locales/cs/translation.json @@ -7,27 +7,6 @@ "connect": "", "done": "" }, - "intro": { - "send": { - "title": "", - "description": "" - }, - "paywall": { - "title": "", - "description": "" - }, - "privacy": { - "title": "", - "description": "" - }, - "foss": { - "title": "", - "description": "" - }, - "actions": { - "get_started": "" - } - }, "set_password": { "title": "", "description": "", @@ -45,8 +24,6 @@ }, "test_connection": { "ready": "", - "tutorial": "", - "try_tutorial": "", "initializing": "", "connection_error": "", "review_connection_details": "", @@ -307,24 +284,8 @@ "actions": { "add_account": "" }, - "edit": { - "title": "", - "name": { - "label": "" - }, - "screen_reader": "" - }, "export": { - "title": "", - "screen_reader": "", - "waiting": "", - "your_ln_address": "", - "tip_mobile": "", - "export_uri": "", - "scan_qr": "" - }, - "remove": { - "confirm": "" + "tip_mobile": "" } }, "enable": { @@ -441,10 +402,7 @@ "hint": "", "private_key": { "title": "", - "subtitle": "", - "generate": "", - "warning": "", - "success": "" + "subtitle": "" } } }, @@ -577,7 +535,6 @@ "nostr": { "title": "", "allow": "", - "read_public_key": "", "block_and_ignore": "" } }, diff --git a/src/i18n/locales/de/translation.json b/src/i18n/locales/de/translation.json index d092f0eb45..0747a88fdf 100644 --- a/src/i18n/locales/de/translation.json +++ b/src/i18n/locales/de/translation.json @@ -4,58 +4,36 @@ "nav": { "welcome": "Willkommen", "password": "Dein Passwort", - "connect": "Ihr Lightning Konto", + "connect": "Dein Lightning Konto", "done": "Fertig" }, - "intro": { - "send": { - "title": "Senden mit einem Klick", - "description": "Alle Lightning Transaktionen direkt in Deinem Browser ausführen. Keine umständlichen Testenkombinationen oder QR-Code scannen mehr nötig." - }, - "paywall": { - "title": "Keine lästigen Paywalls mehr", - "description": "Definieren Sie individuelle Budgets für Websites, um nahtlose Zahlungsströme zu ermöglichen. Keine lästigen Paywalls mehr." - }, - "privacy": { - "title": "Datenschutz an erster Stelle", - "description": "Verwende Lightning, um dich zu authentifizieren und deine Privatsphäre zu kontrollieren." - }, - "foss": { - "title": "Kostenlos und Open Source", - "description": "Vollständig offener Code, der von jedermann überprüft werden kann. Keine Statistiken oder Tracker. Du hast die Kontrolle." - }, - "actions": { - "get_started": "Beginne" - } - }, "set_password": { "title": "Passwort zum Entsperren festlegen", - "description": "Die Daten deines Lightning-Kontos sind mit einem Freischalt-Passwort sicher verschlüsselt. Vergesse das Passwort nicht! Du benötigst es, um die Alby-Erweiterung zu entsperren (in diesem Browser)", + "description": "Die Daten Ihres Lightning-Kontos sind mit einem Freischalt Passwort sicher verschlüsselt. Vergesse dieses Passwort nicht! Du benötigst es, um die Alby Erweiterung zu entsperren (in diesem Browser)", "choose_password": { - "label": "Wähle ein Passwort zum Entsperren:" + "label": "Wähle ein Password zum Entsperren:" }, "errors": { "mismatched_password": "Die Passwörter stimmen nicht überein.", - "enter_password": "Bitte gib ein Passwort ein.", + "enter_password": "Bitte gebe ein Passwort ein.", "confirm_password": "Bitte bestätige dein Passwort." }, "confirm_password": { - "label": "Wiederhole deine Passworteingabe:" + "label": "Lass uns es bestätigen, dass du es richtig eingegeben hast:" } }, "test_connection": { - "try_tutorial": "Probiere es jetzt aus", "review_connection_details": "Bitte überprüfe deine Verbindungsdaten.", "actions": { "delete_edit_account": "Ungültiges Konto löschen und erneut bearbeiten" }, - "ready": "Super, du hast es geschafft, es kann losgehen!", - "tutorial": "Du hast jetzt deine Wallet verbunden, möchtest du ein Tutorial starten?", - "initializing": "Dein Account wird initialisiert. Bitte warte, das kann ein wenig dauern.", + "ready": "Großartig, du bist startklar!", + "initializing": "Ihr Konto wird initialisiert. Bitte warten Sie, dies kann eine Minute dauern...", "connection_error": "Verbindungsfehler", - "connection_taking_long": "Das Verbinden dauert länger als erwartet...sind deine Angaben richtig? Ist dein Node erreichbar?", - "contact_support": "Wenn du Hilfe brauchst, kontaktiere bitte support@getalby.com" - } + "connection_taking_long": "Der Verbindungsversuch dauert länger als erwartet... Sind deine Angaben korrekt? Ist dein Knoten erreichbar?", + "contact_support": "Wenn du Hilfe benötigst, wende dich bitte an support@getalby.com" + }, + "title": "Willkommen bei Alby" }, "choose_connector": { "lnd": { @@ -66,20 +44,20 @@ }, "url": { "label": "REST-API-Host und -Port", - "placeholder": "https://Ihr-Knoten-URL:8080" + "placeholder": "https://Dein-Knoten-URL:8080" }, "macaroon": { "label": "Makrone (HEX-Format)" }, "page": { - "description": "Du benötigst deinen URL-Knoten und eine Macaroon mit Lese- und Sendeberechtigungen (z.B. admin.macaroon)", + "description": "Du benötigst deinen URL-Knoten und eine Macaroon mit Lese- und Sendeberechtigung (z.B. admin.macaroon)", "title": "Verbinde dich zu deinem LND Node" }, "title": "LND" }, "lndhub_bluewallet": { "uri": { - "label": "BlueWallet-Export-URI" + "label": "BlueWallet Export URI" }, "errors": { "invalid_uri": "Ungültige BlueWallet-URI", @@ -87,7 +65,7 @@ }, "title": "BlueWallet", "page": { - "title": "Verbinde dich mit Bluewallet", + "title": "Verbinde dich mit BlueWallet", "description": "Wähle in der BlueWallet die Wallet, die du verbinden willst, öffne sie, klicke auf \"...\", klicke auf Export/Backup, um den QR Code anzuzeigen. Scanne ihn dann mit deiner Webcam." } }, @@ -101,22 +79,22 @@ "connection_failed": "Verbindung fehlgeschlagen. Ist dein LNDHub-URI korrekt?" }, "page": { - "title": "Verbinden dich mit LNDHub", + "title": "Verbinde dich mit LNDHub", "description": "Gebe hier deine LNDHub-Zugangsdaten-URI ein oder scanne den QR-Code mit deiner Webcam." } }, "lnbits": { "admin_key": { - "label": "LNbits-Admin-Schlüssel", - "placeholder": "Ihr 32-stelliger Admin-Schlüssel" + "label": "LNbits Admin Schlüssel", + "placeholder": "Dein 32-stelliger Admin-Schlüssel" }, "title": "LNbits", "page": { "title": "Verbinde dich mit <0>LNbits", - "instructions": "Wähle in LNbits die Wallet, die du verbinden willst, öffne sie, klicke auf API Info und kopiere den Admin Key. Füge ihn unten ein:" + "instructions": "Wähle in LNbits die Wallet, die du verbinden möchtest, öffne sie, klicke auf API Info und kopiere den Admin Key. Füge ihn unten ein:" }, "url": { - "label": "LNbits-URL" + "label": "LNbits URL" }, "errors": { "connection_failed": "Verbindung fehlgeschlagen. Hast du die richtige URL und den richtigen Admin-Schlüssel?" @@ -124,86 +102,86 @@ }, "mynode": { "page": { - "instructions": "Klicke auf deiner myNode Übersicht auf den Button <0>Wallet für deinen <0>Lightning Service. Klicke jetzt auf den Button <0>Pair Wallet unterhalb dem Tab <0>Status . Gib dein Passwort ein, wenn es verlang wird. <1/> Wähle das Dropdown Menü und entscheide dich für eine Pairingoption. Abhängig von deinem Setup kannst du entweder für lokale Verbindung wählen <0>Lightning (REST + Local IP) oder für TOR die Auswahl <0>Lightning (REST + Tor).", - "title": "Verbinde dich mit <0>myNode" + "instructions": "Klicke auf deiner myNode-Startseite auf die Schaltfläche <0>Wallet für Ihren <0>Llightning-Dienst.<1/> Klicke nun auf die Schaltfläche <0>Wallet koppeln unter der <0 Registerkarte >Status. Gebe dein Passwort ein, wenn du dazu aufgefordert wirst.<1/> Wählen dann das Dropdown-Menü und wähle eine Kopplungsoption. Abhängig von deiner Einrichtung könnte entweder die Verbindung <0>Llightning (REST + Local IP) oder die Verbindung <0>Llightning (REST + Tor) verwenden.", + "title": "Verbinde dich <0>myNode" }, "rest_url": { "label": "Indconnect REST URL", - "placeholder": "Indconnect://IhrKnoten:8080?..." + "placeholder": "Indconnect://DeinKnoten:8080?..." }, "title": "meinKnoten" }, "umbrel": { "page": { - "instructions": "Gehe in deine Regenschirm Seite zu <0>verbinde Wallet. Wähle <0>lndconnect REST und kopiere <0>lndconnect URL. (Abhängig von deinem Setup kannst du entweder eine lokale Verbindung verwenden oder die Verbindung über TOR.)", - "title": "Verbinde dich mit deinem <0>Regenschirm-Knoten" + "instructions": "Gehe in deine Umbrel Seite zu <0>verbinde Wallet. Wähle <0>lndconnect REST und kopiere <0>lndconnect URL. (Abhängig von deinem Setup kannst du entweder eine lokale Verbindung verwenden oder die Verbindung über TOR.)", + "title": "Verbinde dich <0>Umbrel-Knoten" }, "rest_url": { - "label": "Indconnect-REST-URL", + "label": "Indconnect-REST URL", "placeholder": "lndconnect://deinknoten:8080?..." }, - "title": "Regenschirm" + "title": "Umbrel" }, - "description": "Du musst dich zuerst mit einer Lightning Wallet verbinden, damit du dich mit deinen Lieblingswebseiten verbinden kannst, die Bitcoin Lightning Zahlungen akzeptieren!", + "description": "Stelle eine Verbindung zu deiner externen Lightning-Wallet oder deinem Knoten her", "citadel": { "page": { "instructions": "Das funktioniert aktuell nicht, wenn 2FA ausgewählt ist.", - "title": "Verbinde dich mit dem Knoten <0>Zitadelle" + "title": "Verbinde dich <0>Citadel Knoten" }, - "title": "Zitadelle", + "title": "Citadel", "password": { - "label": "Zitadelle Passwort" + "label": "Citadel Passwort" }, "url": { - "label": "Zitadelle URL", - "placeholder": "http://zitadelle.local" + "label": "Citadel URL", + "placeholder": "http://citadel.local" } }, "raspiblitz": { "page": { "instructions1": "Du benötigst deine Node-Onion-Adresse, Port und eine Macaroon mit Lese- und Sendeberechtigungen (z. B. admin.macaroon).<1/><1/><0>SSH in deinem <0>RaspiBlitz.<1/>Führe den Befehl <0>sudo cat /mnt/hdd/tor/lndrest/hostname aus.<1/>Kopiere die <0>.onion-Adresse und füge deine Eingabe unten ein .<1/>Füge deinen <0>Port nach der Onion-Adresse hinzu, der Standardport ist <0>:8080.", - "title": "Verbinde dich mit deinem <0>RaspiBlitz-Knoten", + "title": "Verbinde dich mit deinem <0>RaspiBlitz Knoten", "instructions2": "Wähle <0>VERBINDEN.<1/>Wähle <0>EXPORTIEREN.<1/>Wähle <0>HEX.<1/>Kopiere das <0>adminMacaroon.<1/>Füge die Makrone in die Eingabe unten ein." }, - "title": "Raspiblitz", + "title": "RaspiBlitz", "rest_api_host": { - "label": "REST-API-Host", - "placeholder": "Ihr-Knoten-Onion-Adresse: Port" + "label": "REST API Host", + "placeholder": "Deine Knoten-Onion-Adresse: Port" } }, "eclair": { - "title": "Eclair Brieftasche", + "title": "Eclair", "page": { - "title": "Verbinde dich mit <0>Eclair", - "instructions": "Du benötigst deine Eclair-URL und dein Passwort." + "title": "Verbinde dich mit <0>Eclair", + "instructions": "Du benötigst deine Eclair URL und dein Passwort." }, "password": { "label": "Eclair Passwort" }, "url": { - "label": "Eclair Brieftasche URL", + "label": "Eclair URL", "placeholder": "http://localhost:8080" } }, "start9": { "page": { - "instructions": "<0>Hinweis: Derzeit unterstützen wir nur LND, aber wir werden in Zukunft c-lightning-Unterstützung hinzufügen!<1/>Klicke auf deinem Embassy-Dashboard auf den Dienst <0>Lightning Network Daemon. <1/>Wählen die Registerkarte <0>Eigenschaften.<1/>Kopiere nun die <0>LND Connect REST URL.", - "title": "Verbinde dich mit deinem <0>Embassy-Knoten" + "instructions": "<0>Hinweis: Derzeit unterstützen wir nur LND, aber wir werden in Zukunft c-lightning Unterstützung hinzufügen!<1/>Klicke auf deinem Embassy Dashboard auf den Dienst <0>lightning network Daemon. <1/>Wähle die Registerkarte <0>Eigenschaften.<1/>Kopiere nun die <0>LND Connect REST URL.", + "title": "Verbinde mit deinem <0>Embassy Knoten" }, "rest_url": { "placeholder": "lndconnect://deinknoten:8080?...", - "label": "Indconnect-REST-URL" + "label": "Indconnect REST URL" }, "title": "Start9" }, "bitcoin_beach": { - "title": "Bitcoin Beach Brieftasche", + "title": "Bitcoin Beach Wallet", "page": { - "title": "Verbinde dich mit <0>Bitcoin Beach Wallet" + "title": "Verbinde dich mit <0> Bitcoin Beach Wallet" } }, "bitcoin_jungle": { - "title": "Bitcoin Jungle Geldbörse", + "title": "Bitcoin Jungle Wallet", "page": { "title": "Verbinde dich mit <0>Bitcoin Jungle Wallet" } @@ -216,7 +194,7 @@ "label": "Gib deinen SMS-Bestätigungscode ein" }, "jwt": { - "label": "Gebe deinen JWT-Token ein", + "label": "Gebe deinen JWT Token ein", "info": "Das {{label}}-Login wird derzeit aktualisiert. Wenn du ein fortgeschrittener Benutzer bist, kannst du dir dein JWT-Token holen, indem du über <0>Web Wallet (wallet.mainnet.galoy.io)<1/><1/> anmeldest. Das JWT sieht so aus: < 2>eyJhbG...<1/><1/>" }, "actions": { @@ -225,31 +203,31 @@ }, "errors": { "failed_to_request_sms": "SMS-Code konnte nicht angefordert werden", - "failed_to_login_sms": "Anmeldung mit SMS-Code fehlgeschlagen", + "failed_to_login_sms": "Anmeldung mit SMS Code fehlgeschlagen", "setup_failed": "Einrichtung fehlgeschlagen", "missing_jwt": "JWT fehlt, Anmeldung nicht möglich.", "invalid_jwt": "ungültige JWT übermittelt" } }, "btcpay": { - "title": "BTCPay-Server", + "title": "BTCPay Server", "page": { "title": "Verbinde dich mit deinem BTCPay LND-Knoten", - "instructions": "Navigiere zu deinem BTCPayServer und melde dich als Administrator an. Gehen zu Servereinstellungen > Dienste > LND-Rest – Siehe Informationen. Klicke dann auf „QR-Code-Informationen anzeigen“ und kopiere die QR-Code-Daten. Füge es unten ein:" + "instructions": "Navigiere zu deinem BTCPayServer und melde dich als Administrator an. Gehe zu Servereinstellungen > Dienste > LND-Rest – Siehe Informationen. Klicke dann auf „QR-Code-Informationen anzeigen“ und kopiere die QR-Code-Daten. Füge es unten ein:" }, "config": { "label": "Konfigurationsdaten", "placeholder": "config=https://your-btc-pay.org/lnd-config/212121/lnd.config" }, "errors": { - "connection_failed": "Verbindung fehlgeschlagen. Ist die BTCPay-Verbindungs-URL korrekt und zugänglich?" + "connection_failed": "Verbindung fehlgeschlagen. Ist die BTCPay Verbindungs URL korrekt und zugänglich?" } }, "commando": { "title": "Core Lightning", "page": { - "title": "Stelle eine Verbindung zu deinem Core Lightning-Knoten her", - "instructions": "Stelle sicher, dass du Core Lightning Version 0.12.0 oder neuer hast, das Commando-Plugin ausgeführt wird und dein Knoten über das Lightning-Netzwerk zugänglich ist. Erstelle eine Rune, indem du „lightning-cli commando-rune“ ausführst." + "title": "Stelle eine Verbindung zu deinem Core Lightning Knoten her", + "instructions": "Stelle sicher, dass du die Core Lightning Version 0.12.0 oder neuer hast, das Commando-Plugin ausgeführt wird und dein Knoten über das Llightning Network zugänglich ist. Erstelle eine Rune, indem du „lightning-cli commando-rune“ ausführst." }, "host": { "label": "Host" @@ -274,19 +252,46 @@ "placeholder": "config=https://your-btc-pay.org/lnd-config/212121/lnd.config" }, "errors": { - "connection_failed": "Verbindung fehlgeschlagen. Ist dein Core Lightning-Knoten online und verwendet es das Commando-Plugin?" + "connection_failed": "Verbindung fehlgeschlagen. Ist dein Core Lightning Knoten online und verwendet es das Commando Plugin?" } }, "kollider": { "title": "Kollider", - "description": "Anmeldung bei deinem Kollider-Konto" + "description": "Anmeldung bei deinem Kollider Konto", + "page": { + "description": "Du hast noch kein Konto? <0>Jetzt anmelden!", + "title": "Verbinde dich mit deinem Kollider-Konto" + }, + "password": { + "label": "Gebe dein Kollider Passwort ein" + }, + "currency": { + "label": "Wähle dein Währungskonto" + }, + "errors": { + "connection_failed": "Verbindung fehlgeschlagen. Bist du dir sicher, dass die Kontodaten korrekt sind?" + }, + "username": { + "label": "Gebe dein Kollider Benutzernamen ein" + } }, - "title": "Verbinde Lightning Wallet" + "title": "Verbinde Lightning Wallet", + "lnc": { + "title": "LND mit LNC", + "page": { + "title": "Verbinde dich mit deinem LND Knoten", + "description": "Erstelle eine neue Sitzung im Terminal (litd), um eine neue Paarungsphrase zu erhalten, und gebe sie hier ein" + }, + "pairing_phrase": { + "label": "Dein Paarungssatz ", + "placeholder": "geheimer stapel sats phrase" + } + } }, "unlock": { "unlock_error": { "link": "Zurücksetzen und neuen Account erstellen", - "help": "Deine Kontodaten werden mit dem Freischalt-Passwort verschlüsselt. Wenn du dein Freischalt-Passwort wirklich vergessen hast, muss das Lightning-Konto zurückgesetzt und erneut hinzugefügt werden." + "help": "Deine Kontodaten werden mit deinem Entsperrpasswort verschlüsselt. Wenn du dein Entsperrpasswort wirklich vergessen hast, must du das Lightning-Konto zurücksetzen und erneut hinzufügen." }, "errors": { "invalid_password": "ungültiges Passwort" @@ -296,15 +301,12 @@ "part1": "Brauchst du Hilfe? Kontaktiere uns", "part2": "Alby-Unterstützung" }, - "unlock_password": "Ihr Freischalt-Passwort" + "unlock_password": "Dein Freischalt Passwort" }, "settings": { "nostr": { "private_key": { - "warning": "Das wird deinen alten Private Key löschen. Bist du sicher?", - "success": "Privaten Schlüssel erfolgreich verschlüsselt und gespeichert.", "title": "Privater Schlüssel", - "generate": "Generiere", "subtitle": "Füge deinen privaten Schlüssel ein oder erzeuge einen neuen Schlüssel. Beim Generieren eines Schlüssels wird ein neuer Schlüssel auf der Grundlage des aktuellen Kontos, das du verwendest, erstellt. Er kann erneut generiert werden, aber bitte stelle sicher, dass du diesen privaten Schlüssel sicherst. <0>Mehr erfahren\"" }, "hint": "Es ist ein einfaches und offenes Protokoll, das darauf abzielt, zensurresistente soziale Netzwerke zu schaffen. Nostr arbeitet mit kryptographischen Schlüsseln. Um etwas zu veröffentlichen, signiere es mit deinem Schlüssel und senden es an mehrere Relays. Du kannst Alby verwenden, um deinen Nostr-Schlüssel zu verwalten. Viele Nostr-Anwendungen erlauben dann, einfach den Schlüssel aus der Alby-Erweiterung zu verwenden.", @@ -312,19 +314,19 @@ }, "lnurl_auth": { "legacy_lnurl_auth": { - "title": "Legacy-Signatur für LNDhub und LNBits", + "title": "Legacy Signatur für LNDhub und LNbits", "subtitle": "Das Signieren von Nachrichten und die Anmeldung mit LNDhub- (z.B. BlueWallet) und LNbits-Konten wurde geändert (März 2022). Wenn du dich mit diesen Konten angemeldet hast, kannst du noch die alte Signiermethode aktivieren. Diese Option wird später entfernt werden, stelle also sicher, dass du auf das neue Login umsteigst." }, "legacy_lnurl_auth_202207": { "subtitle": "Die Schlüsselerzeugung für LNURL-auth hat sich geändert (Juli 2022). Alby war nicht kompatibel mit anderen Implementierungen. Dies wurde geändert, aber jetzt werden andere Anmeldeschlüssel verwendet. Wenn du dich bisher mit LNURL-auth angemeldet hast, kannst du noch die alte Methode aktivieren. Diese Option wird später entfernt werden, stelle sicher, dass du auf die neue Anmeldung umsteigst.", "title": "Vererbte LNURL-Authentifizierung" }, - "title": "LNURL-Authentifizierung", - "hint": "ist ein allgemeines Authentifizierungsprotokoll. Es authentifiziert den Benutzer anhand digitaler Signaturen. Das Protokoll erfordert keine anderen identifizierenden Informationen wie Passwörter, E-Mails, Benutzernamen oder ähnliches. Mit Alby kannst du deine Lightning-Konten verwenden, um sich sicher auf Websites anzumelden.
Um mit anderen Wallets kompatibel zu sein, mussten wir ein paar Änderungen vornehmen, die du hier konfigurieren kannst.
Idealerweise sind alle diese Optionen ausgeschaltet. Verwende sie nur, wenn du alte Konten hast." + "title": "LNURL Authentifizierung", + "hint": "ist ein allgemeines Authentifizierungsprotokoll. Es authentifiziert den Benutzer anhand digitaler Signaturen. Das Protokoll erfordert keine anderen identifizierenden Informationen wie Passwörter, E-Mails, Benutzernamen oder ähnliches. Mit Alby kannst du deine Lightning-Konten verwenden, um sich sicher auf Websites anzumelden.
Um mit anderen Wallets kompatibel zu sein, mussten wir ein paar Änderungen vornehmen, die du hier konfigurieren kannst.
Idealerweise sind alle diese Optionen ausgeschaltet. Verwende es nur, wenn du alte Konten hast." }, "browser_notifications": { - "subtitle": "Zahlungs- und authentifizierungsbezogene Benachrichtigungen.", - "title": "Browser-Benachrichtigungen" + "subtitle": "Zahlungs- und Authentifizierungsbezogene Benachrichtigungen.", + "title": "Browser Benachrichtigungen" }, "website_enhancements": { "title": "Erweiterungen der Website", @@ -336,11 +338,11 @@ "label": "Gib ein neues Entsperrkennwort ein:" }, "submit": { - "label": "Änder du" + "label": "Ändern" }, "errors": { "enter_password": "Bitte gebe ein neues Freischalt-Passwort ein.", - "confirm_password": "Bitte bestätigen dein Passwort.", + "confirm_password": "Bitte bestätige dein Passwort.", "mismatched_password": "Die Passwörter stimmen nicht überein." }, "success": "Passwort erfolgreich geändert", @@ -361,7 +363,7 @@ }, "exchange": { "title": "Austausch Quelle", - "subtitle": "Quelle der Bitcoin-Wechselkurse" + "subtitle": "Quelle für Bitcoin Wechselkurse" }, "language": { "title": "Sprache", @@ -369,7 +371,7 @@ }, "currency": { "title": "Währung", - "subtitle": "Zeigen Sie die Beträge zusätzlich in dieser Währung an" + "subtitle": "Zeige die Beträge zusätzlich in dieser Währung an" }, "camera_access": { "title": "Kamerazugriff", @@ -397,14 +399,14 @@ "home": { "default_view": { "is_blocked_hint": "Alby ist derzeit auf dem {{host}} deaktiviert", - "recent_transactions": "kürzliche Transaktionen", + "recent_transactions": "Jüngste Transaktionen", "no_transactions": "Es wurden noch keine Transaktionen mit Alby getätigt.", - "block_removed": "{{Host}} aktiviert. Bitte laden Sie die Website neu." + "block_removed": "{{Host}} aktiviert. Bitte lade die Website neu." }, "allowance_view": { "sats_used": "verwendete Sats", "no_transactions": "Noch keine Transaktionen auf <0>{{name}}.", - "recent_transactions": "kürzliche Transaktionen", + "recent_transactions": "Jüngste Transaktionen", "allowance": "Erlaubnis" }, "actions": { @@ -417,38 +419,24 @@ "incoming": "Eingehend" } }, - "recent_transactions": "kürzliche Transaktionen" + "recent_transactions": "Jüngste Transaktionen" }, "accounts": { "title": "Konten", - "export": { - "title": "Konto exportieren", - "screen_reader": "Kontodaten exportieren", - "waiting": "Warten auf LndHub-Daten...", - "scan_qr": "Importiere diese Brieftasche in Zeus oder BlueWallet, indem du den QRCode scannst.", - "your_ln_address": "Deine Lightning Adresse:", - "tip_mobile": "Tipp: Verwende diese Geldbörse mit deinem Mobilgerät", - "export_uri": "LNDHub Anmeldeinformationen URI" - }, "edit": { "name": { "label": "Name" - }, - "screen_reader": "Kontonamen bearbeiten", - "title": "Konto bearbeiten" - }, - "remove": { - "confirm": "Bist du dir sicher, dass du das Konto entfernen möchten: {{Name}}? \nDies kann nicht rückgängig gemacht werden. Wenn du dich mit diesem Konto bei Webseiten angemeldet hast, verlierst du möglicherweise den Zugriff auf diese." + } }, "actions": { "add_account": "Konto hinzufügen" } }, "enable": { - "allow": "Erlauben Sie {{host}} zu:", + "allow": "Erlaube {{host}} zu:", "title": "Verbinden", "request1": "Genehmigung für Transaktionen anfordern", - "request2": "Fordern Sie Rechnungen und Lightning Informationen an", + "request2": "Fordere deine Rechnungen und Lightning Informationen an", "block_and_ignore": "Blockieren und Ignorieren von {{host}}", "block_added": "Hinzugefügt {{host}} zur Blocklist, bitte Website neu laden." }, @@ -477,7 +465,11 @@ "title": "Erlaubnis", "used_budget": "sats verwendet" } - } + }, + "title": "Deine ⚡ Website", + "description": "Websites, auf denen du Alby bereits verwendet hast", + "no_info": "Es sieht so aus, als hättest du Alby noch in keiner Website verwendet.", + "discover": "Websites entdecken" }, "send": { "input": { @@ -516,18 +508,14 @@ "title": "Nostr", "allow": "Erlaube dieser Website:", "content": "Auf dieser Website wirst du aufgefordert zu signieren:", - "read_public_key": "Lese deinen öffentlichen Schlüssel", "block_and_ignore": "Blockieren und Ignorieren von {{host}}", "block_added": "{{host}} zur Blockliste hinzugefügt, bitte lade deine Website neu.", - "permissions": { - "decrypt": "Daten entschlüsseln", - "encrypt": "Daten verschlüsseln", - "allow_sign": "Erlaube {{host}} zu unterschreiben:" - } + "allow_sign": "Erlaube dein {{host}} zu unterschreiben:" }, "confirm_request_permission": { "title": "Antrag genehmigen", - "allow": "Erlaube die Ausführung dieser Website:" + "allow": "Erlaube die Ausführung dieser Website:", + "always_allow": "Denke an meine Wahl und frage nicht noch einmal" }, "lnurlauth": { "submit": "Anmeldung", @@ -552,7 +540,7 @@ }, "confirm_sign_message": { "title": "Unterschrift", - "content": "Auf dieser Website werden Sie aufgefordert, zu signieren:" + "content": "Auf dieser Website wirst du aufgefordert, zu signieren:" }, "confirm_keysend": { "title": "Zahlung genehmigen", @@ -566,7 +554,7 @@ "actions": { "pay_now": "Jetzt bezahlen" }, - "success": "Zahlung von {{Betrag}} erfolgreich gesendet!" + "success": "Zahlung von einem {{Betrag}} erfolgreich gesendet!" }, "lnurlpay": { "amount": { @@ -628,7 +616,7 @@ }, "errors": { "enter_password": "Bitte gebe dein Passwort ein.", - "confirm_password": "Bestätige dein Passwort", + "confirm_password": "Bitte bestätige dein Passwort.", "mismatched_password": "Die Passwörter stimmen nicht überein." } } @@ -638,7 +626,7 @@ "other": { "and_more": "& mehr...", "title": "Andere Wallets", - "description": "Verbinde dich mit deiner bestehenden Lightning Wallet oder Node und wählen aus verschiedenen Anschlüssen.", + "description": "Verbinde dich mit deiner bestehenden Lightning Wallet oder Node und wähle aus verschiedenen Anschlüssen.", "connect": "Verbinden" }, "alby": { @@ -648,39 +636,148 @@ }, "title": "Verbinden", "description": "Um Alby für Online-Zahlungen zu nutzen, verbinde deine Lightning Wallet mit der Erweiterung." + }, + "discover": { + "description": "Websites und Webapplikationen, auf denen du Alby verwenden kannst", + "title": "Erkunde das Ökosystem von Lightning ⚡️", + "list": { + "gaming": "Spielen", + "trading": "Handel", + "entertaiment": "Unterhaltung", + "shopping": "Einkaufen", + "miscellaneous": "Sonstiges", + "showcases": "Schaukästen", + "nostr": "Nostr" + }, + "tips": { + "title": "Deine Alby-Wallet ist bereit", + "top_up_wallet": { + "description": "Erstelle eine Lightning-Rechnung, sende dir selbst Bitcoin und nutze das Alby im Lightning-Ökosystem", + "title": "⚡️ Wallet aufladen", + "label1": "Bitcoin erhalten", + "label2": "Bitcoin kaufen" + }, + "demo": { + "description": "Entdecke alles, was du mit Alby tun könntest, auf unserer Demo-Website", + "title": "🕹️ Alby Demo ausprobieren", + "label1": "Probiere es aus" + }, + "address": { + "description": "Erstelle eine eigene Lightningadresse und empfange Lightningzahlungen einfach per E-Mail", + "title": "Eine Lightning Adresse erhalten", + "label1": "Hole es dir jetzt!" + }, + "description": "Ein paar Tipps für den Anfang 🐝", + "pin": { + "title": "📌 Pin für die Alby-Erweiterung", + "description2": "Suche in der Liste der Erweiterungen von Alby und klicke auf das Pinsymbol, um es an die Symbolleiste anzuheften", + "description3": "Das war's! Um auf Alby zuzugreifen, klicke einfach auf das Alby-Symbol", + "description1": "<0>Um einfach auf Alby zuzugreifen, klicke auf das Erweiterungssymbol <1/> <2>in der oberen rechten Ecke Ihres Browsers" + } + } } }, "components": { "account_menu": { "options": { "account": { - "add": "Füge einen neuen Account hinzu" + "add": "Füge einen neuen Account hinzu", + "manage": "Konten verwalten" } - } + }, + "title": "Konto wechseln", + "screen_reader": "Dropdown umschalten" }, "publishers_table": { - "payments": "Zahlungen" + "payments": "Zahlungen", + "used": "gebraucht" }, "toasts": { "connection_error": { - "double_check": "Überprüfe nochmals deine Verbindungseinstellungen" + "double_check": "Überprüfe nochmals deine Verbindungseinstellungen", + "if_ssl_errors": "und wenn es SSL-Fehler gibt (z.B. ERR_CERT_AUTHORITY_INVALID), klicke auf \"Erweitert\" und fahre fort, das Zertifikat zu akzeptieren.", + "visit_guides": "Besuche unsere Leitfäden für weitere Hilfe", + "what_you_can_do": "Das kannst du tun:" }, "errors": { "invalid_credentials": "Falsches Passwort. Überprüfe das Passwort und E-Mail Adresse und versuche es noch einmal." + }, + "login_failed": { + "password_reset": "Hast du dein Passwort vergessen? Klicke bitte hier" } }, "companion_download_info": { - "using_tor": "Klicke hier zum Weitermachen, wenn du den TOR Browser verwendest" + "using_tor": "Klicke hier zum Weitermachen, wenn du den TOR Browser verwendest", + "description": "Du versuchst, dich mit einem Knoten hinter Tor zu verbinden. Dazu muss entweder die Alby Companion App installiert oder Tor ausgeführt werden. (Wenn du dir nicht sicher bist, empfehlen wir die Alby Companion App.)", + "download_here": "Lade dein Alby-Begleiter hier herunter!", + "or": "Oder:" + }, + "allowance_menu": { + "edit_allowance": { + "title": "Zulage bearbeiten", + "screen_reader": "Zulässigkeitsoptionen" + }, + "enable_login": { + "title": "Anmeldung auf der Website aktivieren", + "subtitle": "Automatisches Einloggen ohne Bestätigung, wenn die Website dazu auffordert." + }, + "edit_permissions": "Berechtigungen bearbeiten", + "hint": "Dadurch wird das aktuelle Budget zurückgesetzt", + "new_budget": { + "label": "Neues Budget" + }, + "confirm_delete": "Bist du dir sicher, dass du diese Website löschen willst?" + }, + "qrcode_scanner": { + "actions": { + "stop_scanning": "Scannen beenden", + "start_scanning": "Scannen starten" + }, + "errors": { + "allow_camera_access": "Bitte erlaube deinen Kamerazugriff in den Einstellungen." + }, + "title": "QR Code scannen" + }, + "transactionsTable": { + "sent": "Gesendet", + "boostagram": { + "sender": "Sender", + "app": "App", + "podcast": "Podcast", + "message": "Nachricht" + }, + "preimage": "Vorschaubild", + "received": "Empfangen", + "fee": "Gebühr", + "open_location": "Website öffnen" + }, + "budget_control": { + "remember": { + "description": "Du kannst dein Guthaben so einstellen, dass du erst dann um eine Zahlungsbestätigung gebeten wirst, wenn es aufgebraucht ist.", + "label": "Erinnere dich und lege ein Budget fest" + }, + "budget": { + "label": "Budget" + } + }, + "badge": { + "label": { + "active": "AKTIV", + "auth": "LOGIN" + } + }, + "confirm_or_cancel": { + "only_trusted": "Verbinde dich nur mit Websites, denen du vertraust." } }, "common": { "optional": "Optional", "feedback": "Rückmeldung", "error": "Fehler", - "websites": "Websites", + "websites": "Webseite", "loading": "Laden", "sats_one": "sat", - "success": "Erfolgreich!", + "success": "Erfolgreich", "password": "Passwort", "advanced": "Fortgeschrittene", "description_full": "Vollständige Beschreibung", @@ -688,6 +785,123 @@ "copied": "Kopiert!", "description": "Beschreibung", "settings": "Einstellungen", - "sats_other": "sats" + "sats_other": "sats", + "confirm_password": "Bestätige dein Passwort", + "help": "Hilfe", + "actions": { + "back": "Zurück", + "open": "Öffnen", + "cancel": "Abbrechen", + "confirm": "Bestätigen", + "send": "Senden", + "unlock": "Freischalten", + "receive": "Empfangen", + "delete": "Löschen", + "edit": "Bearbeiten", + "next": "Weiter", + "export": "Exportieren", + "log_in": "Einloggen", + "close": "Schließen", + "remove": "entfernen", + "continue": "Weiter", + "lock": "Schloss", + "connect": "Verbinden", + "save": "Speichern", + "copy": "Kopieren", + "remember": "Denke an meine Wahl und frage nicht noch einmal" + }, + "discover": "Entdecken", + "errors": { + "connection_failed": "Verbindung fehlgeschlagen", + "payment_failed": "Zahlung fehlgeschlagen" + }, + "message": "Nachricht", + "response": "Antwort", + "success_message": "{{amount}}{{fiatAmount}} gesendet an {{destination}}" + }, + "permissions": { + "commando": { + "bkpr-listbalances": "Liste aller aktuellen und historischen Kontosalden.", + "checkmessage": "Überprüfe, ob die Signatur von einem bestimmten Knoten erzeugt wurde.", + "fundchannel": "Öffne einen Zahlungskanal mit einem Peer, indem du eine Finanzierungstransaktion durchführst.", + "getroute": "Finde die beste Route für die Zahlung an einen Lightningnoten.", + "invoice": "Schaffe die Erwartung einer Zahlung.", + "keysend": "Sende eine Zahlung an einen anderen Knoten.", + "listoffers": "Alle Angebote auflisten oder ein bestimmtes Angebot erhalten.", + "listpays": "Ruft den Status aller Zahlungsbefehle ab.", + "signmessage": "Erstelle eine Signatur aus diesem Knoten.", + "feerates": "Rückgabe der Fee Rate, die bei CLN verwenden wird.", + "listforwards": "Auflistung aller versuchten Übermittlungsversuche.", + "listfunds": "Listet alle verfügbaren Mittel auf.", + "listinvoices": "Abrufen des Status aller Rechnungen.", + "sendpay": "Sende eine Zahlung über eine Route.", + "multifundchannel": "Öffne mehrere Zahlungskanäle mit Knotenpunkten, indem du eine einzige Finanzierungstransaktion durchführst.", + "disconnect": "Schließen einer bestehenden Verbindung zu einem Peer.", + "decodepay": "Prüfe und analysiere einen Bolt11-String.", + "getinfo": "Abrufen der Zusammenfassung des Knotens.", + "listtransactions": "Liste der in der Wallet verfolgten Transaktionen.", + "offer": "Erstelle ein Angebot.", + "connect": "Stelle eine neue Verbindung zu einem anderen Knoten her.", + "decode": "Dekodiere eine Bolt11/Bolt12/Rune-Kette.", + "listpeers": "Liste der Knoten, die mit diesem Knoten verbunden sind oder offene Kanäle haben.", + "listsendpays": "Ruft den Status aller sendpay-Befehle ab.", + "pay": "Sende eine Zahlung an eine BOLT11-Rechnung.", + "setchannel": "Konfiguriere die für einen Kanal beworbenen Gebühren / htlc-Bereiche.", + "listnodes": "Nodes auflisten, von denen der Node über Nachrichten erfahren hat." + }, + "lnd": { + "channelbalance": "Du erhältst einen Bericht über den Gesamtbetrag der Mittel für alle offenen Kanäle.", + "walletbalance": "Ermittel die gesamten nicht verbrauchten Ausgaben der Wallet.", + "listchannels": "Erhalte eine Beschreibung aller offenen Kanäle.", + "openchannel": "Öffne einen neuen Kanal.", + "estimatefee": "Schätze den Gebührensatz und die Gesamtgebühren für eine Transaktion.", + "getchaninfo": "Ermittelt die Netzansage für den angegebenen Kanal.", + "getinfo": "Abrufen der Knoteninformationen.", + "listinvoices": "Erhalte eine Liste aller Rechnungen.", + "connectpeer": "Stelle eine Verbindung zu einer entfernten Gegenstelle her.", + "disconnectpeer": "Trenne die Verbindung zu einer entfernten Gegenstelle.", + "getnetworkinfo": "Abrufen grundlegender Statistiken über die bekannte Kanalkurve.", + "getnodeinfo": "Abrufen der Kanalinformationen für einen Knoten.", + "gettransactions": "Abrufen einer Liste aller für die Wallets relevanten Transaktionen.", + "listpeers": "Erhalte eine Liste aller derzeit aktiven Peers.", + "lookupinvoice": "Rechnungsdetails nachschlagen.", + "queryroutes": "Abfrage nach einer möglichen Route.", + "sendtoroute": "Führe eine Zahlung über die angegebene Route durch.", + "routermc": "Lese den internen Status der Missionskontrolle.", + "decodepayreq": "Entschlüssel eine Zahlungsanforderungszeichenfolge.", + "addinvoice": "Erstelle neue Rechnungen.", + "listpayments": "Erhalte eine Liste aller ausgehenden Zahlungen.", + "verifymessage": "Überprüfe eine Signatur über eine Nachricht." + }, + "nostr": { + "getpublickey": "Lese deinen öffentlichen Schlüssel.", + "nip04decrypt": "Daten entschlüsseln.", + "nip04encrypt": "Daten verschlüsseln.", + "signmessage": "Unterschreibe deine Nachricht mit deinem Schlüssel." + }, + "lnc": { + "connectpeer": "Stelle eine Verbindung zu einer Gegenstelle her.", + "disconnectpeer": "Trenne die Verbindung zu einer entfernten Gegenstelle.", + "estimatefee": "Schätze den Gebührensatz und die Gesamtgebühren für eine Transaktion.", + "getnodeinfo": "Abrufen der Kanalinformationen für einen Knoten.", + "getchaninfo": "Ermittelt die Netzansage für den angegebenen Kanal.", + "listpayments": "Erhalte eine Liste aller ausgehenden Zahlungen.", + "queryroutes": "Abfrage nach einer möglichen Route.", + "verifymessage": "Überprüfung einer Signatur über eine Nachricht.", + "listpeers": "Ermittelt eine Liste aller derzeit aktiven Peers.", + "sendtoroute": "Nehme eine Zahlung über die angegebene Route vor.", + "decodepayreq": "Dekodierung einer Zahlungsanforderungszeichenfolge.", + "addinvoice": "Erstelle eine neue Rechnungen.", + "routermc": "Lese den internen Status der Missionskontrolle.", + "gettransactions": "Abrufen einer Liste aller für die Wallet relevanten Transaktionen.", + "getnetworkinfo": "Abrufen grundlegender Statistiken über die bekannte Kanalkurve.", + "lookupinvoice": "Rechnungsdetails nachschlagen.", + "getinfo": "Abrufen der Knoteninformationen.", + "listchannels": "Erhalte eine Beschreibung aller offenen Kanäle.", + "listinvoices": "Erhalte eine Liste aller Rechnungen.", + "channelbalance": "Erhalte einen Bericht über den Gesamtbetrag der Mittel für alle offenen Kanäle.", + "walletbalance": "Ermittel die gesamten nicht verbrauchten Ausgaben deiner Wallet.", + "openchannel": "Öffne einen neuen Kanal." + } } } diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index db07359438..d2e0a6f4cb 100644 --- a/src/i18n/locales/en/translation.json +++ b/src/i18n/locales/en/translation.json @@ -1,36 +1,16 @@ { "translation": { "welcome": { + "title": "Welcome to Alby", "nav": { "welcome": "Welcome", "password": "Your Password", "connect": "Your Lightning Account", "done": "Done" }, - "intro": { - "send": { - "title": "Send in One Click", - "description": "Lightning transactions happen all in your browser. No alt+tab or QR-code scanning needed." - }, - "paywall": { - "title": "No more annoying paywalls", - "description": "Define individual budgets for websites to enable seamless payment streams. No more annoying paywalls." - }, - "privacy": { - "title": "Privacy first", - "description": "Use lightning to authenticate and control your privacy." - }, - "foss": { - "title": "Free and Open Source", - "description": "Completely open code that can be audited by anyone. No stats or trackers. You are in control." - }, - "actions": { - "get_started": "Get Started" - } - }, "set_password": { "title": "Set an unlock password", - "description": "Your lightning account data is securely encrypted with an unlock password. Do not forget this password! You need it to unlock the Alby Extension (in this browser)", + "description": "This password is used to protect your wallet and provide access to the browser extension. It cannot be reset and is separate from your Alby account password.", "choose_password": { "label": "Choose an unlock password:" }, @@ -45,8 +25,6 @@ }, "test_connection": { "ready": "Awesome, you’re ready to go!", - "tutorial": "Now you’ve connected your wallet would you like to go through a tutorial?", - "try_tutorial": "Give it a try now", "initializing": "Initializing your account. Please wait, this can take a minute...", "connection_error": "Connection Error", "review_connection_details": "Please review your connection details.", @@ -66,7 +44,7 @@ "create_new": "Sign up" }, "other": { - "title": "Other wallets", + "title": "Other Wallets", "description": "Connect to your existing lightning wallet or node and choose from various connectors.", "and_more": "& more...", "connect": "Connect" @@ -138,6 +116,17 @@ "connection_failed": "Connection failed. Are your LND credentials correct?" } }, + "lnc": { + "title": "LND with LNC", + "page": { + "title": "Connect to your LND node", + "description": "Create a new session in terminal (litd) to obtain a new pairing phrase and enter it here" + }, + "pairing_phrase": { + "label": "Your pairing phrase ", + "placeholder": "secret stack sats phrase" + } + }, "lndhub_bluewallet": { "title": "Bluewallet", "page": { @@ -385,27 +374,61 @@ }, "accounts": { "title": "Accounts", - "actions": { - "add_account": "Add account" - }, - "edit": { - "title": "Edit Account", + "account_view": { + "title1": "Account Information", + "title2": "Edit Account", "name": { - "label": "Name" + "title": "Name", + "placeholder": "Account Name" + }, + "export": { + "title": "Export Account", + "screen_reader": "Export account details", + "waiting": "waiting for LndHub data...", + "your_ln_address": "Your Lightning Address:", + "tip_mobile": "Tip: Use this wallet with your mobile device", + "export_uri": "LNDHub Credentials URI", + "scan_qr": "Import this wallet into Zeus or BlueWallet by scanning the QRCode." + }, + "nostr": { + "title": "Nostr", + "hint": "is a simple and open protocol that aims to create censorship-resistant social networks. Nostr works with cryptographic keys. To publish something you sign it with your key and send it to multiple relays. You can use Alby to manage your Nostr key. Many Nostr applications will then allow you to simply use the key from the Alby extension.", + "private_key": { + "title": "Manage your keys", + "subtitle": "Paste your private key or generate a new one. Make sure you backup your private key before you generate a new one. <0>Learn more »", + "warning": "This will delete your old private key. Are you sure?", + "success": "Private key encrypted & saved successfully.", + "successfully_removed": "Private key removed successfully.", + "label": "Private Key" + }, + "public_key": { + "label": "Public Key" + }, + "generate_keys": { + "title": "Generate new Nostr keys", + "screen_reader": "Generate new nostr keys for your account", + "hint": "You can generate a random set of Nostr keys or derive keys from this account details (using a signed canonical phrase). <0>Learn more »", + "actions": { + "random_keys": "Generate random keys", + "derived_keys": "Derive keys from account" + } + }, + "actions": { + "generate": "Generate new keys" + } }, - "screen_reader": "Edit account name" - }, - "export": { - "title": "Export Account", - "screen_reader": "Export account details", - "waiting": "waiting for LndHub data...", - "your_ln_address": "Your Lightning Address:", - "tip_mobile": "Tip: Use this wallet with your mobile device", - "export_uri": "LNDHub Credentials URI", - "scan_qr": "Import this wallet into Zeus or BlueWallet by scanning the QRCode." + "remove": { + "title": "Remove This Account", + "subtitle": "All the linked allowances will be deleted. Please be certain.", + "confirm": "Are you sure you want to remove account: {{name}}? \nThis can not be undone. If you used this account to login to websites you might lose access to those." + }, + "actions": { + "remove_account": "Remove account", + "export": "Export" + } }, - "remove": { - "confirm": "Are you sure you want to remove account: {{name}}? \nThis can not be undone. If you used this account to login to websites you might lose access to those." + "actions": { + "add_account": "Add account" } }, "enable": { @@ -523,10 +546,8 @@ "hint": "is a simple and open protocol that aims to create censorship-resistant social networks. Nostr works with cryptographic keys. To publish something you sign it with your key and send it to multiple relays. You can use Alby to manage your Nostr key. Many Nostr applications will then allow you to simply use the key from the Alby extension.", "private_key": { "title": "Private key", - "subtitle": "Paste your private key or generate a new one. Generating a key will create a new key based on the current account you use. It can be re-generated, but please make sure you backup this private key. <0>Learn more »", - "generate": "Generate", - "warning": "This will delete your old private key. Are you sure?", - "success": "Private key encrypted & saved successfully." + "subtitle": "This section is moved to accounts page as keys are account specific now.", + "go_to": "Go To Accounts" } } }, @@ -611,11 +632,38 @@ "entertaiment": "Entertaiment", "shopping": "Shopping", "miscellaneous": "Miscellaneous", - "showcases": "Showcases" + "showcases": "Showcases", + "nostr": "Nostr" + }, + "tips": { + "title": "Your Alby wallet is ready", + "description": "A few tips to get you started buzzin'🐝", + "top_up_wallet": { + "title": "⚡️ Top up your wallet", + "description": "Create a lightning invoice, send some bitcoin to yourself and start using Alby in the lightning ecosystem", + "label1": "Receive Bitcoin", + "label2": "Buy Bitcoin" + }, + "pin": { + "title": "📌 Pin your Alby extension", + "description1": "<0>To access Alby easily, click on extension icon <1/> <2>in the top right corner of your browser", + "description2": "In the list of extensions, find Alby and click pin icon to pin it to the toolbar", + "description3": "Thats it! To access Alby just click Alby icon" + }, + "demo": { + "title": "🕹️ Try out Alby Demo", + "description": "Discover all things you can do with Alby on our demo website", + "label1": "Give it a try" + }, + "address": { + "title": "⚡️ Get a lightning address", + "description": "Create your own lightning address and to receive lightning payments with ease of sending an e-mail", + "label1": "Get it now!" + } } }, "publishers": { - "title": "Your ⚡️ Websites", + "title": "Your ⚡ Websites", "description": "Websites where you have used Alby before", "no_info": "It looks like you haven't used Alby in any websites yet.", "discover": "Discover Websites", @@ -676,19 +724,9 @@ "title": "Nostr", "allow": "Allow this website to:", "content": "This website asks you to sign:", - "read_public_key": "Read your public key", + "allow_sign": "Allow {{host}} to sign:", "block_and_ignore": "Block and ignore {{host}}", - "block_added": "Added {{host}} to the blocklist, please reload the website.", - "confirm_sign_message": { - "remember": { - "label": "Remember my choice and don't ask again" - } - }, - "permissions": { - "decrypt": "Decrypt data", - "encrypt": "Encrypt data", - "allow_sign": "Allow {{host}} to sign:" - } + "block_added": "Added {{host}} to the blocklist, please reload the website." } }, "common": { @@ -698,6 +736,7 @@ "success": "Success", "error": "Error", "settings": "Settings", + "accounts": "Accounts", "discover": "Discover", "websites": "Websites", "sats_one": "sat", @@ -732,7 +771,8 @@ "export": "Export", "remove": "Remove", "copy": "Copy", - "log_in": "Log in" + "log_in": "Log in", + "remember": "Remember my choice and don't ask again" }, "errors": { "connection_failed": "Connection failed", @@ -753,7 +793,8 @@ "edit_allowance": { "title": "Edit Allowance", "screen_reader": "Allowance Options" - } + }, + "edit_permissions": "Edit Permissions" }, "qrcode_scanner": { "title": "Scan QR Code", @@ -832,6 +873,12 @@ } }, "permissions": { + "nostr": { + "getpublickey": "Read your public key.", + "nip04encrypt": "Encrypt data.", + "nip04decrypt": "Decrypt data.", + "signmessage": "Sign message with your key." + }, "commando": { "bkpr-listbalances": "List of all current and historical account balances.", "checkmessage": "Verify that the signature was generated by a given node.", @@ -884,6 +931,30 @@ "decodepayreq": "Decode a payment request string.", "routermc": "Read the internal mission control state.", "addinvoice": "Create new invoices." + }, + "lnc": { + "getinfo": "Get the node information.", + "listchannels": "Get a description of all the open channels.", + "listinvoices": "Get a list of all invoices.", + "channelbalance": "Get a report on the total funds across all open channels.", + "walletbalance": "Get the total unspent outputs of the wallet.", + "openchannel": "Open a new channel.", + "connectpeer": "Establish a connection to a remote peer.", + "disconnectpeer": "Disconnect from a remote peer.", + "estimatefee": "Estimate the fee rate and total fees for a transaction.", + "getchaninfo": "Get the network announcement for the given channel.", + "getnetworkinfo": "Get basic stats about the known channel graph.", + "getnodeinfo": "Get the channel information for a node.", + "gettransactions": "Get a list of all transactions relevant to the wallet.", + "listpayments": "Get a list of all outgoing payments.", + "listpeers": "Get a list all currently active peers.", + "lookupinvoice": "Look up invoice details.", + "queryroutes": "Query for a possible route.", + "verifymessage": "Verify a signature over a msg.", + "sendtoroute": "Make a payment via the specified route.", + "decodepayreq": "Decode a payment request string.", + "routermc": "Read the internal mission control state.", + "addinvoice": "Create new invoices." } } } diff --git a/src/i18n/locales/eo/translation.json b/src/i18n/locales/eo/translation.json index f0fbcfe84d..d0b88fe1d4 100644 --- a/src/i18n/locales/eo/translation.json +++ b/src/i18n/locales/eo/translation.json @@ -7,27 +7,6 @@ "connect": "Via konto ĉe Lightning", "done": "Farite" }, - "intro": { - "send": { - "title": "Sendi per unu alklako", - "description": "" - }, - "paywall": { - "title": "", - "description": "" - }, - "privacy": { - "title": "Privateco unue", - "description": "" - }, - "foss": { - "title": "Libera kaj malfermitkoda", - "description": "" - }, - "actions": { - "get_started": "Komenci" - } - }, "set_password": { "title": "", "description": "", @@ -45,8 +24,6 @@ }, "test_connection": { "ready": "Bonege, ĉio pretas!", - "tutorial": "", - "try_tutorial": "Ekprovi nun", "initializing": "", "connection_error": "Konekta eraro", "review_connection_details": "", @@ -307,24 +284,8 @@ "actions": { "add_account": "" }, - "edit": { - "title": "", - "name": { - "label": "Nomo" - }, - "screen_reader": "" - }, "export": { - "title": "", - "screen_reader": "", - "waiting": "", - "your_ln_address": "", - "tip_mobile": "", - "export_uri": "", - "scan_qr": "" - }, - "remove": { - "confirm": "" + "tip_mobile": "" } }, "enable": { @@ -441,10 +402,7 @@ "hint": "", "private_key": { "title": "Privata ŝlosilo", - "subtitle": "", - "generate": "Generi", - "warning": "", - "success": "" + "subtitle": "" } } }, @@ -577,7 +535,6 @@ "nostr": { "title": "Nostr", "allow": "", - "read_public_key": "", "block_and_ignore": "" } }, diff --git a/src/i18n/locales/es/translation.json b/src/i18n/locales/es/translation.json index 833d28a3c7..2bb3ddf4cf 100644 --- a/src/i18n/locales/es/translation.json +++ b/src/i18n/locales/es/translation.json @@ -7,27 +7,6 @@ "connect": "Su cuenta de Lightning", "done": "Hecho" }, - "intro": { - "send": { - "title": "Enviar en un Click", - "description": "Las transacciones de lightning ocurren en su navegador. No se necesita alt+tab ni escaneo de códigos QR." - }, - "paywall": { - "title": "No más molestos muros de pago", - "description": "Defina presupuestos individuales para sitios web para permitir flujos de pago sin interrupciones. No más molestos muros de pago." - }, - "privacy": { - "title": "Privacidad primero", - "description": "Utilice lightning para autenticarse y controlar su privacidad." - }, - "foss": { - "title": "Gratis y de Código Abierto", - "description": "Código completamente abierto que puede ser auditado por cualquier persona. Sin estadísticas ni rastreadores. Tú tienes el control." - }, - "actions": { - "get_started": "Comience" - } - }, "set_password": { "title": "Proteja su billetera", "description": "Los datos de su cuenta Lightning se cifran de forma segura con una contraseña de desbloqueo. No olvides esta contraseña! Lo necesita para desbloquear el teléfono por extensión (en este navegador)", @@ -45,8 +24,6 @@ }, "test_connection": { "ready": "¡Impresionante, estás listo para comenzar!", - "tutorial": "Ahora que ha conectado su billetera, ¿le gustaría realizar un tutorial?", - "try_tutorial": "Pruébalo ahora", "initializing": "Inicializando su cuenta. Por favor, espere, esto puede tardar un minuto...", "connection_error": "Error de Conexión", "review_connection_details": "Revise los detalles de su conexión.", @@ -216,24 +193,8 @@ "actions": { "add_account": "Añadir Cuenta" }, - "edit": { - "title": "Editar Cuenta", - "name": { - "label": "Nombre" - }, - "screen_reader": "Editar nombre de cuenta" - }, "export": { - "title": "Cuenta de exportación", - "screen_reader": "Exportar detalles de la cuenta", - "waiting": "esperando los datos de LndHub...", - "your_ln_address": "Su Dirección de Lightning:", - "tip_mobile": "Consejo: Use esta billetera con su dispositivo móvil", - "export_uri": "Credenciales de la URI LNDHub", - "scan_qr": "Importe esta billetera a Zeus o BlueWallet escaneando el código QR." - }, - "remove": { - "confirm": "¿Está seguro de que desea eliminar la cuenta: {{name}}? \nEsto no se puede deshacer. Si usó esta cuenta para iniciar sesión en sitios web, es posible que pierda el acceso a ellos." + "tip_mobile": "Consejo: Use esta billetera con su dispositivo móvil" } }, "enable": { diff --git a/src/i18n/locales/fi/translation.json b/src/i18n/locales/fi/translation.json index 7387dd4bf6..fa542f2476 100644 --- a/src/i18n/locales/fi/translation.json +++ b/src/i18n/locales/fi/translation.json @@ -7,27 +7,6 @@ "connect": "Lightning-tilisi", "done": "Tehty" }, - "intro": { - "send": { - "title": "Lähetä yhdellä napsautuksella", - "description": "Lightning-transaktiot tapahtuvat selaimessasi. Alt+tab tai QR-koodin skannausta ei tarvita." - }, - "paywall": { - "title": "Ei enää ärsyttäviä maksumuureja", - "description": "Määrittele verkkosivustoille omat budjetit, jotta maksuvirrat ovat saumattomia. Ei enää ärsyttäviä maksumuureja." - }, - "privacy": { - "title": "Yksityisyys ensin", - "description": "Käytä Lightningia todentamiseen ja yksityisyyden hallintaan." - }, - "foss": { - "title": "Ilmainen ja avoin lähdekoodi", - "description": "Täysin avoin koodi, jonka kuka tahansa voi tarkastaa. Ei tilastoja tai jäljittimiä. Sinä hallitset." - }, - "actions": { - "get_started": "Aloita" - } - }, "set_password": { "title": "Aseta lukituksen avaussalasana", "description": "Lightning-tilisi tiedot on salattu turvallisesti salasanan avulla. Älä unohda tätä salasanaa! Tarvitset sitä avataksesi Alby-laajennuksen lukituksen (tässä selaimessa)", @@ -45,8 +24,6 @@ }, "test_connection": { "ready": "Mahtavaa, olet valmis menemään!", - "tutorial": "Nyt kun olet yhdistänyt lompakkosi, haluaisitko käydä läpi opastuksen?", - "try_tutorial": "Kokeile sitä nyt", "initializing": "Tilin alustaminen. Odota, tämä voi kestää hetken...", "connection_error": "Yhteysvirhe", "review_connection_details": "Ole hyvä ja tarkista yhteystietosi.", @@ -307,24 +284,8 @@ "actions": { "add_account": "Lisää tili" }, - "edit": { - "title": "Muokkaa tiliä", - "name": { - "label": "Nimi" - }, - "screen_reader": "Muokkaa tilin nimeä" - }, "export": { - "title": "Vie tili", - "screen_reader": "Vie tilin yksityiskohdat", - "waiting": "Odotetaan LndHub-tietoja...", - "your_ln_address": "Lightning-osoitteesi:", - "tip_mobile": "Vinkki: Käytä tätä lompakkoa mobiililaitteesi kanssa", - "export_uri": "LNDHub-tunnistetietojen URI", - "scan_qr": "Tuo tämä lompakko Zeusiin tai sinilompakkoon skannaamalla QRCode." - }, - "remove": { - "confirm": "Haluatko varmasti poistaa tilin: {{name}}?\nTätä ei voi peruuttaa. Jos käytit tätä tiliä verkkosivustoille kirjautumiseen, saatat menettää pääsyn niihin." + "tip_mobile": "Vinkki: Käytä tätä lompakkoa mobiililaitteesi kanssa" } }, "enable": { @@ -441,10 +402,7 @@ "hint": "on yksinkertainen ja avoin protokolla, jonka tarkoituksena on luoda sensuurin kestäviä sosiaalisia verkostoja. Nostr toimii kryptografisten avainten kanssa. Jos haluat julkaista jotain, allekirjoita se avaimellasi ja lähetä se useille välittäjille. Voit käyttää Albyä Nostr-avaimen hallintaan. Monet Nostr-sovellukset sallivat sitten yksinkertaisesti käyttää avainta Alby-laajennuksesta.", "private_key": { "title": "Yksityinen avain", - "subtitle": "Avaimen luominen luo uuden avaimen nykyisen käyttämäsi tilin perusteella. Varmista, että varmuuskopioit tämän yksityisen avaimen.", - "generate": "Luo", - "warning": "Tämä poistaa vanhan yksityisen avaimesi. Oletko varma?", - "success": "Yksityinen avain salattu ja tallennettu onnistuneesti." + "subtitle": "Avaimen luominen luo uuden avaimen nykyisen käyttämäsi tilin perusteella. Varmista, että varmuuskopioit tämän yksityisen avaimen." } } }, @@ -577,7 +535,6 @@ "nostr": { "title": "Nostr", "allow": "Salli {{host}}:", - "read_public_key": "Lue julkinen avaimesi", "block_and_ignore": "Estä ja jätä huomiotta {{host}}" } }, diff --git a/src/i18n/locales/hi/translation.json b/src/i18n/locales/hi/translation.json index 2c1a253e8f..1971b0349b 100644 --- a/src/i18n/locales/hi/translation.json +++ b/src/i18n/locales/hi/translation.json @@ -1,27 +1,6 @@ { "translation": { "welcome": { - "intro": { - "send": { - "title": "एक क्लिक में भेजें", - "description": "लाइटनिंगल लेनदेन आपके ब्राउज़र में होता है | alt+tab या QR-कोड स्कैन करने की आवश्यकता नहीं|" - }, - "paywall": { - "title": "अब और झंझट वाले paywalls नहीं", - "description": "वेबसाइटों के लिए व्यक्तिगत खर्चा निर्धारित करें जिससे पायें निर्बाध भुगतान धाराएँ| और ज्यादा झंझट वाली paywalls नहीं|" - }, - "actions": { - "get_started": "शुरू करें" - }, - "privacy": { - "title": "गोपनीयता पहले", - "description": "लाइटनिंग का प्रयोग गोपनीयता को प्रमाणित एवं नियंत्रित करने के लिए |" - }, - "foss": { - "description": "पूर्णतः ओपन कोड जोकि किसी के भी द्वारा लेखा परीक्षित किया जा सके| किसी भी प्रकार के आंकडें या trackers नहीं | आप पूरी तरह नियंत्रण में |", - "title": "निशुल्क एवं ओपन सोर्स" - } - }, "nav": { "welcome": "नमस्कार", "password": "आपका पासवर्ड", @@ -44,8 +23,6 @@ } }, "test_connection": { - "tutorial": "अब आप अपना वॉलेट कनेक्ट कर चुके हैं क्या आप एक ट्यूटोरियल के माध्यम से जाना चाहेंगे?", - "try_tutorial": "इसे अभी आज़माएं", "initializing": "आपका खाता प्रारंभ किया जा रहा है। कृपया प्रतीक्षा करें, इसमें कुछ मिनट लग सकतें है...", "connection_error": "संपर्क त्रुटि", "review_connection_details": "कृपया अपने कनेक्शन विवरण की समीक्षा करें।", @@ -62,6 +39,10 @@ }, "choose_connector": { "description": "आपको पहले एक लाइटनिंग वॉलेट से कनेक्ट करने की आवश्यकता है ताकि आप अपनी पसंदीदा वेबसाइटों से बातचीत कर सकें जो बिटकॉइन लाइटनिंग भुगतान स्वीकार करते हैं!" + }, + "choose_path": { + "title": "जुड़ें", + "description": "ऑनलाइन भुगतान करने के लिए एल्बी का उपयोग शुरू करने के लिए, अपने लाइटनिंग वॉलेट को एक्सटेंशन से कनेक्ट करें।" } } } diff --git a/src/i18n/locales/it/translation.json b/src/i18n/locales/it/translation.json index 6db66e0f03..c9745d086f 100644 --- a/src/i18n/locales/it/translation.json +++ b/src/i18n/locales/it/translation.json @@ -4,33 +4,12 @@ "nav": { "welcome": "Benvenuto", "password": "La tua Password", - "connect": "Il tuo account Lightning", + "connect": "Il tuo Account Lightning", "done": "Fatto" }, - "intro": { - "send": { - "title": "Invia in un Click", - "description": "Le transazioni Lightning avvengono direttamente nel tuo browser. Niente più alt+tab o scansioni di QR necessari." - }, - "paywall": { - "title": "Niente più fastidiosi paywall", - "description": "Definisci budget individuali per creare flussi di pagamento ai siti web. Basta noiosi paywalls." - }, - "privacy": { - "title": "La privacy prima di tutto", - "description": "Usa Lightning per autenticarti e mantenere la tua privacy." - }, - "foss": { - "title": "Gratuito e Open Source", - "description": "Codice completamente aperto che può essere controllato da chiunque. Nessuna statistica o tracker. Sei tu ad avere il controllo." - }, - "actions": { - "get_started": "Inizia subito" - } - }, "set_password": { "title": "Imposta una password di sblocco", - "description": "I dati del tuo account Lightning sono crittografati in modo sicuro con una password di sblocco. Non dimenticare questa password! Ti serve per sbloccare l'estensione Alby (in questo browser)", + "description": "I dati del tuo account lightning sono crittografati in modo sicuro con una password di sblocco. Non dimenticare questa password! Ti serve per sbloccare l'estensione Alby (in questo browser)", "choose_password": { "label": "Scegli una password di sblocco:" }, @@ -45,8 +24,6 @@ }, "test_connection": { "ready": "Fantastico, sei pronto per partire!", - "tutorial": "Ora che hai collegato il tuo portafoglio ti piacerebbe seguire un tutorial?", - "try_tutorial": "Provalo ora", "initializing": "Stiamo inizializzando il tuo account. Per favore attendi, questa operazione potrebbe impiegare alcuni minuti…", "connection_error": "Errore di connessione", "review_connection_details": "Controlla i dettagli della tua connessione.", @@ -275,24 +252,8 @@ "actions": { "add_account": "Aggiungi account" }, - "edit": { - "title": "Modifica account", - "name": { - "label": "Nome" - }, - "screen_reader": "Modifica nome dell'account" - }, "export": { - "title": "Esporta Account", - "screen_reader": "Esporta i dettagli dell'account", - "waiting": "in attesa dei dati di LndHub...", - "your_ln_address": "Il tuo indirizzo Lightning:", - "tip_mobile": "Suggerimento: Utilizza questo portafoglio con il tuo dispositivo mobile", - "export_uri": "URI delle credenziali di LNDHub", - "scan_qr": "Importa questo portafoglio in Zeus o BlueWallet scansionando il QRCode." - }, - "remove": { - "confirm": "Sei sicuro di voler rimuovere l'account: {{name}}? \nQuesta operazione non può essere annullata. Se hai usato questo account per accedere a siti web, potresti perdere l'accesso a questi ultimi." + "tip_mobile": "Suggerimento: Utilizza questo portafoglio con il tuo dispositivo mobile" } }, "enable": { @@ -409,10 +370,7 @@ "hint": "è un protocollo semplice e aperto che mira a creare reti sociali resistenti alla censura. Nostr funziona con chiavi crittografiche. Per pubblicare qualcosa si firma con la propria chiave e la si invia a più relè. È possibile utilizzare Alby per gestire la propria chiave Nostr. Molte applicazioni Nostr vi permetteranno poi di utilizzare semplicemente la chiave dall'estensione Alby.", "private_key": { "title": "Chiave privata", - "subtitle": "La generazione di una chiave creerà una nuova chiave basata sull'account correntemente utilizzato. Assicurati di fare un backup della chiave privata.", - "generate": "Genera", - "warning": "In questo modo si cancellerà la vecchia chiave privata. Sei sicuro?", - "success": "Chiave privata criptata e salvata con successo." + "subtitle": "La generazione di una chiave creerà una nuova chiave basata sull'account correntemente utilizzato. Assicurati di fare un backup della chiave privata." } } }, @@ -540,8 +498,58 @@ "nostr": { "title": "Nostr", "allow": "Consenti a {{host}} di:", - "read_public_key": "Leggere la propria chiave pubblica", "block_and_ignore": "Blocca e ignora {{host}}" + }, + "alby": { + "pre_connect": { + "create_account": "Crea un nuovo account Alby per inviare e ricevere pagamenti bitcoin.", + "login_account": "Accedi per connettere il tuo account Alby esistente.", + "optional_lightning_note": { + "part4": "Scopri di più", + "part3": ". Si tratta di un semplice modo per fare in modo che chiunque possa inviarti bitcoin sulla rete lightning.", + "part1": "Il tuo account Alby include anche un optional", + "part2": "Indirizzo Lightning" + }, + "forgot_password": "Hai dimenticato la password?", + "title": "Il tuo account Alby", + "host_wallet": "Ospitiamo un portafoglio Lightning per te!", + "email": { + "create": { + "label": "Indirizzo email" + }, + "login": { + "label": "Indirizzo email o indirizzo Lightning" + } + }, + "set_password": { + "choose_password": { + "label": "Password" + }, + "confirm_password": { + "label": "Conferma password" + }, + "errors": { + "enter_password": "Inserisci una password.", + "confirm_password": "Si prega di confermare la password.", + "mismatched_password": "Le password non corrispondono." + } + } + } + }, + "choose_path": { + "title": "Connettiti", + "description": "Per iniziare a utilizzare Alby per effettuare pagamenti online, collega il tuo portafoglio lightning all'estensione.", + "alby": { + "title": "Alby", + "description": "Registrati o utilizza il tuo account Alby per iniziare a pagare attraverso lightning in pochissimo tempo.", + "create_new": "Iscriviti" + }, + "other": { + "title": "Altri portafogli", + "description": "Collegati al tuo portafoglio o nodo Lightning e scegli tra vari connettori.", + "and_more": "& altro...", + "connect": "Connettiti" + } } }, "common": { diff --git a/src/i18n/locales/nl/translation.json b/src/i18n/locales/nl/translation.json index 158b7b828e..827653e19f 100644 --- a/src/i18n/locales/nl/translation.json +++ b/src/i18n/locales/nl/translation.json @@ -7,27 +7,6 @@ "connect": "Jouw Lightning account", "done": "Klaar" }, - "intro": { - "send": { - "title": "Verstuur in één klik", - "description": "Lightning transacties gebeuren in je browser. Geen alt+tab of QR-code scan nodig." - }, - "paywall": { - "title": "Geen vervelende paywalls meer", - "description": "Definieer individuele budgetten voor websites om naadloze betalingsstromen mogelijk te maken. Geen vervelende paywalls meer." - }, - "privacy": { - "title": "Privacy voorop", - "description": "Gebruik Lightning om te verifiëren en controle te houden over je privacy." - }, - "foss": { - "title": "Gratis en Open Source", - "description": "Volledig transparante open code. Geen statistieken of trackers. Jij hebt de controle." - }, - "actions": { - "get_started": "Begin Nu" - } - }, "set_password": { "title": "Stel een ontgrendelingswachtwoord in", "description": "Lightning-accountgegevens zijn veilig versleuteld met een ontgrendelingswachtwoord. Vergeet dit wachtwoord niet! Je hebt het nodig om de Alby-extensie te ontgrendelen (in deze browser)", @@ -45,8 +24,6 @@ }, "test_connection": { "ready": "Geweldig, klaar om te starten!", - "tutorial": "Nu je Lightning wallet is geconnect, wil je een tutorial doorlopen?", - "try_tutorial": "Probeer nu", "initializing": "Uw account wordt geïnitialiseerd. Even geduld, dit kan even duren...", "connection_error": "Connectie Error", "review_connection_details": "Controleer uw verbindingsgegevens.", @@ -308,24 +285,8 @@ "actions": { "add_account": "" }, - "edit": { - "title": "", - "name": { - "label": "" - }, - "screen_reader": "" - }, "export": { - "title": "", - "screen_reader": "", - "waiting": "", - "your_ln_address": "", - "tip_mobile": "", - "export_uri": "", - "scan_qr": "" - }, - "remove": { - "confirm": "" + "tip_mobile": "" } }, "enable": { @@ -443,10 +404,7 @@ "hint": "", "private_key": { "title": "", - "subtitle": "", - "generate": "", - "warning": "", - "success": "" + "subtitle": "" } } }, @@ -580,19 +538,8 @@ "title": "", "allow": "", "content": "", - "read_public_key": "", "block_and_ignore": "", - "block_added": "", - "confirm_sign_message": { - "remember": { - "label": "" - } - }, - "permissions": { - "decrypt": "", - "encrypt": "", - "allow_sign": "" - } + "block_added": "" } }, "common": { diff --git a/src/i18n/locales/pt_BR/translation.json b/src/i18n/locales/pt_BR/translation.json index 3aae9cd8cc..a3fec13975 100644 --- a/src/i18n/locales/pt_BR/translation.json +++ b/src/i18n/locales/pt_BR/translation.json @@ -4,33 +4,12 @@ "nav": { "welcome": "Bem-vindo", "password": "Senha", - "connect": "Sua conta", + "connect": "Sua Conta", "done": "Feito" }, - "intro": { - "send": { - "title": "Clicou pagou", - "description": "Realize pagamentos enquanto você navega na web" - }, - "paywall": { - "title": "Chega de paywalls irritantes", - "description": "Habilite pagamentos automáticos e estabeleça limites de gastos personalizados para cada site." - }, - "privacy": { - "title": "Privacidade", - "description": "Use a rede relâmpago do bitcoin para autenticar em sites e tenha controle sobre sua privacidade." - }, - "foss": { - "title": "Gratuito e de código aberto", - "description": "Código completamente aberto e que pode ser auditado por qualquer pessoa. Sem estatísticas ou rastreadores. Você está no controle." - }, - "actions": { - "get_started": "Começar" - } - }, "set_password": { "title": "Definir uma senha de desbloqueio", - "description": "Os dados armazenados na extensão Alby são criptografados com uma senha de desbloqueio. Não esqueça esta senha! Você precisa dela para usar a extensão Alby (neste navegador)", + "description": "Essa senha é usada para proteger sua carteira e fornecer acesso à extensão do navegador. Não esqueça essa senha pois não é possível recuperá-la. Obs. Essa senha é diferente da senha da conta da Alby.", "choose_password": { "label": "Escolha uma senha de desbloqueio:" }, @@ -45,8 +24,6 @@ }, "test_connection": { "ready": "Perfeito, tudo configurado!", - "tutorial": "Agora que você já conectou a carteira, gostaria de ler o tutorial?", - "try_tutorial": "Experimente agora", "initializing": "Iniciando sua conta. Aguarde, pode demorar um pouco...", "connection_error": "Erro de conexão", "review_connection_details": "Revise os detalhes da conexão.", @@ -55,10 +32,11 @@ "actions": { "delete_edit_account": "Remover conta inválida e editar novamente" } - } + }, + "title": "Bem-vindo a Alby" }, "choose_connector": { - "description": "Você precisa primeiro conectar-se a uma conta ou servidor para poder interagir com seus sites favoritos que aceitam pagamentos em bitcoin!", + "description": "Escolha uma carteira ou servidor disponível abaixo", "lnd": { "title": "LND", "page": { @@ -138,7 +116,7 @@ "title": "myNode", "page": { "title": "Conecte na <0>myNode", - "instructions": "No myNode clique em <0>Wallet botão para seu serviço <0>Lightning.<1/> Agora clique em <0>Pair Wallet botão abaixo da tab <0>Status. Insira sua senha quando solicitada.<1/> Selecione o menu suspenso e escolha uma opção de emparelhamento. Dependendo da sua configuração você pode escolher <0>Conexão Lightning (REST + Local IP) ou conexão <0>Lightning (REST + Tor)." + "instructions": "No myNode clique em <0>Wallet botão para seu serviço <0>lightning.<1/> Agora clique em <0>Pair Wallet botão abaixo da tab <0>Status. Insira sua senha quando solicitada.<1/> Selecione o menu suspenso e escolha uma opção de emparelhamento. Dependendo da sua configuração você pode escolher <0>Conexão lightning (REST + Local IP) ou conexão <0>lightning (REST + Tor)." }, "rest_url": { "label": "lndconnect REST URL", @@ -149,7 +127,7 @@ "title": "Start9", "page": { "title": "Conecte no seu servidor <0>Embassy", - "instructions": "<0>Note: Atualmente somente a LND é suportada mas nós adicionaremos suporte para a c-lightning no futuro!<1/>No seu Embassy clique em service <0>Lightning Network Daemon.<1/>Selecione a tab <0>Properties.<1/>Agora copie o <0>LND Connect REST URL." + "instructions": "<0>Note: Atualmente somente a LND é suportada mas nós adicionaremos suporte para a c-lightning no futuro!<1/>No seu Embassy clique em service <0>lightning network Daemon.<1/>Selecione a tab <0>Properties.<1/>Agora copie o <0>LND Connect REST URL." }, "rest_url": { "label": "lndconnect REST URL", @@ -246,7 +224,7 @@ "commando": { "page": { "title": "Conecte no servidor Core Lightning", - "instructions": "Certifique-se de conectar em um servidor Core Lightning que esteja na versão 0.12.0 ou mais recente, o plugin commando instalado e rodando e o servidor acessível na Rede Relâmpago. Crie um rune executando o comando 'lightning-cli commando-rune'." + "instructions": "Certifique-se de conectar em um servidor Core Lightning que esteja na versão 0.12.0 ou mais recente, o plugin commando instalado e rodando e o servidor acessível na rede relâmpago. Crie um rune executando o comando 'lightning-cli commando-rune'." }, "title": "Core Lightning", "pubkey": { @@ -274,6 +252,10 @@ "errors": { "connection_failed": "Falha na conexão. O servidor Core Lightning está online e usando o plugin comando?" } + }, + "title": "Conectar Carteira Bitcoin", + "kollider": { + "title": "Kollider" } }, "home": { @@ -306,24 +288,8 @@ "actions": { "add_account": "Adicionar conta" }, - "edit": { - "title": "Editar Conta", - "name": { - "label": "Nome" - }, - "screen_reader": "Editar nome da conta" - }, "export": { - "title": "Exportar Conta", - "screen_reader": "Exportar detalhes da conta", - "waiting": "aguardando dados LndHub...", - "your_ln_address": "Seu Endereço Relâmpago:", - "tip_mobile": "Dica: Use esta carteira no seu dispositivo móvel", - "export_uri": "Credenciais URI LNDHub", - "scan_qr": "Leia o Código QR para importar esta carteira na Zeus ou BlueWallet." - }, - "remove": { - "confirm": "Tem certeza de que deseja remover a conta: {{name}}? \nEsta ação não pode ser desfeita. Se você usou essa conta para logar em sites, poderá perder o acesso a eles." + "tip_mobile": "Dica: Use esta carteira no seu dispositivo móvel" } }, "enable": { @@ -396,7 +362,7 @@ }, "exchange": { "title": "Serviço provedor de cotações", - "subtitle": "Escolha o serviço que fornecerá as cotações do Bitcoin exibidas na Alby" + "subtitle": "Escolha o serviço que fornecerá as cotações do bitcoin exibidas na Alby" }, "personal_data": { "title": "Dados pessoais", @@ -439,11 +405,8 @@ "nostr": { "title": "Nostr", "private_key": { - "subtitle": "Ao gerar uma chave, ela será criada a partir da conta atual que você usa. Certifique-se de fazer backup desta chave privada. <0>Saiba mais »", - "warning": "Isso excluirá sua chave privada antiga. Tem certeza de que deseja prosseguir?", - "title": "Chave privada", - "generate": "Gerar", - "success": "A chave privada foi encriptada e salva com sucesso." + "subtitle": "Cole ou gere uma nova chave privada. Ao gerar uma chave, ela será criada a partir da conta atual que você usa. Certifique-se de fazer backup desta chave privada. <0>Saiba mais »", + "title": "Chave privada" }, "hint": "é um protocolo simples e aberto que visa criar redes sociais resistentes à censura. Os perfis na rede Nostr são chaves criptográficas. Para publicar algo, você assina com sua chave e a informação é enviada para servidores. Na Alby você pode usar e gerenciar seu perfil em diferentes aplicações que suportam esse recurso." } @@ -498,7 +461,11 @@ "title": "Limite de gastos", "used_budget": "sats usados" } - } + }, + "title": "Sua lista de Sites ⚡️", + "description": "Sites onde você já usou a Alby", + "no_info": "Nenhum site ainda.", + "discover": "Descubra Sites" }, "make_invoice": { "title": "Gerar Fatura", @@ -539,7 +506,7 @@ "errors": { "status": "Erro: O status de autenticação não está correto" }, - "submit": "Logar", + "submit": "Fazer login", "title": "Autenticação" }, "lnurlwithdraw": { @@ -572,25 +539,112 @@ "nostr": { "allow": "Permitir este site:", "block_and_ignore": "Bloquear e ignorar {{host}}", - "read_public_key": "Ler sua chave pública", "title": "Nostr", - "confirm_sign_message": { - "remember": { - "label": "Lembrar minha escolha e não perguntar novamente" - } - }, "content": "Este site solicita que você assine:", - "permissions": { - "decrypt": "Descriptografar dados", - "encrypt": "Criptografar dados", - "allow_sign": "Permitir {{host}} assinar:" - }, - "block_added": "{{host}} adicionado na lista de bloqueio, por favor recarregue o site." + "block_added": "{{host}} adicionado na lista de bloqueio, por favor recarregue o site.", + "allow_sign": "Permitir {{host}} assinar:" }, "confirm_request_permission": { "title": "Aprovar Solicitação", "allow": "Permitir este site executar:", "always_allow": "Lembrar minha escolha e não perguntar novamente" + }, + "discover": { + "title": "Explore o ecossistema ⚡️", + "description": "Sites onde você pode usar a Alby", + "list": { + "trading": "Negociação", + "gaming": "Jogos", + "entertaiment": "Entretenimento", + "shopping": "Compras", + "miscellaneous": "Outros", + "nostr": "Nostr" + }, + "tips": { + "top_up_wallet": { + "description": "Crie uma fatura relâmpago, envie alguns bitcoins para você mesmo e comece a usar a Alby", + "label2": "Comprar Bitcoin", + "label1": "Receber Bitcoin", + "title": "⚡️ Adicione fundos na sua carteira" + }, + "pin": { + "title": "📌 Fixar extensão Alby", + "description1": "<0>Para acessar a Alby facilmente, clique no ícone da extensão <1/> <2>no canto superior direito do seu navegador", + "description2": "Na lista de extensões, localize a Alby e clique no ícone de fixação para fixá-lo na barra de ferramentas do navegador", + "description3": "É isso! Agora é só clicar no ícone da Alby" + }, + "address": { + "label1": "Eu quero!", + "title": "⚡️ Endereço relâmpago", + "description": "Crie seu próprio endereço relâmpago e receba pagamentos bitcoin com a mesma facilidade de enviar um e-mail" + }, + "demo": { + "title": "🕹️ Teste a Alby", + "description": "Descubra tudo o que você pode fazer com a Alby em nosso site de demonstração", + "label1": "Experimente" + }, + "title": "Sua carteira Alby está pronta", + "description": "Algumas dicas para você iniciar na Alby 🐝" + } + }, + "choose_path": { + "alby": { + "description": "Crie uma nova conta Alby ou faça o login na sua conta existente.", + "create_new": "Criar nova conta", + "title": "Conta Alby" + }, + "other": { + "title": "Outras Contas", + "and_more": "& mais...", + "description": "Conecte-se em sua carteira bitcoin ou em um dos vários servidores de carteira disponíveis.", + "connect": "Conectar" + }, + "description": "Você pode usar Alby criando uma conta conosco ou conectando-se nas carteiras bitcoin disponíveis.", + "title": "Como você deseja usar a Alby?" + }, + "alby": { + "pre_connect": { + "title": "Sua conta Alby", + "set_password": { + "errors": { + "enter_password": "Insira uma senha.", + "confirm_password": "Confirme sua senha.", + "mismatched_password": "As senhas não correspondem." + }, + "confirm_password": { + "label": "Confirmar Senha" + }, + "choose_password": { + "label": "Senha" + } + }, + "login_account": "Acesse sua conta Alby.", + "host_wallet": "A custódia dos seus bitcoins é por nossa conta!", + "create_account": "Crie uma nova conta Alby para enviar e receber pagamentos em bitcoin.", + "email": { + "create": { + "label": "Endereço de E-mail" + }, + "login": { + "label": "Endereço de E-mail ou Endereço Relâmpago" + } + }, + "forgot_password": "Esqueceu sua senha?", + "optional_lightning_note": { + "part4": "saiba mais", + "part3": ". É o jeito mais simples para você receber bitcoin na rede relâmpago.", + "part2": "endereço relâmpago", + "part1": "Ao criar uma conta Alby, você pode escolher um" + }, + "optional_lightning_address": { + "title": "números e letras, pelo menos 3 caracteres", + "suffix": "@getalby.com", + "label": "Escolha um Endereço Relâmpago (opcional)" + }, + "errors": { + "create_wallet_error": "Falha ao logar ou criar uma nova conta. Se precisar de ajuda, entre em contato com support@getalby.com" + } + } } }, "common": { @@ -624,7 +678,9 @@ "export": "Exportar", "remove": "Remover", "copy": "Copiar", - "back": "Voltar" + "back": "Voltar", + "log_in": "Fazer login", + "remember": "Lembrar minha escolha e não perguntar novamente" }, "errors": { "connection_failed": "Falha na conexão", @@ -637,7 +693,8 @@ "response": "Resposta", "success_message": "{{amount}}{{fiatAmount}} enviados para {{destination}}", "advanced": "Avançado", - "discover": "Explorar" + "discover": "Explorar", + "confirm_password": "Confirmar Senha" }, "components": { "allowance_menu": { @@ -653,7 +710,8 @@ "enable_login": { "title": "Login Relâmpago", "subtitle": "O login é realizado de forma automática, sem necessidade de confirmação." - } + }, + "edit_permissions": "Editar Permissões" }, "qrcode_scanner": { "title": "Ler Código QR", @@ -676,7 +734,7 @@ "app": "App", "podcast": "Podcast" }, - "open_location": "Abrir site" + "open_location": "Abrir link" }, "confirm_or_cancel": { "only_trusted": "Conecte-se apenas nos sites em que você confia." @@ -729,5 +787,13 @@ "auth": "LOGIN RELÂMPAGO" } } + }, + "permissions": { + "nostr": { + "getpublickey": "Ler sua chave pública.", + "signmessage": "Assinar mensagem com sua chave.", + "nip04decrypt": "Descriptografar dados.", + "nip04encrypt": "Criptografar dados." + } } } diff --git a/src/i18n/locales/sv/translation.json b/src/i18n/locales/sv/translation.json index bff474c880..e06bf5680c 100644 --- a/src/i18n/locales/sv/translation.json +++ b/src/i18n/locales/sv/translation.json @@ -7,27 +7,6 @@ "connect": "Ditt Lightning-konto", "done": "Klar" }, - "intro": { - "send": { - "title": "Skicka med ett klick", - "description": "Lightning-överföringar sker direkt i din webbläsare. Inget behov av att alt-tabba eller skanna QR-koder." - }, - "paywall": { - "title": "Inga fler irriterande betalväggar", - "description": "Ange beloppsgränser för specifika webbplatser för att förenkla betalning. Inga fler irriterande betalväggar." - }, - "privacy": { - "title": "Integritet främst", - "description": "Använd lightning för autentisering och behåll din integritet." - }, - "foss": { - "title": "Fri och öppen källkod", - "description": "Helt öppen källkod som kan granskas och verifieras av vem som helst. Ingen statistik eller spår. Du har full kontroll." - }, - "actions": { - "get_started": "Kom igång" - } - }, "set_password": { "title": "Ange ett lösenord för upplåsning", "description": "Ditt Lightning-konto är krypterat med ett lösenord för upplåsning. Glöm inte detta lösenord! Du behöver lösenordet för att kunna låsa upp Alby-tillägget (i denna webbläsare)", @@ -45,8 +24,6 @@ }, "test_connection": { "ready": "Fantastiskt, du är redo att börja!", - "tutorial": "Nu när du anslutit din plånbok, vill du gå igenom en guide?", - "try_tutorial": "Prova genast", "initializing": "Initialiserar ditt konto. Vänta, det kan ta någon minut...", "connection_error": "Anslutningsfel", "review_connection_details": "Se över dina anslutningsinställningar.", @@ -275,24 +252,8 @@ "actions": { "add_account": "Lägg till konto" }, - "edit": { - "title": "Redigera konto", - "name": { - "label": "Namn" - }, - "screen_reader": "Redigera namn på konto" - }, "export": { - "title": "Exportera Konto", - "screen_reader": "Exportera kontodetaljer", - "waiting": "väntar på LndHub- data...", - "your_ln_address": "Din Lightning-adress:", - "tip_mobile": "Tips: Använd denna plånbok på din mobila enhet", - "export_uri": "LNDHub Credentials URI", - "scan_qr": "Importera denna plånbok till Zeus eller BlueWallet genom att skanna QR-kod." - }, - "remove": { - "confirm": "Är du säker på att du vill radera kontot: {{name}}?\nDetta kan inte ångras. Om du loggat in på webbplatser med kontot kan du förlora åtkomst till dessa." + "tip_mobile": "Tips: Använd denna plånbok på din mobila enhet" } }, "enable": { @@ -409,10 +370,7 @@ "hint": "är ett enkelt och öppet protokoll som syftar till att skapa sociala nätverk som är motståndskraftiga mot censur. Nostr använder kryptografiska nycklar. För att publicera något signerar du med dina nycklar som skickas till multipla reläer. Du kan använda Alby för att hantera din Nostr-nyckel. Flertalet Nostr-applikationer tillåter dig att använda nyckeln med hjälp av Alby-tillägget.", "private_key": { "title": "Privat nyckel", - "subtitle": "Generering av nyckel skapar en ny nyckel baserat på det konto du använder för tillfället. Se till att säkerhetskopiera den privata nyckeln.", - "generate": "Skapa", - "warning": "Detta raderar din gamla privata nyckel. Är du säker?", - "success": "Privat nyckel krypterad och sparad." + "subtitle": "Generering av nyckel skapar en ny nyckel baserat på det konto du använder för tillfället. Se till att säkerhetskopiera den privata nyckeln." } } }, @@ -540,7 +498,6 @@ "nostr": { "title": "Nostr", "allow": "Tillåt {{host}} att:", - "read_public_key": "Visa din publika nyckel", "block_and_ignore": "Blockera och ignorera {{host}}" } }, diff --git a/src/i18n/locales/tl/translation.json b/src/i18n/locales/tl/translation.json index dcb61a33a6..95ab9a8360 100644 --- a/src/i18n/locales/tl/translation.json +++ b/src/i18n/locales/tl/translation.json @@ -7,27 +7,6 @@ "connect": "", "done": "" }, - "intro": { - "send": { - "title": "", - "description": "" - }, - "paywall": { - "title": "", - "description": "" - }, - "privacy": { - "title": "", - "description": "" - }, - "foss": { - "title": "", - "description": "" - }, - "actions": { - "get_started": "" - } - }, "set_password": { "title": "", "description": "", @@ -45,8 +24,6 @@ }, "test_connection": { "ready": "", - "tutorial": "", - "try_tutorial": "", "initializing": "", "connection_error": "", "review_connection_details": "", @@ -275,24 +252,8 @@ "actions": { "add_account": "" }, - "edit": { - "title": "", - "name": { - "label": "" - }, - "screen_reader": "" - }, "export": { - "title": "", - "screen_reader": "", - "waiting": "", - "your_ln_address": "", - "tip_mobile": "", - "export_uri": "", - "scan_qr": "" - }, - "remove": { - "confirm": "" + "tip_mobile": "" } }, "enable": { diff --git a/src/i18n/locales/zh_Hans/translation.json b/src/i18n/locales/zh_Hans/translation.json index 12ac685c84..83218cc5c5 100644 --- a/src/i18n/locales/zh_Hans/translation.json +++ b/src/i18n/locales/zh_Hans/translation.json @@ -7,27 +7,6 @@ "done": "已完成", "connect": "你的闪电账户" }, - "intro": { - "paywall": { - "title": "不再有恼人的付费墙", - "description": "为网站定义个人预算,实现无缝支付流。不再有恼人的付费墙。" - }, - "privacy": { - "description": "用闪电认证并控制你的隐私。", - "title": "隐私第一" - }, - "actions": { - "get_started": "开始" - }, - "foss": { - "title": "自由开放源码软件", - "description": "完全开放的代码,任何人都可以进行审计。没有数据统计或跟踪器。一切由你掌控。" - }, - "send": { - "title": "一键发送", - "description": "闪电交易全部发生在你的浏览器中。不需要alt+tab或扫描二维码。" - } - }, "set_password": { "title": "设置解锁密码", "description": "你的闪电账户数据是用解锁密码安全加密的。不要忘记这个密码! 你需要它来解锁Alby Extension(在这个浏览器中)", @@ -45,17 +24,16 @@ }, "test_connection": { "ready": "真棒,你已准备就绪!", - "try_tutorial": "现在就试试看", "initializing": "正在初始化你的账户。请耐心等待,可能需要几分钟。。。", "connection_error": "连接错误", - "tutorial": "现在你已经连接了你的钱包,你想通过教程吗?", "review_connection_details": "请检查你的连接详情。", "connection_taking_long": "尝试连接花费的时间不预期的要长。。。你的详情正确吗?你的节点是否可达?", "contact_support": "如果你需要帮助,请联系support@getalby.com", "actions": { "delete_edit_account": "删除无效账户并重新编辑" } - } + }, + "title": "欢迎使用Alby" }, "choose_connector": { "lnd": { @@ -303,26 +281,10 @@ } }, "accounts": { - "edit": { - "title": "编辑账户", - "name": { - "label": "名称" - }, - "screen_reader": "编辑账户名称" - }, "export": { - "waiting": "正在等待 LndHub 数据...", - "title": "导出账户", - "your_ln_address": "你的闪电地址:", - "tip_mobile": "提示:用你的移动设备使用这个钱包", - "export_uri": "LNDHub凭证URI", - "scan_qr": "通过扫描二维码将这个钱包导入Zeus或BlueWallet。", - "screen_reader": "导出帐户详情" + "export_uri": "LNDHub凭证URI" }, "title": "账户", - "remove": { - "confirm": "是否确定要删除帐户:{{name}}?\n这是无法撤消的。如果你使用此账户登录网站,则可能无法访问这些网站。" - }, "actions": { "add_account": "添加账户" } @@ -354,9 +316,6 @@ "nostr": { "private_key": { "title": "私钥", - "generate": "生成", - "warning": "这将删除你的旧私钥。是否确定?", - "success": "私钥已加密并成功保存。", "subtitle": "生成密钥将根据你当前使用的帐户创建一个新密钥。请确保备份此私钥。" }, "title": "Nostr", @@ -538,18 +497,7 @@ "nostr": { "title": "Nostr", "allow": "允许{{host}}进行:", - "read_public_key": "读取你的公钥", "block_and_ignore": "阻止并忽略 {{host}}", - "confirm_sign_message": { - "remember": { - "label": "记住,不要再问" - } - }, - "permissions": { - "allow_sign": "允许{{host}}签署:", - "decrypt": "解密数据", - "encrypt": "加密数据" - }, "content": "{{host}} 要求你签署:" }, "lnurlpay": { @@ -580,6 +528,17 @@ "payment_summary": { "description": "付款至: {{destination}}" } + }, + "choose_path": { + "title": "连接", + "description": "要使用Alby进行在线支付,请将Alby连接到你的闪电网络钱包。", + "other": { + "title": "其他钱包" + }, + "alby": { + "description": "注册或使用您现有的Alby账户,立即开始闪电支付。", + "create_new": "注册" + } } }, "common": { diff --git a/src/i18next.d.ts b/src/i18next.d.ts index cd85ae5d6a..43bf6f716e 100644 --- a/src/i18next.d.ts +++ b/src/i18next.d.ts @@ -3,6 +3,6 @@ import { resources, defaultNS } from "./i18n/i18nConfig"; declare module "i18next" { interface CustomTypeOptions { defaultNS: typeof defaultNS; - resources: typeof resources["en"]; + resources: (typeof resources)["en"]; } } diff --git a/src/manifest.json b/src/manifest.json index e37e9eef73..1803a66670 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -50,9 +50,9 @@ ], "__chrome__host_permissions": ["*://*/*"], - "content_security_policy": "script-src 'self'; object-src 'self'", + "content_security_policy": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'", "__chrome__content_security_policy": { - "extension_pages": "script-src 'self'; object-src 'self'" + "extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'" }, "__chrome|firefox__author": "Alby", diff --git a/src/types.ts b/src/types.ts index 3173a5ac19..788478993b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,5 @@ import { PaymentRequestObject } from "bolt11"; -import { CURRENCIES, ACCOUNT_CURRENCIES } from "~/common/constants"; +import { ACCOUNT_CURRENCIES, CURRENCIES, TIPS } from "~/common/constants"; import connectors from "~/extension/background-script/connectors"; import { ConnectorInvoice, @@ -16,6 +16,7 @@ export interface Account { connector: ConnectorType; config: string; name: string; + nostrPrivateKey?: string | null; } export interface Accounts { @@ -166,6 +167,11 @@ export interface MessagePaymentAll extends MessageDefault { }; } +export interface MessageAccountGet extends MessageDefault { + args?: { id?: Account["id"] }; + action: "getAccount"; +} + export interface MessageAccountRemove extends MessageDefault { args?: { id: Account["id"] }; action: "removeAccount"; @@ -372,8 +378,23 @@ export interface MessagePublicKeyGet extends MessageDefault { action: "getPublicKeyOrPrompt"; } +export interface MessagePrivateKeyGet extends MessageDefault { + args?: { + id?: Account["id"]; + }; + action: "getPrivateKey"; +} + +export interface MessagePrivateKeyGenerate extends MessageDefault { + args?: { + type?: "random"; + }; + action: "generatePrivateKey"; +} + export interface MessagePrivateKeySet extends MessageDefault { args: { + id?: Account["id"]; privateKey: string; }; action: "setPrivateKey"; @@ -627,6 +648,7 @@ export interface SettingsStorage { exchange: SupportedExchanges; debug: boolean; nostrEnabled: boolean; + closedTips: TIPS[]; } export interface Badge { @@ -681,3 +703,5 @@ export interface Invoice { value_msat_total: number; }; } + +export type BrowserType = "chrome" | "firefox"; diff --git a/static/assets/icons/puzzle.svg b/static/assets/icons/puzzle.svg new file mode 100644 index 0000000000..e044be65a8 --- /dev/null +++ b/static/assets/icons/puzzle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/e2e/001-createWallets.spec.ts b/tests/e2e/001-createWallets.spec.ts index dcf793c72b..7853175225 100644 --- a/tests/e2e/001-createWallets.spec.ts +++ b/tests/e2e/001-createWallets.spec.ts @@ -28,17 +28,8 @@ const commonCreateWalletUserCreate = async ( }> => { const { page, browser } = await loadExtension(); - // get document from welcome page + // get document from onboard page const $document = await getDocument(page); - - // go through welcome page - const startedButton = await getByText($document, "Get Started"); - startedButton.click(); - - await Promise.all([ - page.waitForNavigation(), // The promise resolves after navigation has finished - ]); - await findByText($document, "Set an unlock password"); // type user password and confirm password @@ -86,14 +77,19 @@ const commonCreateWalletUserCreate = async ( const commonCreateWalletSuccessCheck = async ({ page, $document }) => { // submit form const continueButton = await findByText($document, "Continue"); - continueButton.click(); + continueButton.click(), + // options.html + await Promise.all([ + page.waitForNavigation(), // The promise resolves after navigation has finished + ]); + // options.html#publishers await Promise.all([ - page.waitForResponse(() => true), page.waitForNavigation(), // The promise resolves after navigation has finished ]); - await findByText($document, "Success", undefined, { timeout: 15000 }); + const $optionsdocument = await getDocument(page); + await findByText($optionsdocument, "Explore the Lightning ⚡️ Ecosystem"); }; test.describe("Create or connect wallets", () => { @@ -226,7 +222,6 @@ test.describe("Create or connect wallets", () => { await runeField.type(rune); await commonCreateWalletSuccessCheck({ page, $document }); - await browser.close(); }); diff --git a/tests/e2e/002-walletFeatures.spec.ts b/tests/e2e/002-walletFeatures.spec.ts index e4ecdae1e5..094299782d 100644 --- a/tests/e2e/002-walletFeatures.spec.ts +++ b/tests/e2e/002-walletFeatures.spec.ts @@ -39,7 +39,7 @@ test.describe("Wallet features", () => { extensionId, }); - await findByText($optionsdocument, "Your ⚡️ Websites"); + await findByText($optionsdocument, "Your ⚡ Websites"); await browser.close(); }); diff --git a/yarn.lock b/yarn.lock index 5fb6701b34..60dc9740ba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -318,13 +318,6 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.14.5", "@babel/runtime@^7.9.2": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72" - integrity sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg== - dependencies: - regenerator-runtime "^0.13.4" - "@babel/runtime@^7.19.4": version "7.20.1" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.1.tgz#1148bb33ab252b165a06698fde7576092a78b4a9" @@ -339,6 +332,13 @@ dependencies: regenerator-runtime "^0.13.11" +"@babel/runtime@^7.9.2": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72" + integrity sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.16.7", "@babel/template@^7.18.6", "@babel/template@^7.3.3": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.6.tgz#1283f4993e00b929d6e2d3c72fdc9168a2977a31" @@ -406,92 +406,92 @@ resolved "https://registry.yarnpkg.com/@bitcoin-design/bitcoin-icons-react/-/bitcoin-icons-react-0.1.9.tgz#25c18808f167e242cd15ba27c185d785f2728980" integrity sha512-nJvTD1+zG/ffHdMeGQ39vdsmEFo9WcCIP1RlR7ZpZoP2H+IgKwzwow8VSY6ebroLoCT7WWtUPJQSbgQwgWYrFg== -"@commitlint/cli@^17.3.0": - version "17.3.0" - resolved "https://registry.yarnpkg.com/@commitlint/cli/-/cli-17.3.0.tgz#d8497f03e27a5161178e802168d77de2941959a0" - integrity sha512-/H0md7TsKflKzVPz226VfXzVafJFO1f9+r2KcFvmBu08V0T56lZU1s8WL7/xlxqLMqBTVaBf7Ixtc4bskdEEZg== +"@commitlint/cli@^17.4.1": + version "17.4.1" + resolved "https://registry.yarnpkg.com/@commitlint/cli/-/cli-17.4.1.tgz#71086653a42abafe5519c6c5f8ada35b17f1dc53" + integrity sha512-W8OJwz+izY+fVwyUt1HveCDmABMZNRVZHSVPw/Bh9Y62tp11SmmQaycgbsYLMiMy7JGn4mAJqEGlSHS9Uti9ZQ== dependencies: - "@commitlint/format" "^17.0.0" - "@commitlint/lint" "^17.3.0" - "@commitlint/load" "^17.3.0" - "@commitlint/read" "^17.2.0" - "@commitlint/types" "^17.0.0" + "@commitlint/format" "^17.4.0" + "@commitlint/lint" "^17.4.0" + "@commitlint/load" "^17.4.1" + "@commitlint/read" "^17.4.0" + "@commitlint/types" "^17.4.0" execa "^5.0.0" lodash.isfunction "^3.0.9" resolve-from "5.0.0" resolve-global "1.0.0" yargs "^17.0.0" -"@commitlint/config-conventional@^17.3.0": - version "17.3.0" - resolved "https://registry.yarnpkg.com/@commitlint/config-conventional/-/config-conventional-17.3.0.tgz#77bcfabfed932bc80e97f31f2201ba05f504e145" - integrity sha512-hgI+fN5xF8nhS9uG/V06xyT0nlcyvHHMkq0kwRSr96vl5BFlRGaL2C0/YY4kQagfU087tmj01bJkG9Ek98Wllw== +"@commitlint/config-conventional@^17.4.2": + version "17.4.2" + resolved "https://registry.yarnpkg.com/@commitlint/config-conventional/-/config-conventional-17.4.2.tgz#671f7febfcfef90ec11b122a659c6be25e11c19e" + integrity sha512-JVo1moSj5eDMoql159q8zKCU8lkOhQ+b23Vl3LVVrS6PXDLQIELnJ34ChQmFVbBdSSRNAbbXnRDhosFU+wnuHw== dependencies: conventional-changelog-conventionalcommits "^5.0.0" -"@commitlint/config-validator@^17.1.0": - version "17.1.0" - resolved "https://registry.yarnpkg.com/@commitlint/config-validator/-/config-validator-17.1.0.tgz#51d09ca53d7a0d19736abf34eb18a66efce0f97a" - integrity sha512-Q1rRRSU09ngrTgeTXHq6ePJs2KrI+axPTgkNYDWSJIuS1Op4w3J30vUfSXjwn5YEJHklK3fSqWNHmBhmTR7Vdg== +"@commitlint/config-validator@^17.4.0": + version "17.4.0" + resolved "https://registry.yarnpkg.com/@commitlint/config-validator/-/config-validator-17.4.0.tgz#2cb229672a22476cf1f21bedbfcd788e5da5b54f" + integrity sha512-Sa/+8KNpDXz4zT4bVbz2fpFjvgkPO6u2V2fP4TKgt6FjmOw2z3eEX859vtfeaTav/ukBw0/0jr+5ZTZp9zCBhA== dependencies: - "@commitlint/types" "^17.0.0" + "@commitlint/types" "^17.4.0" ajv "^8.11.0" -"@commitlint/ensure@^17.3.0": - version "17.3.0" - resolved "https://registry.yarnpkg.com/@commitlint/ensure/-/ensure-17.3.0.tgz#d7bb60291a254152b468ccb2be8c0dc79667247e" - integrity sha512-kWbrQHDoW5veIUQx30gXoLOCjWvwC6OOEofhPCLl5ytRPBDAQObMbxTha1Bt2aSyNE/IrJ0s0xkdZ1Gi3wJwQg== +"@commitlint/ensure@^17.4.0": + version "17.4.0" + resolved "https://registry.yarnpkg.com/@commitlint/ensure/-/ensure-17.4.0.tgz#3de65768bfccb9956ec3a0ecd8a415421bf315e5" + integrity sha512-7oAxt25je0jeQ/E0O/M8L3ADb1Cvweu/5lc/kYF8g/kXatI0wxGE5La52onnAUAWeWlsuvBNar15WcrmDmr5Mw== dependencies: - "@commitlint/types" "^17.0.0" + "@commitlint/types" "^17.4.0" lodash.camelcase "^4.3.0" lodash.kebabcase "^4.1.1" lodash.snakecase "^4.1.1" lodash.startcase "^4.4.0" lodash.upperfirst "^4.3.1" -"@commitlint/execute-rule@^17.0.0": - version "17.0.0" - resolved "https://registry.yarnpkg.com/@commitlint/execute-rule/-/execute-rule-17.0.0.tgz#186e9261fd36733922ae617497888c4bdb6e5c92" - integrity sha512-nVjL/w/zuqjCqSJm8UfpNaw66V9WzuJtQvEnCrK4jDw6qKTmZB+1JQ8m6BQVZbNBcwfYdDNKnhIhqI0Rk7lgpQ== +"@commitlint/execute-rule@^17.4.0": + version "17.4.0" + resolved "https://registry.yarnpkg.com/@commitlint/execute-rule/-/execute-rule-17.4.0.tgz#4518e77958893d0a5835babe65bf87e2638f6939" + integrity sha512-LIgYXuCSO5Gvtc0t9bebAMSwd68ewzmqLypqI2Kke1rqOqqDbMpYcYfoPfFlv9eyLIh4jocHWwCK5FS7z9icUA== -"@commitlint/format@^17.0.0": - version "17.0.0" - resolved "https://registry.yarnpkg.com/@commitlint/format/-/format-17.0.0.tgz#2c991ac0df3955fe5d7d4d733967bd17e6cfd9e0" - integrity sha512-MZzJv7rBp/r6ZQJDEodoZvdRM0vXu1PfQvMTNWFb8jFraxnISMTnPBWMMjr2G/puoMashwaNM//fl7j8gGV5lA== +"@commitlint/format@^17.4.0": + version "17.4.0" + resolved "https://registry.yarnpkg.com/@commitlint/format/-/format-17.4.0.tgz#1c80cf3a6274ff9b3d3c0dd150a97882d557aa0f" + integrity sha512-Z2bWAU5+f1YZh9W76c84J8iLIWIvvm+mzqogTz0Nsc1x6EHW0Z2gI38g5HAjB0r0I3ZjR15IDEJKhsxyblcyhA== dependencies: - "@commitlint/types" "^17.0.0" + "@commitlint/types" "^17.4.0" chalk "^4.1.0" -"@commitlint/is-ignored@^17.2.0": - version "17.2.0" - resolved "https://registry.yarnpkg.com/@commitlint/is-ignored/-/is-ignored-17.2.0.tgz#07c329396e2457fd37e8707f990c3a49731a168d" - integrity sha512-rgUPUQraHxoMLxiE8GK430HA7/R2vXyLcOT4fQooNrZq9ERutNrP6dw3gdKLkq22Nede3+gEHQYUzL4Wu75ndg== - dependencies: - "@commitlint/types" "^17.0.0" - semver "7.3.7" - -"@commitlint/lint@^17.3.0": - version "17.3.0" - resolved "https://registry.yarnpkg.com/@commitlint/lint/-/lint-17.3.0.tgz#16506deaa347d61bd1195b17df1c6809a553d2a0" - integrity sha512-VilOTPg0i9A7CCWM49E9bl5jytfTvfTxf9iwbWAWNjxJ/A5mhPKbm3sHuAdwJ87tDk1k4j8vomYfH23iaY+1Rw== - dependencies: - "@commitlint/is-ignored" "^17.2.0" - "@commitlint/parse" "^17.2.0" - "@commitlint/rules" "^17.3.0" - "@commitlint/types" "^17.0.0" - -"@commitlint/load@^17.3.0": - version "17.3.0" - resolved "https://registry.yarnpkg.com/@commitlint/load/-/load-17.3.0.tgz#ebfec0198dd1627627e32a2b2ae4744d297599a8" - integrity sha512-u/pV6rCAJrCUN+HylBHLzZ4qj1Ew3+eN9GBPhNi9otGxtOfA8b+8nJSxaNbcC23Ins/kcpjGf9zPSVW7628Umw== - dependencies: - "@commitlint/config-validator" "^17.1.0" - "@commitlint/execute-rule" "^17.0.0" - "@commitlint/resolve-extends" "^17.3.0" - "@commitlint/types" "^17.0.0" - "@types/node" "^14.0.0" +"@commitlint/is-ignored@^17.4.2": + version "17.4.2" + resolved "https://registry.yarnpkg.com/@commitlint/is-ignored/-/is-ignored-17.4.2.tgz#2d40a34e071c3e595e485fafe8460457a7b7af9d" + integrity sha512-1b2Y2qJ6n7bHG9K6h8S4lBGUl6kc7mMhJN9gy1SQfUZqe92ToDjUTtgNWb6LbzR1X8Cq4SEus4VU8Z/riEa94Q== + dependencies: + "@commitlint/types" "^17.4.0" + semver "7.3.8" + +"@commitlint/lint@^17.4.0": + version "17.4.2" + resolved "https://registry.yarnpkg.com/@commitlint/lint/-/lint-17.4.2.tgz#1277cb4d5395e9d6c39cbc351984bac9dcc6b7cd" + integrity sha512-HcymabrdBhsDMNzIv146+ZPNBPBK5gMNsVH+el2lCagnYgCi/4ixrHooeVyS64Fgce2K26+MC7OQ4vVH8wQWVw== + dependencies: + "@commitlint/is-ignored" "^17.4.2" + "@commitlint/parse" "^17.4.2" + "@commitlint/rules" "^17.4.2" + "@commitlint/types" "^17.4.0" + +"@commitlint/load@^17.4.1": + version "17.4.2" + resolved "https://registry.yarnpkg.com/@commitlint/load/-/load-17.4.2.tgz#551875c3e1dce6dc0375dc9c8ad551de8ba35de4" + integrity sha512-Si++F85rJ9t4hw6JcOw1i2h0fdpdFQt0YKwjuK4bk9KhFjyFkRxvR3SB2dPaMs+EwWlDrDBGL+ygip1QD6gmPw== + dependencies: + "@commitlint/config-validator" "^17.4.0" + "@commitlint/execute-rule" "^17.4.0" + "@commitlint/resolve-extends" "^17.4.0" + "@commitlint/types" "^17.4.0" + "@types/node" "*" chalk "^4.1.0" - cosmiconfig "^7.0.0" + cosmiconfig "^8.0.0" cosmiconfig-typescript-loader "^4.0.0" lodash.isplainobject "^4.0.6" lodash.merge "^4.6.2" @@ -500,70 +500,70 @@ ts-node "^10.8.1" typescript "^4.6.4" -"@commitlint/message@^17.2.0": - version "17.2.0" - resolved "https://registry.yarnpkg.com/@commitlint/message/-/message-17.2.0.tgz#c546b7a441b9f69493257f9fe0c3c8fc37933b27" - integrity sha512-/4l2KFKxBOuoEn1YAuuNNlAU05Zt7sNsC9H0mPdPm3chOrT4rcX0pOqrQcLtdMrMkJz0gC7b3SF80q2+LtdL9Q== +"@commitlint/message@^17.4.2": + version "17.4.2" + resolved "https://registry.yarnpkg.com/@commitlint/message/-/message-17.4.2.tgz#f4753a79701ad6db6db21f69076e34de6580e22c" + integrity sha512-3XMNbzB+3bhKA1hSAWPCQA3lNxR4zaeQAQcHj0Hx5sVdO6ryXtgUBGGv+1ZCLMgAPRixuc6en+iNAzZ4NzAa8Q== -"@commitlint/parse@^17.2.0": - version "17.2.0" - resolved "https://registry.yarnpkg.com/@commitlint/parse/-/parse-17.2.0.tgz#d87b09436ec741c2267b76a41972b34e53459a81" - integrity sha512-vLzLznK9Y21zQ6F9hf8D6kcIJRb2haAK5T/Vt1uW2CbHYOIfNsR/hJs0XnF/J9ctM20Tfsqv4zBitbYvVw7F6Q== +"@commitlint/parse@^17.4.2": + version "17.4.2" + resolved "https://registry.yarnpkg.com/@commitlint/parse/-/parse-17.4.2.tgz#b0f8a257a1f93387a497408b0b4cadba60ee3359" + integrity sha512-DK4EwqhxfXpyCA+UH8TBRIAXAfmmX4q9QRBz/2h9F9sI91yt6mltTrL6TKURMcjUVmgaB80wgS9QybNIyVBIJA== dependencies: - "@commitlint/types" "^17.0.0" + "@commitlint/types" "^17.4.0" conventional-changelog-angular "^5.0.11" conventional-commits-parser "^3.2.2" -"@commitlint/read@^17.2.0": - version "17.2.0" - resolved "https://registry.yarnpkg.com/@commitlint/read/-/read-17.2.0.tgz#7a67b7b611d978a344c2430cba030252c2170723" - integrity sha512-bbblBhrHkjxra3ptJNm0abxu7yeAaxumQ8ZtD6GIVqzURCETCP7Dm0tlVvGRDyXBuqX6lIJxh3W7oyKqllDsHQ== +"@commitlint/read@^17.4.0": + version "17.4.2" + resolved "https://registry.yarnpkg.com/@commitlint/read/-/read-17.4.2.tgz#4880a05271fb44cefa54d365a17d5753496a6de0" + integrity sha512-hasYOdbhEg+W4hi0InmXHxtD/1favB4WdwyFxs1eOy/DvMw6+2IZBmATgGOlqhahsypk4kChhxjAFJAZ2F+JBg== dependencies: - "@commitlint/top-level" "^17.0.0" - "@commitlint/types" "^17.0.0" - fs-extra "^10.0.0" + "@commitlint/top-level" "^17.4.0" + "@commitlint/types" "^17.4.0" + fs-extra "^11.0.0" git-raw-commits "^2.0.0" minimist "^1.2.6" -"@commitlint/resolve-extends@^17.3.0": - version "17.3.0" - resolved "https://registry.yarnpkg.com/@commitlint/resolve-extends/-/resolve-extends-17.3.0.tgz#413a9ec393266d0673e6b9ec2f0974c358ed662d" - integrity sha512-Lf3JufJlc5yVEtJWC8o4IAZaB8FQAUaVlhlAHRACd0TTFizV2Lk2VH70et23KgvbQNf7kQzHs/2B4QZalBv6Cg== +"@commitlint/resolve-extends@^17.4.0": + version "17.4.0" + resolved "https://registry.yarnpkg.com/@commitlint/resolve-extends/-/resolve-extends-17.4.0.tgz#9023da6c70c4ebd173b4b0995fe29f27051da2d3" + integrity sha512-3JsmwkrCzoK8sO22AzLBvNEvC1Pmdn/65RKXzEtQMy6oYMl0Snrq97a5bQQEFETF0VsvbtUuKttLqqgn99OXRQ== dependencies: - "@commitlint/config-validator" "^17.1.0" - "@commitlint/types" "^17.0.0" + "@commitlint/config-validator" "^17.4.0" + "@commitlint/types" "^17.4.0" import-fresh "^3.0.0" lodash.mergewith "^4.6.2" resolve-from "^5.0.0" resolve-global "^1.0.0" -"@commitlint/rules@^17.3.0": - version "17.3.0" - resolved "https://registry.yarnpkg.com/@commitlint/rules/-/rules-17.3.0.tgz#4b31d6739f7eb8c7222b323b0bc2b63bd298a4ad" - integrity sha512-s2UhDjC5yP2utx3WWqsnZRzjgzAX8BMwr1nltC0u0p8T/nzpkx4TojEfhlsOUj1t7efxzZRjUAV0NxNwdJyk+g== +"@commitlint/rules@^17.4.2": + version "17.4.2" + resolved "https://registry.yarnpkg.com/@commitlint/rules/-/rules-17.4.2.tgz#cdf203bc82af979cb319210ef9215cb1de216a9b" + integrity sha512-OGrPsMb9Fx3/bZ64/EzJehY9YDSGWzp81Pj+zJiY+r/NSgJI3nUYdlS37jykNIugzazdEXfMtQ10kmA+Kx2pZQ== dependencies: - "@commitlint/ensure" "^17.3.0" - "@commitlint/message" "^17.2.0" - "@commitlint/to-lines" "^17.0.0" - "@commitlint/types" "^17.0.0" + "@commitlint/ensure" "^17.4.0" + "@commitlint/message" "^17.4.2" + "@commitlint/to-lines" "^17.4.0" + "@commitlint/types" "^17.4.0" execa "^5.0.0" -"@commitlint/to-lines@^17.0.0": - version "17.0.0" - resolved "https://registry.yarnpkg.com/@commitlint/to-lines/-/to-lines-17.0.0.tgz#5766895836b8085b099a098482f88a03f070b411" - integrity sha512-nEi4YEz04Rf2upFbpnEorG8iymyH7o9jYIVFBG1QdzebbIFET3ir+8kQvCZuBE5pKCtViE4XBUsRZz139uFrRQ== +"@commitlint/to-lines@^17.4.0": + version "17.4.0" + resolved "https://registry.yarnpkg.com/@commitlint/to-lines/-/to-lines-17.4.0.tgz#9bd02e911e7d4eab3fb4a50376c4c6d331e10d8d" + integrity sha512-LcIy/6ZZolsfwDUWfN1mJ+co09soSuNASfKEU5sCmgFCvX5iHwRYLiIuoqXzOVDYOy7E7IcHilr/KS0e5T+0Hg== -"@commitlint/top-level@^17.0.0": - version "17.0.0" - resolved "https://registry.yarnpkg.com/@commitlint/top-level/-/top-level-17.0.0.tgz#ebd0df4c703c026c2fbdc20fa746836334f4ed15" - integrity sha512-dZrEP1PBJvodNWYPOYiLWf6XZergdksKQaT6i1KSROLdjf5Ai0brLOv5/P+CPxBeoj3vBxK4Ax8H1Pg9t7sHIQ== +"@commitlint/top-level@^17.4.0": + version "17.4.0" + resolved "https://registry.yarnpkg.com/@commitlint/top-level/-/top-level-17.4.0.tgz#540cac8290044cf846fbdd99f5cc51e8ac5f27d6" + integrity sha512-/1loE/g+dTTQgHnjoCy0AexKAEFyHsR2zRB4NWrZ6lZSMIxAhBJnmCqwao7b4H8888PsfoTBCLBYIw8vGnej8g== dependencies: find-up "^5.0.0" -"@commitlint/types@^17.0.0": - version "17.0.0" - resolved "https://registry.yarnpkg.com/@commitlint/types/-/types-17.0.0.tgz#3b4604c1a0f06c340ce976e6c6903d4f56e3e690" - integrity sha512-hBAw6U+SkAT5h47zDMeOu3HSiD0SODw4Aq7rRNh1ceUmL7GyLKYhPbUvlRWqZ65XjBLPHZhFyQlRaPNz8qvUyQ== +"@commitlint/types@^17.4.0": + version "17.4.0" + resolved "https://registry.yarnpkg.com/@commitlint/types/-/types-17.4.0.tgz#c7c2b97b959f6175c164632bf26208ce417b3f31" + integrity sha512-2NjAnq5IcxY9kXtUeO2Ac0aPpvkuOmwbH/BxIm36XXK5LtWFObWJWjXOA+kcaABMrthjWu6la+FUpyYFMHRvbA== dependencies: chalk "^4.1.0" @@ -579,7 +579,7 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz#90420f9f9c6d3987f176a19a7d8e764271a2f55d" integrity sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g== -"@eslint/eslintrc@^1.4.0": +"@eslint/eslintrc@^1.4.1": version "1.4.1" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.4.1.tgz#af58772019a2d271b7e2d4c23ff4ddcba3ccfb3e" integrity sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA== @@ -938,6 +938,11 @@ resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.3.tgz#0300943770e04231041a51bd39f0439b5c7ab4f0" integrity sha512-nkalE/f1RvRGChwBnEIoBfSEYOXnCRdleKuv6+lePbMDrMZXeDQnqak5XDOeBgrPPyPfAdcCu/B5z+v3VhplGg== +"@lightninglabs/lnc-core@0.2.1-alpha": + version "0.2.1-alpha" + resolved "https://registry.yarnpkg.com/@lightninglabs/lnc-core/-/lnc-core-0.2.1-alpha.tgz#b381ea29fb7bc20a7dd10334bb913bb50f6dac36" + integrity sha512-H297V3seL+PE786HcuAQiSass/3y0NSYCy3b4hNTZL4Io80Oy4WY49s08CpRuV8cNfjAzWjYSXkhorALjaRdwA== + "@mswjs/cookies@^0.2.2": version "0.2.2" resolved "https://registry.yarnpkg.com/@mswjs/cookies/-/cookies-0.2.2.tgz#b4e207bf6989e5d5427539c2443380a33ebb922b" @@ -960,10 +965,10 @@ strict-event-emitter "^0.2.4" web-encoding "^1.1.5" -"@noble/secp256k1@^1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.0.tgz#d15357f7c227e751d90aa06b05a0e5cf993ba8c1" - integrity sha512-kbacwGSsH/CTout0ZnZWxnW1B+jH/7r/WAAKLBtrRJ/+CUH7lgmQzl3GTrQua3SGKWNSDsS6lmjnDpIJ5Dxyaw== +"@noble/secp256k1@^1.7.1": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" + integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -991,23 +996,23 @@ resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-1.0.3.tgz#db9cc719191a62e7d9200f6e7bab21c5b848adca" integrity sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q== -"@playwright/test@^1.29.1": - version "1.29.1" - resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.29.1.tgz#f2ed4dc143b9c7825a7ad2703b2f1ac4354e1145" - integrity sha512-iQxk2DX5U9wOGV3+/Jh9OHPsw5H3mleUL2S4BgQuwtlAfK3PnKvn38m4Rg9zIViGHVW24opSm99HQm/UFLEy6w== +"@playwright/test@^1.29.2": + version "1.29.2" + resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.29.2.tgz#c48184721d0f0b7627a886e2ec42f1efb2be339d" + integrity sha512-+3/GPwOgcoF0xLz/opTnahel1/y42PdcgZ4hs+BZGIUjtmEFSXGg+nFoaH3NSmuc7a6GSFwXDJ5L7VXpqzigNg== dependencies: "@types/node" "*" - playwright-core "1.29.1" + playwright-core "1.29.2" "@polka/url@^1.0.0-next.20": version "1.0.0-next.21" resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== -"@remix-run/router@1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.2.1.tgz#812edd4104a15a493dda1ccac0b352270d7a188c" - integrity sha512-XiY0IsyHR+DXYS5vBxpoBe/8veTeoRpMHP+vDosLZxL5bnpetzI0igkxkLZS235ldLzyfkxF+2divEwWHP3vMQ== +"@remix-run/router@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.3.0.tgz#b6ee542c7f087b73b3d8215b9bf799f648be71cb" + integrity sha512-nwQoYb3m4DDpHTeOwpJEuDt8lWVcujhYYSFGLluC+9es2PyLjm+jjq3IeRBQbwBtPLJE/lkuHuGHr8uQLgmJRA== "@sinclair/typebox@^0.24.1": version "0.24.20" @@ -1028,71 +1033,71 @@ dependencies: "@sinonjs/commons" "^1.7.0" -"@swc/core-darwin-arm64@1.3.24": - version "1.3.24" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.24.tgz#d41fc574cb5049def9001903680fdd924f065052" - integrity sha512-rR+9UpWm+fGXcipsjCst2hIL1GYIbo0YTLhJZWdIpQD6KRHHJMFXiydMgQQkDj2Ml7HpqUVgxj6m4ZWYL8b0OA== - -"@swc/core-darwin-x64@1.3.24": - version "1.3.24" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.3.24.tgz#0f7a3960b91cbd7f95f25542b29d0e08bde4f59d" - integrity sha512-px+5vkGtgPH0m3FkkTBHynlRdS5rRz+lK+wiXIuBZFJSySWFl6RkKbvwkD+sf0MpazQlqwlv/rTOGJBw6oDffg== - -"@swc/core-linux-arm-gnueabihf@1.3.24": - version "1.3.24" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.24.tgz#a0fdd97b8341806b57290217830a5d1ab7d0b193" - integrity sha512-jLs8ZOdTV4UW4J12E143QJ4mOMONQtqgAnuhBbRuWFzQ3ny1dfoC3P1jNWAJ2Xi59XdxAIXn0PggPNH4Kh34kw== - -"@swc/core-linux-arm64-gnu@1.3.24": - version "1.3.24" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.24.tgz#0536d03e12dd471ebafc180599488404aebb65cf" - integrity sha512-A/v0h70BekrwGpp1DlzIFGcHQ3QQ2PexXcnnuIBZeMc9gNmHlcZmg3EcwAnaUDiokhNuSUFA/wV94yk1OqmSkw== - -"@swc/core-linux-arm64-musl@1.3.24": - version "1.3.24" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.24.tgz#54f46ffea1bf6ffcbe7c62037efaefdfb5115214" - integrity sha512-pbc9eArWPTiMrbpS/pJo0IiQNAKAQBcBIDjWBGP1tcw2iDXYLw4bruwz9kI/VjakbshWb8MoE4T5ClkeuULvSw== - -"@swc/core-linux-x64-gnu@1.3.24": - version "1.3.24" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.24.tgz#c2b5cef83f8afd2a57d0eafbac083562d50cd0e6" - integrity sha512-pP5pOLlY1xd352qo7rTlpVPUI9/9VhOd4b3Lk+LzfZDq9bTL2NDlGfyrPiwa5DGHMSzrugH56K2J68eutkxYVA== - -"@swc/core-linux-x64-musl@1.3.24": - version "1.3.24" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.24.tgz#3459d01f9bf745568a4196c1993987f3d4a98303" - integrity sha512-phNbP7zGp+Wcyxq1Qxlpe5KkxO7WLT2kVQUC7aDFGlVdCr+xdXsfH1MzheHtnr0kqTVQX1aiM8XXXHfFxR0oNA== - -"@swc/core-win32-arm64-msvc@1.3.24": - version "1.3.24" - resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.24.tgz#85a18c844c00d66bf46db99d9c98e9550b4d28f5" - integrity sha512-qhbiJTWAOqyR+K9xnGmCkOWSz2EmWpDBstEJCEOTc6FZiEdbiTscDmqTcMbCKaTHGu8t+6erVA4t65/Eg6uWPA== - -"@swc/core-win32-ia32-msvc@1.3.24": - version "1.3.24" - resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.24.tgz#18318199ba06cab4ead8f6122b9f30b3f452b1e7" - integrity sha512-JfghIlscE4Rz+Lc08lSoDh+R0cWxrISed5biogFfE6vZqhaDnw3E5Qshqw7O3pIaiq8L2u1nmzuyP581ZmpbRA== - -"@swc/core-win32-x64-msvc@1.3.24": - version "1.3.24" - resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.24.tgz#b53746787e5af021787134d393fd67b0431f90d9" - integrity sha512-3AmJRr0hwciwDBbzUNqaftvppzS8v9X/iv/Wl7YaVLBVpPfQvaZzfqLycvNMGLZb5vIKXR/u58txg3dRBGsJtw== - -"@swc/core@^1.3.24": - version "1.3.24" - resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.3.24.tgz#ef6b30267c1bbd48af62cbc91370fe9b3f5d6a23" - integrity sha512-QMOTd0AgiUT3K1crxLRqd3gw0f3FC8hhH1vvlIlryvYqU4c+FJ/T2G4ZhMKLxQlZ/jX6Rhk0gKINZRBxy2GFyQ== +"@swc/core-darwin-arm64@1.3.30": + version "1.3.30" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.30.tgz#fb9b20a46455f49597e760e4dfe829196e0fe12e" + integrity sha512-GZ4mZZbH77N8renK34A3Lkhl6x8z+c97SCbl43pn5E0Z0sifohA8WNhrtucKrUdid0svYibwotJzeFNpDtg7gQ== + +"@swc/core-darwin-x64@1.3.30": + version "1.3.30" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.3.30.tgz#2fd86123d697c024f7fe45995a3ef5a4e5e4eef0" + integrity sha512-ppGrAJmKpT3vFr2vGaxXFL8JqHsb6kSAj0dVYTNYicl3c6XOjnMiNSfu6HRbdmXt0VpFHhC5L/a7Ta89mQ1sJA== + +"@swc/core-linux-arm-gnueabihf@1.3.30": + version "1.3.30" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.30.tgz#a67ffdc80a63b68471bc176206237bd68576be40" + integrity sha512-XQYY/VNRnM0/779ehfMgh2poO3reOANvfzOprF8xmGK20+DxFqbMWjHhJutscQuEjLtdwk/LfgCkwmTaB1hhwg== + +"@swc/core-linux-arm64-gnu@1.3.30": + version "1.3.30" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.30.tgz#7f9b2f860b8abac6636b6ffb46004da4589b513f" + integrity sha512-ME4BjMYSXana0Lfswa3aQW0rTdmR9wa1NGQ3t6MYLdBVm+76Xwe29JKlOfnI1iCCtcbRBoWy4dlhyuxW8DN7cw== + +"@swc/core-linux-arm64-musl@1.3.30": + version "1.3.30" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.30.tgz#b917db1f71b8382e033b8bcbeccd8a326747c41b" + integrity sha512-h3X9Pn1m5kuFSW8lJyDiMB4ELNZFJ+QxLva5GCxZDArQttkNeY4tMNWFcaG44xUXeywffrgjpXO7Yj2JGzmG4g== + +"@swc/core-linux-x64-gnu@1.3.30": + version "1.3.30" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.30.tgz#fc63a70a34d444b0a9460caeb239c630e8cf905b" + integrity sha512-vfPR8cakx5IZQSpNuXPrpkRprBdVxXsvN5JWN3fpuNVIgFFo3q8njihaItujKvePIHQwov4achfBZlm4JRitWQ== + +"@swc/core-linux-x64-musl@1.3.30": + version "1.3.30" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.30.tgz#07003373a8813c3b82bd8ad2c2ea500a8cd6c9cd" + integrity sha512-jtfv8N+00E2RMTVjwfTfimeqzo0B9FmbbSkzlnLvkmV8xDAPyLmX7v/xL5YiVJRLeSrlJ7DmkCSxLzpJao73dw== + +"@swc/core-win32-arm64-msvc@1.3.30": + version "1.3.30" + resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.30.tgz#473eb708d2a162dd91bca2327e540ad3be57051d" + integrity sha512-fX3T6JzS5F8JJ/UZQWrZfdml8nLuSzgA0EFKetTNa5AHh1x9ltShmlFOJ3OPpD9BKI/QcQSLxyoAjxt7NtAnaQ== + +"@swc/core-win32-ia32-msvc@1.3.30": + version "1.3.30" + resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.30.tgz#7ea1b9b9d68d91050d25baf300a119f699ccb5a7" + integrity sha512-m88NjTcVFHFAciWRWTW7NbeQPrzjKKBzSoSPukhjvKSWQNk5v6BBbTAKpymNGQssPn5WLarC2QlQzCwjyh1QLA== + +"@swc/core-win32-x64-msvc@1.3.30": + version "1.3.30" + resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.30.tgz#43bd3cd1d48a0b7659a8834e0a3e5a3322cea8ca" + integrity sha512-HsePRjbdD5XsnS8NkN+MmhtUyjF16cU3COd92DjRYKsB1rMDE51itfacBvOeZPHFV6VkrLsakubAZCMc+3d/Ag== + +"@swc/core@^1.3.27": + version "1.3.30" + resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.3.30.tgz#f4b3b55d37f766d6246829528b123bc4c8068866" + integrity sha512-pg6GWw615EwCh4vJ5k7xll1E4WJSPeINrRvF/EPyBvNNhlXR3s6+KZevJTx3PpA5PXjprDR0aqwi0/aigSCAPA== optionalDependencies: - "@swc/core-darwin-arm64" "1.3.24" - "@swc/core-darwin-x64" "1.3.24" - "@swc/core-linux-arm-gnueabihf" "1.3.24" - "@swc/core-linux-arm64-gnu" "1.3.24" - "@swc/core-linux-arm64-musl" "1.3.24" - "@swc/core-linux-x64-gnu" "1.3.24" - "@swc/core-linux-x64-musl" "1.3.24" - "@swc/core-win32-arm64-msvc" "1.3.24" - "@swc/core-win32-ia32-msvc" "1.3.24" - "@swc/core-win32-x64-msvc" "1.3.24" + "@swc/core-darwin-arm64" "1.3.30" + "@swc/core-darwin-x64" "1.3.30" + "@swc/core-linux-arm-gnueabihf" "1.3.30" + "@swc/core-linux-arm64-gnu" "1.3.30" + "@swc/core-linux-arm64-musl" "1.3.30" + "@swc/core-linux-x64-gnu" "1.3.30" + "@swc/core-linux-x64-musl" "1.3.30" + "@swc/core-win32-arm64-msvc" "1.3.30" + "@swc/core-win32-ia32-msvc" "1.3.30" + "@swc/core-win32-x64-msvc" "1.3.30" "@swc/jest@^0.2.24": version "0.2.24" @@ -1288,10 +1293,10 @@ dependencies: "@types/node" "*" -"@types/chrome@^0.0.206": - version "0.0.206" - resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.206.tgz#ad1fd9799f368b5993d7c240492d4adaf5efbd8c" - integrity sha512-fQnTFjghPB9S4UzbfublUB6KmsBkvvJeGXGaaoD5Qu+ZxrDUfgJnKN5egLSzDcGAH5YxQubDgbCdNwwUGewQHg== +"@types/chrome@^0.0.210": + version "0.0.210" + resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.210.tgz#6bc137e111cf42857af5012d47c036adc079616e" + integrity sha512-VSjQu1k6a/rAfuqR1Gi/oxHZj4+t6+LG+GobNI3ZWI6DQ+fmphNSF6TrLHG6BYK2bXc9Gb4c1uXFKRRVLaGl5Q== dependencies: "@types/filesystem" "*" "@types/har-format" "*" @@ -1499,6 +1504,13 @@ dependencies: "@types/lodash" "*" +"@types/lodash.snakecase@^4.1.1": + version "4.1.7" + resolved "https://registry.yarnpkg.com/@types/lodash.snakecase/-/lodash.snakecase-4.1.7.tgz#2a1ca7cbc08b63e7c3708f6291222e69b0d3216d" + integrity sha512-nv9M+JJokFyfZ9QmaWVXZu2DfT40K0GictZaA8SwXczp3oCzMkjp7PtvUBQyvdoG9SnlCpoRXZDIVwQRzJbd9A== + dependencies: + "@types/lodash" "*" + "@types/lodash@*": version "4.14.188" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.188.tgz#e4990c4c81f7c9b00c5ff8eae389c10f27980da5" @@ -1529,11 +1541,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.6.tgz#0ba49ac517ad69abe7a1508bc9b3a5483df9d5d7" integrity sha512-/xUq6H2aQm261exT6iZTMifUySEt4GR5KX8eYyY+C4MSNPqSh9oNIP7tz2GLKTlFaiBbgZNxffoR3CVRG+cljw== -"@types/node@^14.0.0": - version "14.18.26" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.26.tgz#239e19f8b4ea1a9eb710528061c1d733dc561996" - integrity sha512-0b+utRBSYj8L7XAp0d+DX7lI4cSmowNaaTkk6/1SKzbKkG+doLuPusB9EOvzLJ8ahJSk03bTLIL6cWaEd4dBKA== - "@types/normalize-package-data@^2.4.0": version "2.4.1" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" @@ -1665,10 +1672,10 @@ resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.0.tgz#53ef263e5239728b56096b0a869595135b7952d2" integrity sha512-kr90f+ERiQtKWMz5rP32ltJ/BtULDI5RVO0uavn1HQUOwjx0R1h0rnDYNL0CepF1zL5bSY6FISAfd9tOdDhU5Q== -"@types/webextension-polyfill@^0.9.2": - version "0.9.2" - resolved "https://registry.yarnpkg.com/@types/webextension-polyfill/-/webextension-polyfill-0.9.2.tgz#4459174d25e83b38fd6d2b2cacb0fc87ad99dfa4" - integrity sha512-op8YeoK60K6/GIx3snWs3JowBZ+/aeSnZzZuuwynA7VARWfzr3st9aQNk9RWfoBx5xqlNZjAsh2QttPUZnabCw== +"@types/webextension-polyfill@^0.10.0": + version "0.10.0" + resolved "https://registry.yarnpkg.com/@types/webextension-polyfill/-/webextension-polyfill-0.10.0.tgz#e87b5e2c101599779a584cdb043887ad73b37b0e" + integrity sha512-If4EcaHzYTqcbNMp/FdReVdRmLL/Te42ivnJII551bYjhX19bWem5m14FERCqdJA732OloGuxCRvLBvcMGsn4A== "@types/ws@^8.5.1": version "8.5.3" @@ -1710,14 +1717,14 @@ dependencies: "@types/node" "*" -"@typescript-eslint/eslint-plugin@^5.45.1": - version "5.45.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.45.1.tgz#ee5b51405f6c9ee7e60e4006d68c69450d3b4536" - integrity sha512-cOizjPlKEh0bXdFrBLTrI/J6B/QMlhwE9auOov53tgB+qMukH6/h8YAK/qw+QJGct/PTbdh2lytGyipxCcEtAw== +"@typescript-eslint/eslint-plugin@^5.48.0": + version "5.48.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.0.tgz#54f8368d080eb384a455f60c2ee044e948a8ce67" + integrity sha512-SVLafp0NXpoJY7ut6VFVUU9I+YeFsDzeQwtK0WZ+xbRN3mtxJ08je+6Oi2N89qDn087COdO0u3blKZNv9VetRQ== dependencies: - "@typescript-eslint/scope-manager" "5.45.1" - "@typescript-eslint/type-utils" "5.45.1" - "@typescript-eslint/utils" "5.45.1" + "@typescript-eslint/scope-manager" "5.48.0" + "@typescript-eslint/type-utils" "5.48.0" + "@typescript-eslint/utils" "5.48.0" debug "^4.3.4" ignore "^5.2.0" natural-compare-lite "^1.4.0" @@ -1725,72 +1732,72 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/parser@^5.45.1": - version "5.45.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.45.1.tgz#6440ec283fa1373a12652d4e2fef4cb6e7b7e8c6" - integrity sha512-JQ3Ep8bEOXu16q0ztsatp/iQfDCtvap7sp/DKo7DWltUquj5AfCOpX2zSzJ8YkAVnrQNqQ5R62PBz2UtrfmCkA== +"@typescript-eslint/parser@^5.48.0": + version "5.48.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.48.0.tgz#02803355b23884a83e543755349809a50b7ed9ba" + integrity sha512-1mxNA8qfgxX8kBvRDIHEzrRGrKHQfQlbW6iHyfHYS0Q4X1af+S6mkLNtgCOsGVl8+/LUPrqdHMssAemkrQ01qg== dependencies: - "@typescript-eslint/scope-manager" "5.45.1" - "@typescript-eslint/types" "5.45.1" - "@typescript-eslint/typescript-estree" "5.45.1" + "@typescript-eslint/scope-manager" "5.48.0" + "@typescript-eslint/types" "5.48.0" + "@typescript-eslint/typescript-estree" "5.48.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.45.1": - version "5.45.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.45.1.tgz#5b87d025eec7035d879b99c260f03be5c247883c" - integrity sha512-D6fCileR6Iai7E35Eb4Kp+k0iW7F1wxXYrOhX/3dywsOJpJAQ20Fwgcf+P/TDtvQ7zcsWsrJaglaQWDhOMsspQ== +"@typescript-eslint/scope-manager@5.48.0": + version "5.48.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.48.0.tgz#607731cb0957fbc52fd754fd79507d1b6659cecf" + integrity sha512-0AA4LviDtVtZqlyUQnZMVHydDATpD9SAX/RC5qh6cBd3xmyWvmXYF+WT1oOmxkeMnWDlUVTwdODeucUnjz3gow== dependencies: - "@typescript-eslint/types" "5.45.1" - "@typescript-eslint/visitor-keys" "5.45.1" + "@typescript-eslint/types" "5.48.0" + "@typescript-eslint/visitor-keys" "5.48.0" -"@typescript-eslint/type-utils@5.45.1": - version "5.45.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.45.1.tgz#cb7d300c3c95802cea9f87c7f8be363cf8f8538c" - integrity sha512-aosxFa+0CoYgYEl3aptLe1svP910DJq68nwEJzyQcrtRhC4BN0tJAvZGAe+D0tzjJmFXe+h4leSsiZhwBa2vrA== +"@typescript-eslint/type-utils@5.48.0": + version "5.48.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.48.0.tgz#40496dccfdc2daa14a565f8be80ad1ae3882d6d6" + integrity sha512-vbtPO5sJyFjtHkGlGK4Sthmta0Bbls4Onv0bEqOGm7hP9h8UpRsHJwsrCiWtCUndTRNQO/qe6Ijz9rnT/DB+7g== dependencies: - "@typescript-eslint/typescript-estree" "5.45.1" - "@typescript-eslint/utils" "5.45.1" + "@typescript-eslint/typescript-estree" "5.48.0" + "@typescript-eslint/utils" "5.48.0" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.45.1": - version "5.45.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.45.1.tgz#8e1883041cee23f1bb7e1343b0139f97f6a17c14" - integrity sha512-HEW3U0E5dLjUT+nk7b4lLbOherS1U4ap+b9pfu2oGsW3oPu7genRaY9dDv3nMczC1rbnRY2W/D7SN05wYoGImg== +"@typescript-eslint/types@5.48.0": + version "5.48.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.48.0.tgz#d725da8dfcff320aab2ac6f65c97b0df30058449" + integrity sha512-UTe67B0Ypius0fnEE518NB2N8gGutIlTojeTg4nt0GQvikReVkurqxd2LvYa9q9M5MQ6rtpNyWTBxdscw40Xhw== -"@typescript-eslint/typescript-estree@5.45.1": - version "5.45.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.45.1.tgz#b3dc37f0c4f0fe73e09917fc735e6f96eabf9ba4" - integrity sha512-76NZpmpCzWVrrb0XmYEpbwOz/FENBi+5W7ipVXAsG3OoFrQKJMiaqsBMbvGRyLtPotGqUfcY7Ur8j0dksDJDng== +"@typescript-eslint/typescript-estree@5.48.0": + version "5.48.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.0.tgz#a7f04bccb001003405bb5452d43953a382c2fac2" + integrity sha512-7pjd94vvIjI1zTz6aq/5wwE/YrfIyEPLtGJmRfyNR9NYIW+rOvzzUv3Cmq2hRKpvt6e9vpvPUQ7puzX7VSmsEw== dependencies: - "@typescript-eslint/types" "5.45.1" - "@typescript-eslint/visitor-keys" "5.45.1" + "@typescript-eslint/types" "5.48.0" + "@typescript-eslint/visitor-keys" "5.48.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.45.1": - version "5.45.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.45.1.tgz#39610c98bde82c4792f2a858b29b7d0053448be2" - integrity sha512-rlbC5VZz68+yjAzQBc4I7KDYVzWG2X/OrqoZrMahYq3u8FFtmQYc+9rovo/7wlJH5kugJ+jQXV5pJMnofGmPRw== +"@typescript-eslint/utils@5.48.0": + version "5.48.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.48.0.tgz#eee926af2733f7156ad8d15e51791e42ce300273" + integrity sha512-x2jrMcPaMfsHRRIkL+x96++xdzvrdBCnYRd5QiW5Wgo1OB4kDYPbC1XjWP/TNqlfK93K/lUL92erq5zPLgFScQ== dependencies: "@types/json-schema" "^7.0.9" "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.45.1" - "@typescript-eslint/types" "5.45.1" - "@typescript-eslint/typescript-estree" "5.45.1" + "@typescript-eslint/scope-manager" "5.48.0" + "@typescript-eslint/types" "5.48.0" + "@typescript-eslint/typescript-estree" "5.48.0" eslint-scope "^5.1.1" eslint-utils "^3.0.0" semver "^7.3.7" -"@typescript-eslint/visitor-keys@5.45.1": - version "5.45.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.45.1.tgz#204428430ad6a830d24c5ac87c71366a1cfe1948" - integrity sha512-cy9ln+6rmthYWjH9fmx+5FU/JDpjQb586++x2FZlveq7GdGuLLW9a2Jcst2TGekH82bXpfmRNSwP9tyEs6RjvQ== +"@typescript-eslint/visitor-keys@5.48.0": + version "5.48.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.0.tgz#4446d5e7f6cadde7140390c0e284c8702d944904" + integrity sha512-5motVPz5EgxQ0bHjut3chzBkJ3Z3sheYVcSwS5BpHZpLqSptSmELNtGixmgj65+rIfhvtQTz5i9OP2vtzdDH7Q== dependencies: - "@typescript-eslint/types" "5.45.1" + "@typescript-eslint/types" "5.48.0" eslint-visitor-keys "^3.3.0" "@vespaiach/axios-fetch-adapter@^0.3.0": @@ -2251,17 +2258,6 @@ array-includes@^3.1.2: get-intrinsic "^1.1.1" is-string "^1.0.5" -array-includes@^3.1.4: - version "3.1.4" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.4.tgz#f5b493162c760f3539631f005ba2bb46acb45ba9" - integrity sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - get-intrinsic "^1.1.1" - is-string "^1.0.7" - array-includes@^3.1.6: version "3.1.6" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" @@ -2290,14 +2286,15 @@ array-uniq@^1.0.1: resolved "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz" integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= -array.prototype.flat@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz#07e0975d84bbc7c48cd1879d609e682598d33e13" - integrity sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg== +array.prototype.flat@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" + integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.0" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" array.prototype.flatmap@^1.3.1: version "1.3.1" @@ -2357,6 +2354,13 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== +avvvatars-react@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/avvvatars-react/-/avvvatars-react-0.4.2.tgz#9569c16ae9a4925f898539ff98c6f734b6a4fe1b" + integrity sha512-D/bnSM+P6pQi71dFeFVqQsqmv+ct5XG7uO2lvJoiksALpXLd6kPgpRR1SUQxFZJkJNrkmg0NPgbhIgJgOsDYRQ== + dependencies: + goober "^2.1.8" + axios@^0.27.2: version "0.27.2" resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" @@ -3167,11 +3171,6 @@ core-js-pure@^3.20.2: resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.22.5.tgz#bdee0ed2f9b78f2862cda4338a07b13a49b6c9a9" integrity sha512-8xo9R00iYD7TcV7OrC98GwxiUEAabVWO3dix+uyWjnYrx9fyASLlIX+f/3p5dW5qByaP2bcZ8X/T47s55et/tA== -core-js@^3.4: - version "3.26.0" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.26.0.tgz#a516db0ed0811be10eac5d94f3b8463d03faccfe" - integrity sha512-+DkDrhoR4Y0PxDz6rurahuB+I45OsEUv8E1maPTB6OuHRohMMcznBq9TMpdpDMm/hUPob/mJJS3PqgbHpMTQgw== - core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" @@ -3182,7 +3181,7 @@ cosmiconfig-typescript-loader@^4.0.0: resolved "https://registry.yarnpkg.com/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.0.0.tgz#4a6d856c1281135197346a6f64dfa73a9cd9fefa" integrity sha512-cVpucSc2Tf+VPwCCR7SZzmQTQkPbkk4O01yXsYqXBIbjE1bhwqSyAgYQkRK1un4i0OPziTleqFhdkmOc4RQ/9g== -cosmiconfig@8.0.0: +cosmiconfig@8.0.0, cosmiconfig@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.0.0.tgz#e9feae014eab580f858f8a0288f38997a7bebe97" integrity sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ== @@ -3258,7 +3257,7 @@ cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -crypto-js@^4.1.1: +crypto-js@4.1.1, crypto-js@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.1.1.tgz#9e485bcf03521041bd85844786b83fb7619736cf" integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw== @@ -3425,7 +3424,7 @@ dayjs@^1.11.7: resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2" integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ== -debug@2.6.9, debug@^2.6.9: +debug@2.6.9: version "2.6.9" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -3614,10 +3613,10 @@ devtools-protocol@0.0.1068969: resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1068969.tgz#8b9a4bc48aed1453bed08d62b07481f9abf4d6d8" integrity sha512-ATFTrPbY1dKYhPPvpjtwWKSK2mIwGmRwX54UASn9THEuIZCe2n9k3vVuMmt6jWeL+e5QaaguEv/pMyR+JQB7VQ== -dexie@^3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/dexie/-/dexie-3.2.2.tgz#fa6f2a3c0d6ed0766f8d97a03720056f88fe0e01" - integrity sha512-q5dC3HPmir2DERlX+toCBbHQXW5MsyrFqPFcovkH9N2S/UW/H3H5AWAB6iEOExeraAu+j+zRDG+zg/D7YhH0qg== +dexie@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/dexie/-/dexie-3.2.3.tgz#f35c91ca797599df8e771b998e9ae9669c877f8c" + integrity sha512-iHayBd4UYryDCVUNa3PMsJMEnd8yjyh5p7a+RFeC8i8n476BC9wMhVvqiImq5zJZJf5Tuer+s4SSj+AA3x+ZbQ== didyoumean@^1.2.2: version "1.2.2" @@ -3893,7 +3892,7 @@ es-abstract@^1.18.0-next.2: string.prototype.trimstart "^1.0.4" unbox-primitive "^1.0.1" -es-abstract@^1.19.0, es-abstract@^1.19.1: +es-abstract@^1.19.0: version "1.19.1" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== @@ -4041,44 +4040,46 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -eslint-config-prettier@^8.5.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" - integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== +eslint-config-prettier@^8.6.0: + version "8.6.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz#dec1d29ab728f4fa63061774e1672ac4e363d207" + integrity sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA== -eslint-import-resolver-node@^0.3.6: - version "0.3.6" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" - integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== +eslint-import-resolver-node@^0.3.7: + version "0.3.7" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" + integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== dependencies: debug "^3.2.7" - resolve "^1.20.0" + is-core-module "^2.11.0" + resolve "^1.22.1" -eslint-module-utils@^2.7.3: - version "2.7.3" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee" - integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ== +eslint-module-utils@^2.7.4: + version "2.7.4" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz#4f3e41116aaf13a20792261e61d3a2e7e0583974" + integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== dependencies: debug "^3.2.7" - find-up "^2.1.0" -eslint-plugin-import@^2.26.0: - version "2.26.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b" - integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA== +eslint-plugin-import@^2.27.5: + version "2.27.5" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz#876a6d03f52608a3e5bb439c2550588e51dd6c65" + integrity sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow== dependencies: - array-includes "^3.1.4" - array.prototype.flat "^1.2.5" - debug "^2.6.9" + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + array.prototype.flatmap "^1.3.1" + debug "^3.2.7" doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.6" - eslint-module-utils "^2.7.3" + eslint-import-resolver-node "^0.3.7" + eslint-module-utils "^2.7.4" has "^1.0.3" - is-core-module "^2.8.1" + is-core-module "^2.11.0" is-glob "^4.0.3" minimatch "^3.1.2" - object.values "^1.1.5" - resolve "^1.22.0" + object.values "^1.1.6" + resolve "^1.22.1" + semver "^6.3.0" tsconfig-paths "^3.14.1" eslint-plugin-react-hooks@^4.6.0: @@ -4086,10 +4087,10 @@ eslint-plugin-react-hooks@^4.6.0: resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3" integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== -eslint-plugin-react@^7.31.11: - version "7.31.11" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.31.11.tgz#011521d2b16dcf95795df688a4770b4eaab364c8" - integrity sha512-TTvq5JsT5v56wPa9OYHzsrOlHzKZKjV+aLgS+55NJP/cuzdiQPC7PfYoUjMoxlffKtvijpk7vA/jmuqRb9nohw== +eslint-plugin-react@^7.32.1: + version "7.32.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.32.1.tgz#88cdeb4065da8ca0b64e1274404f53a0f9890200" + integrity sha512-vOjdgyd0ZHBXNsmvU+785xY8Bfe57EFbTYYk8XrROzWpr9QBvpjITvAXt9xqcE6+8cjR/g1+mfumPToxsl1www== dependencies: array-includes "^3.1.6" array.prototype.flatmap "^1.3.1" @@ -4103,7 +4104,7 @@ eslint-plugin-react@^7.31.11: object.hasown "^1.1.2" object.values "^1.1.6" prop-types "^15.8.1" - resolve "^2.0.0-next.3" + resolve "^2.0.0-next.4" semver "^6.3.0" string.prototype.matchall "^4.0.8" @@ -4140,12 +4141,12 @@ eslint-visitor-keys@^3.3.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== -eslint@^8.30.0: - version "8.30.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.30.0.tgz#83a506125d089eef7c5b5910eeea824273a33f50" - integrity sha512-MGADB39QqYuzEGov+F/qb18r4i7DohCDOfatHaxI2iGlPuC65bwG2gxgO+7DkyL38dRFaRH7RaRAgU6JKL9rMQ== +eslint@^8.32.0: + version "8.33.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.33.0.tgz#02f110f32998cb598c6461f24f4d306e41ca33d7" + integrity sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA== dependencies: - "@eslint/eslintrc" "^1.4.0" + "@eslint/eslintrc" "^1.4.1" "@humanwhocodes/config-array" "^0.11.8" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" @@ -4360,12 +4361,12 @@ extract-zip@2.0.1: optionalDependencies: "@types/yauzl" "^2.9.1" -fake-indexeddb@^3.1.8: - version "3.1.8" - resolved "https://registry.yarnpkg.com/fake-indexeddb/-/fake-indexeddb-3.1.8.tgz#229e3cff6fa7355aebb3f147b908d2efa4605d70" - integrity sha512-7umIgcdnDfNcjw0ZaoD6yR2BflngKmPsyzZC+sV2fdttwz5bH6B6CCaNzzD+MURfRg8pvr/aL0trfNx65FLiDg== +fake-indexeddb@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/fake-indexeddb/-/fake-indexeddb-4.0.1.tgz#09bb2468e21d0832b2177e894765fb109edac8fb" + integrity sha512-hFRyPmvEZILYgdcLBxVdHLik4Tj3gDTu/g7s9ZDOiU3sTNiGx+vEu1ri/AMsFJUZ/1sdRbAVrEcKndh3sViBcA== dependencies: - realistic-structured-clone "^2.0.1" + realistic-structured-clone "^3.0.0" fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" @@ -4485,13 +4486,6 @@ finalhandler@~1.1.2: statuses "~1.5.0" unpipe "~1.0.0" -find-up@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz" - integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= - dependencies: - locate-path "^2.0.0" - find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -4567,7 +4561,7 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== -fs-extra@^10.0.0, fs-extra@^10.1.0: +fs-extra@^10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== @@ -4803,6 +4797,11 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" +goober@^2.1.8: + version "2.1.11" + resolved "https://registry.yarnpkg.com/goober/-/goober-2.1.11.tgz#bbd71f90d2df725397340f808dbe7acc3118e610" + integrity sha512-5SS2lmxbhqH0u9ABEWq7WPU69a4i2pYcHeCxqaNq6Cw3mnrF0ghWNM4tEGid4dKy8XNIAUbuThuozDHHKJVh3A== + graceful-fs@^4.1.2, graceful-fs@^4.2.4: version "4.2.6" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz" @@ -5002,10 +5001,10 @@ html-webpack-plugin@^5.5.0: pretty-error "^4.0.0" tapable "^2.0.0" -html5-qrcode@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/html5-qrcode/-/html5-qrcode-2.3.1.tgz#81861de0feb006de5aa6a1dbe8d301b244a7bb6f" - integrity sha512-sSAgpmmUlssKm9az18I6d7fytH5jPq4xG7E4mCuhsGpMsDwk05AumKNsag5rfxwgMFPiUQg7CcRiOCXlgSJxmg== +html5-qrcode@^2.3.4: + version "2.3.4" + resolved "https://registry.yarnpkg.com/html5-qrcode/-/html5-qrcode-2.3.4.tgz#7e2b4575a23b10ff5e26d2bf147c8027c1ece389" + integrity sha512-VPZrOTG8XR9HmIAhSSiGtJVPErZxKy/DuGc9cPQLburCWZEbvxQGJP9y4K4P+8vdalLtYB/vM5YP1BdWQKZ8jQ== htmlparser2@^6.1.0: version "6.1.0" @@ -5095,10 +5094,10 @@ human-signals@^3.0.1: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-3.0.1.tgz#c740920859dafa50e5a3222da9d3bf4bb0e5eef5" integrity sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ== -husky@^8.0.2: - version "8.0.2" - resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.2.tgz#5816a60db02650f1f22c8b69b928fd6bcd77a236" - integrity sha512-Tkv80jtvbnkK3mYWxPZePGFpQ/tT3HNSs/sasF9P2YfkMezDl3ON37YN6jUUI4eTg5LcyVynlb6r4eyvOmspvg== +husky@^8.0.3: + version "8.0.3" + resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184" + integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg== i18next-browser-languagedetector@^7.0.1: version "7.0.1" @@ -5107,10 +5106,10 @@ i18next-browser-languagedetector@^7.0.1: dependencies: "@babel/runtime" "^7.19.4" -i18next@^22.4.6: - version "22.4.6" - resolved "https://registry.yarnpkg.com/i18next/-/i18next-22.4.6.tgz#876352c3ba81bdfedc38eeda124e2bbd05f46988" - integrity sha512-9Tm1ezxWyzV+306CIDMBbYBitC1jedQyYuuLtIv7oxjp2ohh8eyxP9xytIf+2bbQfhH784IQKPSYp+Zq9+YSbw== +i18next@^22.4.9: + version "22.4.9" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-22.4.9.tgz#98c8384c6bd41ff937da98b1e809ba03d3b41053" + integrity sha512-8gWMmUz460KJDQp/ob3MNUX84cVuDRY9PLFPnV8d+Qezz/6dkjxwOaH70xjrCNDO+JrUL25iXfAIN9wUkInNZw== dependencies: "@babel/runtime" "^7.20.6" @@ -5294,7 +5293,14 @@ is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.2.0, is-core-module@^2.5.0, is-core-module@^2.8.1, is-core-module@^2.9.0: +is-core-module@^2.11.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" + integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== + dependencies: + has "^1.0.3" + +is-core-module@^2.5.0, is-core-module@^2.8.1, is-core-module@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== @@ -5998,10 +6004,10 @@ jest-watcher@^29.3.1: jest-util "^29.3.1" string-length "^4.0.1" -jest-webextension-mock@^3.8.7: - version "3.8.7" - resolved "https://registry.yarnpkg.com/jest-webextension-mock/-/jest-webextension-mock-3.8.7.tgz#ff7777e307c7782e9d283d09c5e116d29100be0f" - integrity sha512-hLLqY24VqebfzKE21oRXVJRpLwxF6VKzTJZdGSpRqtUmnywmopP/jBZsegbtxH50rhn9Ql5Zioivkt35cMHZJg== +jest-webextension-mock@^3.8.8: + version "3.8.8" + resolved "https://registry.yarnpkg.com/jest-webextension-mock/-/jest-webextension-mock-3.8.8.tgz#30d825d73c274a4fd531bda48f050a6e142a5d11" + integrity sha512-W3Yi/+zIgoUayVikGBZcZp+RrBy0h0Ol3Fy6+a72b6GaezXKFcZmTj9sE77E3BJ6Y0UifLuR3fImo5+D4jp+ww== jest-worker@^27.0.2: version "27.0.2" @@ -6279,10 +6285,17 @@ listr2@^5.0.5: through "^2.3.8" wrap-ansi "^7.0.0" -lnmessage@^0.0.14: - version "0.0.14" - resolved "https://registry.yarnpkg.com/lnmessage/-/lnmessage-0.0.14.tgz#0c9c96802ad61d0feceb9751b77f3195f7b7056a" - integrity sha512-bD+iUAhYfh7ky2uqIFX4FdhJOxGYItNO2ILH2WKV4LgMzzFyjg+YqEf2ReRjXLepd3zpVBLxLD/PDZxHwgQzbw== +"lnc-web@git+https://github.com/bumi/lnc-web.git#remove-window-dependency": + version "0.2.1-alpha" + resolved "git+https://github.com/bumi/lnc-web.git#0b312e8c131d6093a498bf5f4e4642196984ca1f" + dependencies: + "@lightninglabs/lnc-core" "0.2.1-alpha" + crypto-js "4.1.1" + +lnmessage@^0.0.18: + version "0.0.18" + resolved "https://registry.yarnpkg.com/lnmessage/-/lnmessage-0.0.18.tgz#bb578a324bae85894bcea12011fe6f27a3021289" + integrity sha512-6uwPNajtVdgcE9wJ5DhWmlfrksXks68MnbsJrKRR+d000DG1asFmERPWDeofFSVmkYfDuCJFHA63h6miQV+P0Q== dependencies: buffer "^6.0.3" crypto-js "^4.1.1" @@ -6299,14 +6312,6 @@ loader-utils@^3.2.0: resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-3.2.0.tgz#bcecc51a7898bee7473d4bc6b845b23af8304d4f" integrity sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ== -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz" - integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - locate-path@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" @@ -6712,10 +6717,10 @@ ms@2.1.3, ms@^2.1.1: resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -msw@^0.49.2: - version "0.49.2" - resolved "https://registry.yarnpkg.com/msw/-/msw-0.49.2.tgz#c815fa514a1b3532e3d3af01d308bb63329ad1e2" - integrity sha512-70/E10f+POE2UmMw16v8PnKatpZplpkUwVRLBqiIdimpgaC3le7y2yOq9JmOrL15jpwWM5wAcPTOj0f550LI3g== +msw@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/msw/-/msw-1.0.0.tgz#4f8e63aa23912561a63b99ff560a089da6969418" + integrity sha512-8QVa1RAN/Nzbn/tKmtimJ+b2M1QZOMdETQW7/1TmBOZ4w+wJojfxuh1Hj5J4FYdBgZWW/TK4CABUOlOM4OjTOA== dependencies: "@mswjs/cookies" "^0.2.2" "@mswjs/interceptors" "^0.17.5" @@ -6733,7 +6738,7 @@ msw@^0.49.2: node-fetch "^2.6.7" outvariant "^1.3.0" path-to-regexp "^6.2.0" - strict-event-emitter "^0.2.6" + strict-event-emitter "^0.4.3" type-fest "^2.19.0" yargs "^17.3.1" @@ -6967,15 +6972,6 @@ object.hasown@^1.1.2: define-properties "^1.1.4" es-abstract "^1.20.4" -object.values@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" - integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - object.values@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" @@ -7086,13 +7082,6 @@ outvariant@^1.2.1, outvariant@^1.3.0: resolved "https://registry.yarnpkg.com/outvariant/-/outvariant-1.3.0.tgz#c39723b1d2cba729c930b74bf962317a81b9b1c9" integrity sha512-yeWM9k6UPfG/nzxdaPlJkB2p08hCg4xP6Lx99F+vP8YF7xyZVfTmJjrrNalkmzudD4WFvNLVudQikqUmF8zhVQ== -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" - p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -7107,13 +7096,6 @@ p-limit@^3.0.2, p-limit@^3.1.0: dependencies: yocto-queue "^0.1.0" -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= - dependencies: - p-limit "^1.1.0" - p-locate@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" @@ -7155,11 +7137,6 @@ p-retry@^4.5.0: "@types/retry" "^0.12.0" retry "^0.13.1" -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= - p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -7210,11 +7187,6 @@ pascal-case@^3.1.2: no-case "^3.0.4" tslib "^2.0.3" -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= - path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -7240,7 +7212,7 @@ path-key@^4.0.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== -path-parse@^1.0.6, path-parse@^1.0.7: +path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -7314,10 +7286,10 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -playwright-core@1.29.1: - version "1.29.1" - resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.29.1.tgz#9ec15d61c4bd2f386ddf6ce010db53a030345a47" - integrity sha512-20Ai3d+lMkWpI9YZYlxk8gxatfgax5STW8GaMozAHwigLiyiKQrdkt7gaoT9UQR8FIVDg6qVXs9IoZUQrDjIIg== +playwright-core@1.29.2: + version "1.29.2" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.29.2.tgz#2e8347e7e8522409f22b244e600e703b64022406" + integrity sha512-94QXm4PMgFoHAhlCuoWyaBYKb92yOcGVHdQLoxQ7Wjlc7Flg4aC/jbFW7xMR52OfXMVkWicue4WXE7QEegbIRA== postcss-calc@^8.2.3: version "8.2.4" @@ -7666,10 +7638,10 @@ postcss@^8.4.18, postcss@^8.4.19: picocolors "^1.0.0" source-map-js "^1.0.2" -postcss@^8.4.20: - version "8.4.20" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.20.tgz#64c52f509644cecad8567e949f4081d98349dc56" - integrity sha512-6Q04AXR1212bXr5fh03u8aAwbLxAQNGQ/Q1LNa0VfOI06ZAlhPHtQvE4OIdpj4kLThXilalPnmDSOD65DcHt+g== +postcss@^8.4.21: + version "8.4.21" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4" + integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg== dependencies: nanoid "^3.3.4" picocolors "^1.0.0" @@ -7693,10 +7665,10 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== -prettier@^2.8.1: - version "2.8.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.1.tgz#4e1fd11c34e2421bc1da9aea9bd8127cd0a35efc" - integrity sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg== +prettier@^2.8.3: + version "2.8.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.3.tgz#ab697b1d3dd46fb4626fbe2f543afe0cc98d8632" + integrity sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw== pretty-error@^4.0.0: version "4.0.0" @@ -7821,10 +7793,10 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -puppeteer-core@19.4.1: - version "19.4.1" - resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-19.4.1.tgz#f4875943841ebdb6fc2ad7a475add958692b0237" - integrity sha512-JHIuqtqrUAx4jGOTxXu4ilapV2jabxtVMA/e4wwFUMvtSsqK4nVBSI+Z1SKDoz7gRy/JUIc8WzmfocCa6SIZ1w== +puppeteer-core@19.5.2: + version "19.5.2" + resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-19.5.2.tgz#9b454b0ef89d3f07e20158dd4ced6ebd85d4dadb" + integrity sha512-Rqk+3kqM+Z2deooTYqcYt8lRtGffJdifWa9td9nbJSjhANWsFouk8kLBNUKycewCCFHM8TZUKS0x28OllavW2A== dependencies: cross-fetch "3.1.5" debug "4.3.4" @@ -7837,16 +7809,16 @@ puppeteer-core@19.4.1: unbzip2-stream "1.4.3" ws "8.11.0" -puppeteer@^19.4.1: - version "19.4.1" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-19.4.1.tgz#cac7d3f0084badebb8ebacbe6f4d7262e7f21818" - integrity sha512-PCnrR13B8A+VSEDXRmrNXRZbrkF1tfsI1hKSC7vs13eNS6CUD3Y4FA8SF8/VZy+Pm1kg5AggJT2Nu3HLAtGkFg== +puppeteer@^19.5.2: + version "19.5.2" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-19.5.2.tgz#f09ed55d5263cacaac4c5818d0d884b1e1d938b0" + integrity sha512-xlqRyrhXhVH114l79Y0XqYXUVG+Yfw4sKlvN55t8Y9DxtA5fzI1uqF8SVXbWK5DUMbD6Jo4lpixTZCTTZGD05g== dependencies: cosmiconfig "8.0.0" https-proxy-agent "5.0.1" progress "2.0.3" proxy-from-env "1.1.0" - puppeteer-core "19.4.1" + puppeteer-core "19.5.2" q@^1.5.1: version "1.5.1" @@ -7915,12 +7887,12 @@ react-dom@^18.2.0: loose-envify "^1.1.0" scheduler "^0.23.0" -react-i18next@^12.1.1: - version "12.1.1" - resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-12.1.1.tgz#2626cdbfe6bcb76ef833861c0184a5c4e5e3c089" - integrity sha512-mFdieOI0LDy84q3JuZU6Aou1DoWW2fhapcTGeBS8+vWSJuViuoCLQAMYSb0QoHhXS8B0WKUOPpx4cffAP7r/aA== +react-i18next@^12.1.4: + version "12.1.4" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-12.1.4.tgz#be0a60d3a45acc4321909f8a4b8cde16518a2926" + integrity sha512-XQND7jYtgM7ht5PH3yIZljCRpAMTlH/zmngM9ZjToqa+0BR6xuu8c7QF0WIIOEjcMTB2S3iOfpN/xG/ZrAnO6g== dependencies: - "@babel/runtime" "^7.14.5" + "@babel/runtime" "^7.20.6" html-parse-stringify "^3.0.1" react-is@^16.13.1, react-is@^16.8.1: @@ -7958,28 +7930,28 @@ react-modal@^3.16.1: react-lifecycles-compat "^3.0.0" warning "^4.0.3" -react-qr-code@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/react-qr-code/-/react-qr-code-2.0.8.tgz#d34a766fb5b664a40dbdc7020f7ac801bacb2851" - integrity sha512-zYO9EAPQU8IIeD6c6uAle7NlKOiVKs8ji9hpbWPTGxO+FLqBN2on+XCXQvnhm91nrRd306RvNXUkUNcXXSfhWA== +react-qr-code@^2.0.11: + version "2.0.11" + resolved "https://registry.yarnpkg.com/react-qr-code/-/react-qr-code-2.0.11.tgz#444c759a2100424972e17135fbe0e32eaffa19e8" + integrity sha512-P7mvVM5vk9NjGdHMt4Z0KWeeJYwRAtonHTghZT2r+AASinLUUKQ9wfsGH2lPKsT++gps7hXmaiMGRvwTDEL9OA== dependencies: prop-types "^15.8.1" qr.js "0.0.0" -react-router-dom@^6.6.1: - version "6.6.1" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.6.1.tgz#1b96ec0b2cefa7319f1251383ea5b41295ee260d" - integrity sha512-u+8BKUtelStKbZD5UcY0NY90WOzktrkJJhyhNg7L0APn9t1qJNLowzrM9CHdpB6+rcPt6qQrlkIXsTvhuXP68g== +react-router-dom@^6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.7.0.tgz#0249f4ca4eb704562b8b0ff29caeb928c3a6ed38" + integrity sha512-jQtXUJyhso3kFw430+0SPCbmCmY1/kJv8iRffGHwHy3CkoomGxeYzMkmeSPYo6Egzh3FKJZRAL22yg5p2tXtfg== dependencies: - "@remix-run/router" "1.2.1" - react-router "6.6.1" + "@remix-run/router" "1.3.0" + react-router "6.7.0" -react-router@6.6.1: - version "6.6.1" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.6.1.tgz#17de6cf285f2d1c9721a3afca999c984e5558854" - integrity sha512-YkvlYRusnI/IN0kDtosUCgxqHeulN5je+ew8W+iA1VvFhf86kA+JEI/X/8NqYcr11hCDDp906S+SGMpBheNeYQ== +react-router@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.7.0.tgz#db262684c13b5c2970694084ae9e8531718a0681" + integrity sha512-KNWlG622ddq29MAM159uUsNMdbX8USruoKnwMMQcs/QWZgFUayICSn2oB7reHce1zPj6CG18kfkZIunSSRyGHg== dependencies: - "@remix-run/router" "1.2.1" + "@remix-run/router" "1.3.0" react-toastify@^9.1.1: version "9.1.1" @@ -8076,12 +8048,11 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" -realistic-structured-clone@^2.0.1: - version "2.0.4" - resolved "https://registry.yarnpkg.com/realistic-structured-clone/-/realistic-structured-clone-2.0.4.tgz#7eb4c2319fc3cb72f4c8d3c9e888b11647894b50" - integrity sha512-lItAdBIFHUSe6fgztHPtmmWqKUgs+qhcYLi3wTRUl4OTB3Vb8aBVSjGfQZUvkmJCKoX3K9Wf7kyLp/F/208+7A== +realistic-structured-clone@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/realistic-structured-clone/-/realistic-structured-clone-3.0.0.tgz#7b518049ce2dad41ac32b421cd297075b00e3e35" + integrity sha512-rOjh4nuWkAqf9PWu6JVpOWD4ndI+JHfgiZeMmujYcPi+fvILUu7g6l26TC1K5aBIp34nV+jE1cDO75EKOfHC5Q== dependencies: - core-js "^3.4" domexception "^1.0.1" typeson "^6.1.0" typeson-registry "^1.0.0-alpha.20" @@ -8198,7 +8169,7 @@ resolve.exports@^1.1.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== -resolve@^1.1.7, resolve@^1.10.0, resolve@^1.22.0: +resolve@^1.1.7, resolve@^1.10.0: version "1.22.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== @@ -8216,13 +8187,14 @@ resolve@^1.20.0, resolve@^1.22.1: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^2.0.0-next.3: - version "2.0.0-next.3" - resolved "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz" - integrity sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q== +resolve@^2.0.0-next.4: + version "2.0.0-next.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" + integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" restore-cursor@^3.1.0: version "3.1.0" @@ -8404,10 +8376,10 @@ selfsigned@^2.1.1: resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@7.3.7, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== +semver@7.3.8, semver@^7.3.8: + version "7.3.8" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" + integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== dependencies: lru-cache "^6.0.0" @@ -8416,10 +8388,10 @@ semver@^6.0.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.3.8: - version "7.3.8" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" - integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== +semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: + version "7.3.7" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" + integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== dependencies: lru-cache "^6.0.0" @@ -8735,12 +8707,10 @@ strict-event-emitter@^0.2.4: dependencies: events "^3.3.0" -strict-event-emitter@^0.2.6: - version "0.2.7" - resolved "https://registry.yarnpkg.com/strict-event-emitter/-/strict-event-emitter-0.2.7.tgz#5326a21811551995ab5f8158ea250de57fb2b04e" - integrity sha512-TavbHJ87WD2tDbKI7bTrmc6U4J4Qjh8E9fVvFkIFw2gCu34Wxstn2Yas0+4D78FJN8DOTEzxiT+udBdraRk4UQ== - dependencies: - events "^3.3.0" +strict-event-emitter@^0.4.3: + version "0.4.4" + resolved "https://registry.yarnpkg.com/strict-event-emitter/-/strict-event-emitter-0.4.4.tgz#47e98eb408fbed187460592385f6547ca200cffb" + integrity sha512-rTCFXHBxs2/XvNc7InSkSwUkwyQ0T9eop/Qvm0atNUXpBxjwsJ5yb7Ih/tgHbjPdeCcB4aCP8K4tuo7hNKssNg== string-argv@^0.3.1: version "0.3.1"