Add "Migrate to signing device" option in account manager

This commit is contained in:
hzrd149 2024-12-18 11:29:49 -06:00
parent 2faa014720
commit 697d4c67a2
7 changed files with 96 additions and 28 deletions

View File

@ -0,0 +1,5 @@
---
"nostrudel": minor
---
Add "Migrate to signing device" option in account manager

View File

@ -49,7 +49,7 @@
"applesauce-lists": "^0.10.0",
"applesauce-net": "^0.10.0",
"applesauce-react": "^0.10.0",
"applesauce-signer": "^0.10.0",
"applesauce-signer": "0.0.0-next-20241218172722",
"bech32": "^2.0.0",
"blossom-client-sdk": "^2.1.0",
"blossom-drive-sdk": "^0.4.1",

10
pnpm-lock.yaml generated
View File

@ -112,8 +112,8 @@ importers:
specifier: ^0.10.0
version: 0.10.0(typescript@5.7.2)
applesauce-signer:
specifier: ^0.10.0
version: 0.10.0(typescript@5.7.2)
specifier: 0.0.0-next-20241218172722
version: 0.0.0-next-20241218172722(typescript@5.7.2)
bech32:
specifier: ^2.0.0
version: 2.0.0
@ -1927,8 +1927,8 @@ packages:
applesauce-react@0.10.0:
resolution: {integrity: sha512-du4EC4cBM9bWbyRVllciPCNwwwKTEsTqSjsB5/h/tktfG/2ubiZfUsDD9o55o0eWx/drDabIN4sjnv3OEwvXww==}
applesauce-signer@0.10.0:
resolution: {integrity: sha512-2Cn2ZUxk47cBJBFoUl9DB37mjgg8/8GwTAG7csPqtooJ4nUh0ylO4Gh1Mr/401Lc8tjZJvpVBySWpCeXrnG1rQ==}
applesauce-signer@0.0.0-next-20241218172722:
resolution: {integrity: sha512-0DvkuVLCiFr7CjLeUr+AHxtiLBwOf7TiDBmpbVTdDgmNYo/hlvYOvgVDfISkpB8DRE1/44BlWBXFxbKmfVWkxg==}
argparse@1.0.10:
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
@ -6469,7 +6469,7 @@ snapshots:
- supports-color
- typescript
applesauce-signer@0.10.0(typescript@5.7.2):
applesauce-signer@0.0.0-next-20241218172722(typescript@5.7.2):
dependencies:
'@noble/hashes': 1.6.1
'@noble/secp256k1': 1.7.1

View File

@ -0,0 +1,62 @@
import { Button, Flex, Heading, Link, useToast } from "@chakra-ui/react";
import { PasswordSigner, SerialPortSigner, SimpleSigner } from "applesauce-signer";
import { useState } from "react";
import useAsyncErrorHandler from "../../../../hooks/use-async-error-handler";
import useCurrentAccount from "../../../../hooks/use-current-account";
import SerialPortAccount from "../../../../classes/accounts/serial-port-account";
import accountService from "../../../../services/account";
export default function MigrateAccountToDevice() {
if (!SerialPortSigner.SUPPORTED) return null;
const toast = useToast();
const current = useCurrentAccount();
const [loading, setLoading] = useState(false);
const migrate = useAsyncErrorHandler(async () => {
try {
setLoading(true);
if (!current?.signer) throw new Error("Account missing signer");
const device = new SerialPortSigner();
if (current.signer instanceof SimpleSigner) {
// send key to device
await device.restore(current.signer.key);
} else if (current.signer instanceof PasswordSigner) {
// unlock the signer first
if (!current.signer.unlocked) {
const password = window.prompt("Decryption password");
if (password === null) throw new Error("Password required");
await current.signer.unlock(password);
}
await device.restore(current.signer.key!);
} else throw new Error("Unsupported signer type");
// replace existing account
const deviceAccount = new SerialPortAccount(current.pubkey);
accountService.replaceAccount(current.pubkey, deviceAccount);
accountService.switchAccount(deviceAccount.pubkey);
} catch (error) {
if (error instanceof Error) toast({ description: error.message, status: "error" });
}
setLoading(false);
}, [setLoading, current]);
return (
<Flex direction="column" gap="2">
<Heading size="md" mt="2">
Migrate to{" "}
<Link isExternal href="https://github.com/lnbits/nostr-signing-device" color="blue.500">
nostr-signing-device
</Link>
</Heading>
<Flex gap="2" maxW="lg">
<Button colorScheme="purple" isLoading={loading} onClick={migrate}>
Start migration
</Button>
</Flex>
</Flex>
);
}

View File

@ -13,10 +13,10 @@ import {
import { useForm } from "react-hook-form";
import { PasswordSigner } from "applesauce-signer";
import useCurrentAccount from "../../../hooks/use-current-account";
import EyeOff from "../../../components/icons/eye-off";
import Eye from "../../../components/icons/eye";
import { CopyIconButton } from "../../../components/copy-icon-button";
import useCurrentAccount from "../../../../hooks/use-current-account";
import EyeOff from "../../../../components/icons/eye-off";
import Eye from "../../../../components/icons/eye";
import { CopyIconButton } from "../../../../components/copy-icon-button";
const fake = Array(48).fill("x");

View File

@ -15,12 +15,12 @@ import { encrypt } from "nostr-tools/nip49";
import { useForm } from "react-hook-form";
import { SimpleSigner } from "applesauce-signer";
import useCurrentAccount from "../../../hooks/use-current-account";
import EyeOff from "../../../components/icons/eye-off";
import Eye from "../../../components/icons/eye";
import { CopyIconButton } from "../../../components/copy-icon-button";
import accountService from "../../../services/account";
import PasswordAccount from "../../../classes/accounts/password-account";
import useCurrentAccount from "../../../../hooks/use-current-account";
import EyeOff from "../../../../components/icons/eye-off";
import Eye from "../../../../components/icons/eye";
import { CopyIconButton } from "../../../../components/copy-icon-button";
import accountService from "../../../../services/account";
import PasswordAccount from "../../../../classes/accounts/password-account";
const fake = Array(48).fill("x");

View File

@ -1,6 +1,6 @@
import { Box, Button, ButtonGroup, Divider, Flex, Heading, Text } from "@chakra-ui/react";
import { useNavigate } from "react-router-dom";
import { PasswordSigner, SimpleSigner } from "applesauce-signer";
import { PasswordSigner, SerialPortSigner, SimpleSigner } from "applesauce-signer";
import { useObservable } from "applesauce-react/hooks";
import VerticalPageLayout from "../../../components/vertical-page-layout";
@ -10,21 +10,22 @@ import UserName from "../../../components/user/user-name";
import UserDnsIdentity from "../../../components/user/user-dns-identity";
import accountService from "../../../services/account";
import AccountTypeBadge from "../../../components/account-info-badge";
import SimpleSignerBackup from "./simple-signer-backup";
import PasswordSignerBackup from "./password-signer-backup";
import SimpleSignerBackup from "./components/simple-signer-backup";
import PasswordSignerBackup from "./components/password-signer-backup";
import { ReactNode } from "react";
import MigrateAccountToDevice from "./components/migrate-to-device";
function AccountBackup() {
const account = useCurrentAccount()!;
if (account.signer instanceof PasswordSigner && account.signer.ncryptsec) {
return <PasswordSignerBackup />;
}
if (account.signer instanceof SimpleSigner && account.signer.key) {
return <SimpleSignerBackup />;
}
return null;
return (
<>
{account.signer instanceof SimpleSigner && account.signer.key && <SimpleSignerBackup />}
{account.signer instanceof PasswordSigner && account.signer.ncryptsec && <SimpleSignerBackup />}
{(account.signer instanceof SimpleSigner || account.signer instanceof PasswordSigner) &&
SerialPortSigner.SUPPORTED && <MigrateAccountToDevice />}
</>
);
}
export default function AccountSettings() {