blowater/UI/signIn.tsx
BlowaterNostr 1fc7dbf731 #58
2023-07-09 12:43:50 +00:00

248 lines
8.1 KiB
TypeScript

/** @jsx h */
import { h } from "https://esm.sh/preact@10.11.3";
import { tw } from "https://esm.sh/twind@0.16.16";
import { GetLocalStorageAccountContext, Nip7ExtensionContext } from "./account-context.ts";
import { ButtonClass, CenterClass, DividerClass } from "./components/tw.ts";
import KeyView from "./key-view.tsx";
import { PrivateKey } from "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/key.ts";
import {
InMemoryAccountContext,
NostrAccountContext,
} from "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/nostr.ts";
import { EventBus, EventEmitter } from "../event-bus.ts";
export type SignInEvent = {
type: "signin";
privateKey?: PrivateKey; // undefined means sign in with extentions
} | {
type: "editSignInPrivateKey";
privateKey: string;
} | {
type: "createNewAccount";
} | {
type: "backToSignInPage";
};
type Props = {
eventBus: EventEmitter<SignInEvent>;
} & SignInModel;
export type SignInModel = {
state: "newAccount" | "enterPrivateKey";
privateKey: string;
warningString?: string;
};
const AlbyURL = "https://getalby.com/";
type SignInState = "none" | "nip07" | "local";
export function getSignInState(): SignInState {
const state = localStorage.getItem("SignInState");
if (state === null) {
return "none";
}
return state as SignInState;
}
export function setSignInState(state: SignInState) {
localStorage.setItem("SignInState", state);
}
////////////////////////
// Check Login Status //
////////////////////////
export async function getCurrentSignInCtx() {
if (getSignInState() === "nip07") {
const albyCtx = await Nip7ExtensionContext.New();
if (albyCtx instanceof Error) {
return albyCtx;
}
if (albyCtx === undefined) {
setSignInState("none");
}
return albyCtx;
}
if (getSignInState() === "local") {
const ctx = GetLocalStorageAccountContext();
if (ctx instanceof Error) {
throw ctx;
}
if (ctx === undefined) {
console.log("GetLocalStorageAccountContext is undefined");
setSignInState("none");
}
return ctx;
}
return undefined;
}
export async function signInWithExtension() {
const albyCtx = await Nip7ExtensionContext.New();
if (albyCtx instanceof Error) {
return albyCtx;
}
if (albyCtx === undefined) {
open(AlbyURL);
} else {
try {
const enabled = await albyCtx.enable();
if (enabled) {
setSignInState("nip07");
} else {
console.error("User rejected Alby login");
}
} catch (e) {
console.log(e);
return "You rejected Alby login. Refresh page to enable it again.";
}
}
return albyCtx;
}
export function signInWithPrivateKey(privateKey: PrivateKey) {
const ctx = InMemoryAccountContext.New(privateKey);
if (ctx instanceof Error) {
throw ctx;
}
localStorage.setItem("MPK", privateKey.hex);
setSignInState("local");
return ctx;
}
export function SignIn(props: Props) {
if (props.state == "newAccount") {
const privateKey = PrivateKey.Generate();
return (
<div
class={tw`fixed inset-0 bg-[#313338] flex items-center justify-center px-4`}
>
<div class={tw`flex flex-col w-[40rem]`}>
<KeyView
privateKey={privateKey}
publicKey={privateKey.toPublicKey()}
/>
<button
onClick={() => {
props.eventBus.emit({
type: "backToSignInPage",
});
}}
class={tw`w-full mt-8 bg-[#404249] hover:bg-[#2B2D31] ${ButtonClass}`}
>
Back
</button>
<button
onClick={() => {
props.eventBus.emit({
type: "signin",
privateKey: privateKey,
});
}}
class={tw`w-full mt-8 bg-[#ED4545] hover:bg-[#E03030] ${ButtonClass}`}
>
Sign In
</button>
</div>
</div>
);
}
let privateKey = PrivateKey.FromHex(props.privateKey);
if (privateKey instanceof Error) {
privateKey = PrivateKey.FromBech32(props.privateKey);
}
return (
<div
class={tw`fixed inset-0 bg-[#313338] flex items-center justify-center px-4`}
>
<div class={tw`w-[40rem] relative`}>
<img
class={tw`w-40 h-40 m-auto mb-16 absolute`}
style={{
top: "-12rem",
left: "calc(50% - 5rem)",
}}
src="logo.png"
alt="Logo"
/>
<h1
class={tw`text-[#F3F4EA] text-center text-[2rem]`}
>
Welcome to Blowater
</h1>
<input
onInput={(e) => {
props.eventBus.emit({
type: "editSignInPrivateKey",
privateKey: e.currentTarget.value,
});
}}
placeholder="Input your private key here"
type="text"
class={tw`w-full px-4 py-2 focus-visible:outline-none rounded-lg mt-8`}
/>
{privateKey instanceof Error
? (
<p class={tw`text-[#F3F4EA] mt-2`}>
Private Key has to be 64 letters hex-decimal or 63 letters nsec string
</p>
)
: undefined}
<button
onClick={() => {
if (privateKey instanceof PrivateKey) {
props.eventBus.emit({
type: "signin",
privateKey: privateKey,
});
}
}}
disabled={privateKey instanceof Error}
class={tw`w-full bg-[#2B2D31] hover:bg-[#404249] mt-4 disabled:bg-[#404249] ${ButtonClass}`}
>
Sign In
</button>
<div class={tw`h-16 w-full relative ${CenterClass}`}>
<div class={tw`${DividerClass}`}></div>
<div class={tw`absolute w-full h-full ${CenterClass}`}>
<span class={tw`bg-[#313338] px-2 text-[#F3F4EA]`}>Or you can</span>
</div>
</div>
<button
onClick={() => {
props.eventBus.emit({
type: "signin",
});
}}
class={tw`${ButtonClass} ${CenterClass} w-full bg-[#F8C455] text-[#313338] hover:bg-[#FFDF6F] py-0`}
>
<img
class={tw`h-10`}
src="alby-logo.svg"
alt="Alby Logo"
/>
Sign in with Alby
</button>
<button
onClick={() => {
props.eventBus.emit({
type: "createNewAccount",
});
}}
class={tw`${ButtonClass} w-full bg-[#2B2D31] hover:bg-[#404249] mt-4`}
>
Create an account
</button>
{props.warningString
? <p class={tw`text-[#F3F4EA] mt-2`}>{props.warningString}</p>
: undefined}
</div>
</div>
);
}