refactor: implement bip39
This commit is contained in:
parent
cce13287c3
commit
39f3984ee4
|
@ -11,13 +11,16 @@
|
||||||
"storybook": "storybook dev -p 6006",
|
"storybook": "storybook dev -p 6006",
|
||||||
"build-storybook": "storybook build"
|
"build-storybook": "storybook build"
|
||||||
},
|
},
|
||||||
"files": ["lib"],
|
"files": [
|
||||||
|
"lib"
|
||||||
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lumeweb/kernel-network-registry-client": "0.1.0-develop.10",
|
"@lumeweb/kernel-network-registry-client": "0.1.0-develop.10",
|
||||||
"@lumeweb/libkernel": "0.1.0-develop.65",
|
"@lumeweb/libkernel": "0.1.0-develop.65",
|
||||||
"@radix-ui/react-dialog": "^1.0.5",
|
"@radix-ui/react-dialog": "^1.0.5",
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
"@radix-ui/react-slot": "^1.0.2",
|
"@radix-ui/react-slot": "^1.0.2",
|
||||||
|
"@scure/bip39": "^1.2.1",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^2.0.0",
|
"clsx": "^2.0.0",
|
||||||
"framer-motion": "^10.16.4",
|
"framer-motion": "^10.16.4",
|
||||||
|
|
|
@ -1,48 +1,26 @@
|
||||||
import React from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
import {
|
||||||
|
login,
|
||||||
|
loginComplete,
|
||||||
|
logoutComplete,
|
||||||
|
} from "@lumeweb/libkernel/kernel";
|
||||||
|
|
||||||
export type Session = string;
|
|
||||||
export const LumeIdentityContext = React.createContext<
|
|
||||||
| {
|
|
||||||
session: Session | undefined;
|
|
||||||
setSession: React.Dispatch<React.SetStateAction<Session | undefined>>;
|
|
||||||
}
|
|
||||||
| undefined
|
|
||||||
>(undefined);
|
|
||||||
export function useLumeIndentity() {
|
export function useLumeIndentity() {
|
||||||
const contextValue = React.useContext(LumeIdentityContext);
|
const [loggedIn, setLoggedIn] = useState(false);
|
||||||
|
useEffect(() => {
|
||||||
// When the `session` changes we want to update the `session` in the local storage?
|
loginComplete().then(() => {
|
||||||
React.useEffect(() => {
|
setLoggedIn(true);
|
||||||
if (contextValue?.session) {
|
});
|
||||||
localStorage.setItem("lume-session", contextValue.session);
|
logoutComplete().then(() => {
|
||||||
} else {
|
setLoggedIn(false);
|
||||||
localStorage.removeItem("lume-session");
|
});
|
||||||
}
|
});
|
||||||
}, [contextValue?.session]);
|
|
||||||
|
|
||||||
// Get the session from the local storage
|
|
||||||
React.useEffect(() => {
|
|
||||||
const session = localStorage.getItem("lume-session");
|
|
||||||
if (session) {
|
|
||||||
contextValue?.setSession(session);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
if (contextValue === undefined) {
|
|
||||||
throw new Error(
|
|
||||||
"useLumeIdentity hook is being used outside of its context. Please ensure that it is wrapped within a <LumeIdentityProvider>."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isSignedIn: !!contextValue.session,
|
isSignedIn: loggedIn,
|
||||||
signIn: (key: string) => {
|
async signIn(key: Uint8Array) {
|
||||||
console.log("signing in with key", key);
|
await login(key);
|
||||||
// TODO: From the key generate a session, and store it
|
|
||||||
contextValue.setSession("session");
|
|
||||||
},
|
|
||||||
signOut: () => {
|
|
||||||
contextValue.setSession(undefined);
|
|
||||||
},
|
},
|
||||||
|
signOut: () => {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,13 @@ import { AnimatePresence, m } from "framer-motion";
|
||||||
import { useLumeIndentity } from "./LumeIdentityContext";
|
import { useLumeIndentity } from "./LumeIdentityContext";
|
||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
|
|
||||||
|
import * as bip39 from "@scure/bip39";
|
||||||
|
import { wordlist } from "@scure/bip39/wordlists/english";
|
||||||
|
|
||||||
|
async function seedToKey(seed: string) {
|
||||||
|
return bip39.mnemonicToSeed(seed);
|
||||||
|
}
|
||||||
|
|
||||||
// Extracted components
|
// Extracted components
|
||||||
const SubmitButtonComponent = () => {
|
const SubmitButtonComponent = () => {
|
||||||
const { setVisibleComponent } = useSwitchableComponent();
|
const { setVisibleComponent } = useSwitchableComponent();
|
||||||
|
@ -20,8 +27,7 @@ const SubmitButtonComponent = () => {
|
||||||
<Button
|
<Button
|
||||||
className="w-full h-12"
|
className="w-full h-12"
|
||||||
variant={"outline"}
|
variant={"outline"}
|
||||||
onClick={() => setVisibleComponent(SeedPhraseInput)}
|
onClick={() => setVisibleComponent(SeedPhraseInput)}>
|
||||||
>
|
|
||||||
<span className="text-center text-lg font-normal leading-normal">
|
<span className="text-center text-lg font-normal leading-normal">
|
||||||
Sign in with Account Key
|
Sign in with Account Key
|
||||||
</span>
|
</span>
|
||||||
|
@ -34,7 +40,7 @@ const SeedPhraseInputComponent = () => {
|
||||||
return (
|
return (
|
||||||
<m.form
|
<m.form
|
||||||
className="flex-col flex gap-y-4"
|
className="flex-col flex gap-y-4"
|
||||||
onSubmit={(e) => {
|
onSubmit={async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const target = e.target as typeof e.target & {
|
const target = e.target as typeof e.target & {
|
||||||
elements: {
|
elements: {
|
||||||
|
@ -42,17 +48,15 @@ const SeedPhraseInputComponent = () => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
const seedPhrase = target.elements.seedPhrase.value;
|
const seedPhrase = target.elements.seedPhrase.value;
|
||||||
signIn(seedPhrase);
|
signIn(await seedToKey(seedPhrase));
|
||||||
}}
|
}}>
|
||||||
>
|
|
||||||
<Input className="h-12 w-full text-lg" name="seedPhrase" />
|
<Input className="h-12 w-full text-lg" name="seedPhrase" />
|
||||||
<m.div
|
<m.div
|
||||||
initial={{ y: 50 }}
|
initial={{ y: 50 }}
|
||||||
animate={{ y: 0 }}
|
animate={{ y: 0 }}
|
||||||
exit={{ y: -50 }}
|
exit={{ y: -50 }}
|
||||||
transition={{ type: "just", delay: 0.1 }}
|
transition={{ type: "just", delay: 0.1 }}
|
||||||
className="h-12"
|
className="h-12">
|
||||||
>
|
|
||||||
<Button className="w-full h-full" role="submit">
|
<Button className="w-full h-full" role="submit">
|
||||||
<span className="text-center text-lg font-normal leading-normal">
|
<span className="text-center text-lg font-normal leading-normal">
|
||||||
Sign in
|
Sign in
|
||||||
|
@ -77,12 +81,10 @@ const SetupAccountKeyComponent = () => {
|
||||||
style={{ maxWidth: width ?? "auto" }}
|
style={{ maxWidth: width ?? "auto" }}
|
||||||
ref={(t) =>
|
ref={(t) =>
|
||||||
setTimeout(() => setWidth(t!.getBoundingClientRect().width!), 0)
|
setTimeout(() => setWidth(t!.getBoundingClientRect().width!), 0)
|
||||||
}
|
}>
|
||||||
>
|
|
||||||
<Button
|
<Button
|
||||||
className="w-full h-full"
|
className="w-full h-full"
|
||||||
onClick={() => setVisibleComponent(SeedPhraseGeneration)}
|
onClick={() => setVisibleComponent(SeedPhraseGeneration)}>
|
||||||
>
|
|
||||||
<span className="text-center text-lg font-normal leading-normal">
|
<span className="text-center text-lg font-normal leading-normal">
|
||||||
I get it, I'll keep it safe. Let's see the key.
|
I get it, I'll keep it safe. Let's see the key.
|
||||||
</span>
|
</span>
|
||||||
|
@ -99,8 +101,7 @@ const SeedPhraseGenerationComponent = ({ phraseLength = 12 }) => {
|
||||||
const { signIn } = useLumeIndentity();
|
const { signIn } = useLumeIndentity();
|
||||||
|
|
||||||
const phrases = useMemo(() => {
|
const phrases = useMemo(() => {
|
||||||
// TODO: Replace with actual BIP39 or whatever is used for phrase generation
|
return bip39.generateMnemonic(wordlist, phraseLength).split(" ");
|
||||||
return Array(phraseLength).fill("a phrase");
|
|
||||||
}, [phraseLength]);
|
}, [phraseLength]);
|
||||||
|
|
||||||
const key = useMemo(() => {
|
const key = useMemo(() => {
|
||||||
|
@ -127,16 +128,14 @@ const SeedPhraseGenerationComponent = ({ phraseLength = 12 }) => {
|
||||||
right: -20,
|
right: -20,
|
||||||
bottom: 120,
|
bottom: 120,
|
||||||
}}
|
}}
|
||||||
transition={{ type: "tween", duration: 0.1 }}
|
transition={{ type: "tween", duration: 0.1 }}></m.div>
|
||||||
></m.div>
|
|
||||||
) : null}
|
) : null}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
<div className="z-20 relative mb-2.5 w-full h-full flex-wrap justify-center items-center gap-2.5 inline-flex">
|
<div className="z-20 relative mb-2.5 w-full h-full flex-wrap justify-center items-center gap-2.5 inline-flex">
|
||||||
{phrases.map((phrase, index) => (
|
{phrases.map((phrase, index) => (
|
||||||
<div
|
<div
|
||||||
key={`SeedPhrase_${index}`}
|
key={`SeedPhrase_${index}`}
|
||||||
className={`justify-center items-center gap-2.5 flex w-[calc(33%-10px)] h-10 rounded border border-current relative ring-current text-neutral-700`}
|
className={`justify-center items-center gap-2.5 flex w-[calc(33%-10px)] h-10 rounded border border-current relative ring-current text-neutral-700`}>
|
||||||
>
|
|
||||||
<span className=" text-white text-md font-normal leading-normal w-full h-fit px-2.5 bg-transparent text-center">
|
<span className=" text-white text-md font-normal leading-normal w-full h-fit px-2.5 bg-transparent text-center">
|
||||||
{phrase}
|
{phrase}
|
||||||
</span>
|
</span>
|
||||||
|
@ -152,8 +151,7 @@ const SeedPhraseGenerationComponent = ({ phraseLength = 12 }) => {
|
||||||
initial={{ opacity: 0, y: 50 }}
|
initial={{ opacity: 0, y: 50 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
exit={{ opacity: 0, y: 50 }}
|
exit={{ opacity: 0, y: 50 }}
|
||||||
transition={{ type: "linear", delay: 0.2, duration: 0.5 }}
|
transition={{ type: "linear", delay: 0.2, duration: 0.5 }}>
|
||||||
>
|
|
||||||
<ExclamationTriangleIcon className="w-14 h-14" />
|
<ExclamationTriangleIcon className="w-14 h-14" />
|
||||||
<span>Make sure to write this down for safe keeping.</span>
|
<span>Make sure to write this down for safe keeping.</span>
|
||||||
</m.div>
|
</m.div>
|
||||||
|
@ -166,8 +164,7 @@ const SeedPhraseGenerationComponent = ({ phraseLength = 12 }) => {
|
||||||
: ""
|
: ""
|
||||||
}`}
|
}`}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={copyPhrasesToClipboard}
|
onClick={copyPhrasesToClipboard}>
|
||||||
>
|
|
||||||
{buttonClickedState === "clicked" ? (
|
{buttonClickedState === "clicked" ? (
|
||||||
<CheckIcon className="w-5 h-5 mr-2.5" />
|
<CheckIcon className="w-5 h-5 mr-2.5" />
|
||||||
) : (
|
) : (
|
||||||
|
@ -180,8 +177,7 @@ const SeedPhraseGenerationComponent = ({ phraseLength = 12 }) => {
|
||||||
<Button
|
<Button
|
||||||
className="z-20 w-full h-12 text-white bg-neutral-700 hover:bg-neutral-800"
|
className="z-20 w-full h-12 text-white bg-neutral-700 hover:bg-neutral-800"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={() => setStep(1)}
|
onClick={() => setStep(1)}>
|
||||||
>
|
|
||||||
Continue
|
Continue
|
||||||
</Button>
|
</Button>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -192,9 +188,10 @@ const SeedPhraseGenerationComponent = ({ phraseLength = 12 }) => {
|
||||||
initial={{ opacity: 0, y: -50 }}
|
initial={{ opacity: 0, y: -50 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
exit={{ opacity: 0, y: 50 }}
|
exit={{ opacity: 0, y: 50 }}
|
||||||
transition={{ type: "linear", delay: 2, duration: 0.5 }}
|
transition={{ type: "linear", delay: 2, duration: 0.5 }}>
|
||||||
>
|
<Button
|
||||||
<Button className="w-full h-full" onClick={() => signIn(key)}>
|
className="w-full h-full"
|
||||||
|
onClick={async () => signIn(await seedToKey(key))}>
|
||||||
Sign In
|
Sign In
|
||||||
</Button>
|
</Button>
|
||||||
</m.div>
|
</m.div>
|
||||||
|
@ -206,17 +203,17 @@ const SeedPhraseGenerationComponent = ({ phraseLength = 12 }) => {
|
||||||
|
|
||||||
export const SubmitButton = makeSwitchable(
|
export const SubmitButton = makeSwitchable(
|
||||||
SubmitButtonComponent,
|
SubmitButtonComponent,
|
||||||
"submit-button"
|
"submit-button",
|
||||||
);
|
);
|
||||||
export const SeedPhraseInput = makeSwitchable(
|
export const SeedPhraseInput = makeSwitchable(
|
||||||
SeedPhraseInputComponent,
|
SeedPhraseInputComponent,
|
||||||
"seed-phrase-input"
|
"seed-phrase-input",
|
||||||
);
|
);
|
||||||
export const SetupAccountKey = makeSwitchable(
|
export const SetupAccountKey = makeSwitchable(
|
||||||
SetupAccountKeyComponent,
|
SetupAccountKeyComponent,
|
||||||
"setup-account-key"
|
"setup-account-key",
|
||||||
);
|
);
|
||||||
export const SeedPhraseGeneration = makeSwitchable(
|
export const SeedPhraseGeneration = makeSwitchable(
|
||||||
SeedPhraseGenerationComponent,
|
SeedPhraseGenerationComponent,
|
||||||
"seed-phrase-form"
|
"seed-phrase-form",
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue