diff --git a/src/app/components/IconLinkCard/IconLinkCard.tsx b/src/app/components/IconLinkCard/IconLinkCard.tsx index 94898f5913..1a7a284343 100644 --- a/src/app/components/IconLinkCard/IconLinkCard.tsx +++ b/src/app/components/IconLinkCard/IconLinkCard.tsx @@ -15,11 +15,11 @@ export function IconLinkCard({ }: IconLinkCardProps) { return (
{icon}
-
+
{title}
@@ -27,7 +27,7 @@ export function IconLinkCard({ {description}
-
+
diff --git a/src/app/screens/Accounts/BackupMnemonic/index.tsx b/src/app/screens/Accounts/BackupMnemonic/index.tsx index 395dfaf736..4d81b5faf9 100644 --- a/src/app/screens/Accounts/BackupMnemonic/index.tsx +++ b/src/app/screens/Accounts/BackupMnemonic/index.tsx @@ -38,6 +38,7 @@ function BackupMnemonic() { fetchData(); }, [fetchData]); + // TODO: set isMnemonicBackupDone, once new ui for screen is merged return loading ? (
diff --git a/src/app/screens/Accounts/GenerateMnemonic/index.tsx b/src/app/screens/Accounts/GenerateMnemonic/index.tsx index a9d4843162..b5b563e649 100644 --- a/src/app/screens/Accounts/GenerateMnemonic/index.tsx +++ b/src/app/screens/Accounts/GenerateMnemonic/index.tsx @@ -53,7 +53,10 @@ function GenerateMnemonic() { } await api.setMnemonic(id, mnemonic); - await api.editAccount(id, { useMnemonicForLnurlAuth: true }); + await api.editAccount(id, { + useMnemonicForLnurlAuth: true, + isMnemonicBackupDone: true, + }); toast.success(t("saved")); // go to account settings diff --git a/src/app/screens/Home/DefaultView/index.tsx b/src/app/screens/Home/DefaultView/index.tsx index 1f7380cb5e..5c4261be98 100644 --- a/src/app/screens/Home/DefaultView/index.tsx +++ b/src/app/screens/Home/DefaultView/index.tsx @@ -2,6 +2,12 @@ import { ArrowRightIcon } from "@bitcoin-design/bitcoin-icons-react/filled"; import Button from "@components/Button"; import Loading from "@components/Loading"; import TransactionsTable from "@components/TransactionsTable"; +import { + PopiconsArrowDownLine, + PopiconsBulbLine, + PopiconsDownloadLine, + PopiconsKeyLine, +} from "@popicons/react"; import dayjs from "dayjs"; import relativeTime from "dayjs/plugin/relativeTime"; import { FC, useEffect, useState } from "react"; @@ -9,12 +15,13 @@ import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import BalanceBox from "~/app/components/BalanceBox"; import Hyperlink from "~/app/components/Hyperlink"; +import { IconLinkCard } from "~/app/components/IconLinkCard/IconLinkCard"; import SkeletonLoader from "~/app/components/SkeletonLoader"; import toast from "~/app/components/Toast"; import { useAccount } from "~/app/context/AccountContext"; import { useTransactions } from "~/app/hooks/useTransactions"; import { PublisherLnData } from "~/app/screens/Home/PublisherLnData"; -import api from "~/common/lib/api"; +import api, { GetAccountRes } from "~/common/lib/api"; import msg from "~/common/lib/msg"; import utils from "~/common/lib/utils"; import type { Battery } from "~/types"; @@ -40,6 +47,7 @@ const DefaultView: FC = (props) => { const lightningAddress = account?.lightningAddress || ""; const [isBlockedUrl, setIsBlockedUrl] = useState(false); + const [currentAccount, setCurrentAccount] = useState(); const { transactions, isLoadingTransactions, loadTransactions } = useTransactions(); @@ -62,6 +70,18 @@ const DefaultView: FC = (props) => { } }, [props.currentUrl]); + useEffect(() => { + (async () => { + try { + const account = await api.getAccount(); + setCurrentAccount(account); + } catch (e) { + console.error(e); + if (e instanceof Error) toast.error(`Error: ${e.message}`); + } + })(); + }, []); + const unblock = async () => { try { if (props.currentUrl?.host) { @@ -88,6 +108,10 @@ const DefaultView: FC = (props) => { } } + function openOptions(path: string) { + utils.openPage(`options.html#/${path}`); + } + return (
{props.renderPublisherWidget && !!props.lnDataFromCurrentTab?.length && ( @@ -164,10 +188,88 @@ const DefaultView: FC = (props) => { {!isLoading && (
+
+ {transactions.length == 0 && ( + + } + onClick={() => { + utils.openUrl( + "https://guides.getalby.com/user-guide/v/alby-account-and-browser-extension/" + ); + }} + /> + )} + + {!( + currentAccount?.hasMnemonic && + currentAccount?.isMnemonicBackupDone + ) && ( + + } + onClick={async () => { + if (currentAccount?.hasMnemonic) { + openOptions( + `accounts/${currentAccount?.id}/secret-key/backup` + ); + } else { + openOptions( + `accounts/${currentAccount?.id}/secret-key/new` + ); + } + }} + /> + )} + + {transactions.length == 0 && ( + + } + onClick={() => { + navigate("/receive"); + }} + /> + )} + + {!( + currentAccount?.hasMnemonic && + currentAccount?.isMnemonicBackupDone + ) && ( + + } + onClick={async () => { + openOptions( + `accounts/${currentAccount?.id}/secret-key/import` + ); + }} + /> + )} +
{!isLoading && transactions.length > 0 && ( diff --git a/src/app/screens/Receive/index.tsx b/src/app/screens/Receive/index.tsx index 9558692df9..fb913cc41e 100644 --- a/src/app/screens/Receive/index.tsx +++ b/src/app/screens/Receive/index.tsx @@ -1,12 +1,12 @@ import { - BitcoinCircleIcon, + BitcoinIcon, CaretLeftIcon, CopyIcon, - LightningIcon, } from "@bitcoin-design/bitcoin-icons-react/outline"; import Container from "@components/Container"; import Header from "@components/Header"; import IconButton from "@components/IconButton"; +import { PopiconsBoltLine, PopiconsWithdrawalLine } from "@popicons/react"; import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; @@ -15,7 +15,6 @@ import QRCode from "~/app/components/QRCode"; import SkeletonLoader from "~/app/components/SkeletonLoader"; import toast from "~/app/components/Toast"; import { useAccount } from "~/app/context/AccountContext"; -import RedeemIcon from "~/app/icons/RedeemIcon"; import { isAlbyLNDHubAccount, isAlbyOAuthAccount } from "~/app/utils"; import api from "~/common/lib/api"; import { IconLinkCard } from "../../components/IconLinkCard/IconLinkCard"; @@ -123,7 +122,9 @@ function Receive() { } + icon={ + + } onClick={() => { navigate("/receive/invoice"); }} @@ -132,7 +133,9 @@ function Receive() { } + icon={ + + } onClick={() => { navigate("/onChainReceive"); }} @@ -141,7 +144,9 @@ function Receive() { } + icon={ + + } onClick={() => { navigate("/lnurlRedeem"); }} diff --git a/src/common/lib/api.ts b/src/common/lib/api.ts index 19ddfd1bfe..fb8375ec8c 100644 --- a/src/common/lib/api.ts +++ b/src/common/lib/api.ts @@ -57,6 +57,7 @@ export interface GetAccountRes extends Pick { liquidEnabled: boolean; nostrEnabled: boolean; hasMnemonic: boolean; + isMnemonicBackupDone: boolean; hasImportedNostrKey: boolean; bitcoinNetwork: BitcoinNetworkType; useMnemonicForLnurlAuth: boolean; 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 1b6f108575..1948d04cbb 100644 --- a/src/extension/background-script/actions/accounts/__tests__/add.test.ts +++ b/src/extension/background-script/actions/accounts/__tests__/add.test.ts @@ -28,6 +28,7 @@ const message: MessageAccountAdd = { config: "123456config", name: "purple", nostrPrivateKey: "123456nostr", + isMnemonicBackupDone: false, }, origin: { internal: true }, prompt: true, @@ -59,6 +60,7 @@ describe("add account to account-list", () => { config: "secret-config-string-42", name: "purple", nostrPrivateKey: "123456nostr", + isMnemonicBackupDone: false, }, }, }); @@ -107,6 +109,7 @@ describe("add account to account-list", () => { config: "secret-config-string-42", name: "purple", nostrPrivateKey: "123456nostr", + isMnemonicBackupDone: false, }, "666": { config: "xyz", diff --git a/src/extension/background-script/actions/accounts/__tests__/get.test.ts b/src/extension/background-script/actions/accounts/__tests__/get.test.ts index 6022ed3c94..71f714eb89 100644 --- a/src/extension/background-script/actions/accounts/__tests__/get.test.ts +++ b/src/extension/background-script/actions/accounts/__tests__/get.test.ts @@ -30,6 +30,7 @@ const mockState = { mnemonic: btcFixture.mnemonic, bitcoinNetwork: "regtest", useMnemonicForLnurlAuth: true, + isMnemonicBackupDone: true, }, "1e1e8ea6-493e-480b-9855-303d37506e97": { config: "config-123-456", @@ -65,6 +66,7 @@ describe("account info", () => { hasImportedNostrKey: true, bitcoinNetwork: "bitcoin", useMnemonicForLnurlAuth: false, + isMnemonicBackupDone: true, }; expect(await getAccount(message)).toStrictEqual({ @@ -92,6 +94,7 @@ describe("account info", () => { hasImportedNostrKey: true, bitcoinNetwork: "regtest", useMnemonicForLnurlAuth: true, + isMnemonicBackupDone: true, }; expect(await getAccount(message)).toStrictEqual({ diff --git a/src/extension/background-script/actions/accounts/add.ts b/src/extension/background-script/actions/accounts/add.ts index df38e0d0ad..29e8f9eda2 100644 --- a/src/extension/background-script/actions/accounts/add.ts +++ b/src/extension/background-script/actions/accounts/add.ts @@ -27,6 +27,7 @@ const add = async (message: MessageAccountAdd) => { ...newAccount, id: accountId, name, + isMnemonicBackupDone: false, }; const mnemonic = await generateMnemonic({ diff --git a/src/extension/background-script/actions/accounts/edit.ts b/src/extension/background-script/actions/accounts/edit.ts index 597b56e2bb..262c48fb75 100644 --- a/src/extension/background-script/actions/accounts/edit.ts +++ b/src/extension/background-script/actions/accounts/edit.ts @@ -22,6 +22,11 @@ const edit = async (message: MessageAccountEdit) => { message.args.useMnemonicForLnurlAuth; } + if (message.args.isMnemonicBackupDone !== undefined) { + accounts[accountId].isMnemonicBackupDone = + message.args.isMnemonicBackupDone; + } + state.setState({ accounts }); // make sure we immediately persist the updated accounts await state.getState().saveToStorage(); diff --git a/src/extension/background-script/actions/accounts/get.ts b/src/extension/background-script/actions/accounts/get.ts index bffb228e67..7d102a892c 100644 --- a/src/extension/background-script/actions/accounts/get.ts +++ b/src/extension/background-script/actions/accounts/get.ts @@ -22,6 +22,10 @@ const get = async (message: MessageAccountGet) => { liquidEnabled: !!account.mnemonic, nostrEnabled: !!account.nostrPrivateKey, hasMnemonic: !!account.mnemonic, + // for existing accounts consider mnemonic backup already done + isMnemonicBackupDone: account.isMnemonicBackupDone + ? account.isMnemonicBackupDone + : true, // Note: undefined (default for new accounts) it is also considered imported hasImportedNostrKey: account.hasImportedNostrKey !== false, bitcoinNetwork: account.bitcoinNetwork || "bitcoin", diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index 19c7ed1e4d..2c5364eff4 100644 --- a/src/i18n/locales/en/translation.json +++ b/src/i18n/locales/en/translation.json @@ -373,7 +373,26 @@ "is_blocked_hint": "Alby is currently disabled on {{host}}", "block_removed": "Enabled {{host}}. Please reload the website.", "no_transactions": "No transactions for this account yet.", - "see_all": "See all" + "see_all": "See all", + "actions": { + "get_started": { + "title": "Get Started With Alby Extension", + "description": "New to Alby Extension? Check out our guides, videos, and explanations" + }, + "backup_masterkey": { + "title": "Back up your Master Key", + "description": "Learn what Master Key is, how it’s used for Nostr and make sure to back up" + }, + + "receive_bitcoin": { + "title": "Receive bitcoin", + "description": "Get bitcoin to your lightning address, a lightning invoice or bitcoin address." + }, + "import_masterkey": { + "title": "Import your Master Key", + "description": "If you already have a Master Key, you can import it using recovery phrase" + } + } } }, "accounts": { diff --git a/src/types.ts b/src/types.ts index 6bc4f7f414..9d2a499e59 100644 --- a/src/types.ts +++ b/src/types.ts @@ -25,6 +25,7 @@ export interface Account { mnemonic?: string | null; hasImportedNostrKey?: boolean; bitcoinNetwork?: BitcoinNetworkType; + isMnemonicBackupDone?: boolean; useMnemonicForLnurlAuth?: boolean; avatarUrl?: string; } @@ -231,6 +232,7 @@ export interface MessageAccountEdit extends MessageDefault { name?: Account["name"]; bitcoinNetwork?: BitcoinNetworkType; useMnemonicForLnurlAuth?: boolean; + isMnemonicBackupDone?: boolean; }; action: "editAccount"; }