mirror of
https://github.com/BlowaterNostr/blowater.git
synced 2024-10-18 15:43:20 +00:00
refactor signin page (#276)
This commit is contained in:
parent
24324af7bc
commit
cdfe0eacb2
@ -43,7 +43,6 @@ export async function Start(database: DexieDatabase) {
|
||||
const ctx = await getCurrentSignInCtx();
|
||||
if (ctx instanceof Error) {
|
||||
console.error(ctx);
|
||||
model.signIn.warningString = "Please add your private key to your NIP-7 extension";
|
||||
} else if (ctx) {
|
||||
const dbView = await Database_Contextual_View.New(database);
|
||||
if (dbView instanceof Error) {
|
||||
@ -386,13 +385,7 @@ export function AppComponent(props: {
|
||||
|
||||
if (model.app == undefined) {
|
||||
console.log("render sign in page");
|
||||
return (
|
||||
<SignIn
|
||||
eventBus={props.eventBus}
|
||||
privateKey={model.signIn.privateKey}
|
||||
warningString={model.signIn.warningString}
|
||||
/>
|
||||
);
|
||||
return <SignIn emit={props.eventBus.emit} />;
|
||||
}
|
||||
|
||||
const app = model.app;
|
||||
|
@ -3,7 +3,6 @@ import { SearchInitModel, SearchModel } from "./search_model.ts";
|
||||
import { ProfileData } from "../features/profile.ts";
|
||||
import { RightPanelModel } from "./message-panel.tsx";
|
||||
import { App } from "./app.tsx";
|
||||
import { SignInModel } from "./signIn.tsx";
|
||||
import { EditorModel } from "./editor.tsx";
|
||||
import { DM_Model } from "./dm.tsx";
|
||||
|
||||
@ -25,9 +24,6 @@ export type Model = {
|
||||
// UI
|
||||
navigationModel: NavigationModel;
|
||||
rightPanelModel: RightPanelModel;
|
||||
|
||||
// sign in
|
||||
signIn: SignInModel;
|
||||
};
|
||||
|
||||
export function initialModel(): Model {
|
||||
@ -53,8 +49,5 @@ export function initialModel(): Model {
|
||||
show: false,
|
||||
},
|
||||
myProfile: undefined,
|
||||
signIn: {
|
||||
privateKey: "",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ import { NavigationUpdate } from "./nav.tsx";
|
||||
import { Model } from "./app_model.ts";
|
||||
import { SearchUpdate, SelectConversation } from "./search_model.ts";
|
||||
import { LamportTime } from "../time.ts";
|
||||
import { SignInEvent, signInWithExtension, signInWithPrivateKey } from "./signIn.tsx";
|
||||
import { SignInEvent } from "./signIn.tsx";
|
||||
import {
|
||||
Encrypted_Event,
|
||||
getTags,
|
||||
@ -84,26 +84,8 @@ export async function* UI_Interaction_Update(args: {
|
||||
for await (const event of events) {
|
||||
console.log(event);
|
||||
switch (event.type) {
|
||||
case "editSignInPrivateKey":
|
||||
model.signIn.privateKey = event.privateKey;
|
||||
yield model;
|
||||
continue;
|
||||
break;
|
||||
case "signin":
|
||||
let ctx;
|
||||
if (event.privateKey) {
|
||||
ctx = signInWithPrivateKey(event.privateKey);
|
||||
} else {
|
||||
const ctx2 = await signInWithExtension();
|
||||
console.log(ctx2);
|
||||
if (typeof ctx2 == "string") {
|
||||
model.signIn.warningString = ctx2;
|
||||
} else if (ctx2 instanceof Error) {
|
||||
model.signIn.warningString = ctx2.message;
|
||||
} else {
|
||||
ctx = ctx2;
|
||||
}
|
||||
}
|
||||
case "SignInEvent":
|
||||
const ctx = event.ctx;
|
||||
if (ctx) {
|
||||
console.log("sign in as", ctx.publicKey.bech32());
|
||||
const dbView = await Database_Contextual_View.New(dexieDB);
|
||||
|
@ -3,7 +3,7 @@ import { DividerBackgroundColor, PlaceholderColor, PrimaryTextColor } from "../s
|
||||
export const CenterClass = "flex items-center justify-center";
|
||||
export const NoOutlineClass = "focus:outline-none focus-visible:outline-none";
|
||||
export const inputBorderClass = `border-[2px] border-[${DividerBackgroundColor}]`;
|
||||
export const DividerClass = `h-[0.0625rem] bg-[${DividerBackgroundColor}] my-[1.5rem]`;
|
||||
export const DividerClass = `h-[0.0625rem] bg-[${DividerBackgroundColor}] my-[1.5rem] w-full`;
|
||||
export const LinearGradientsClass = "bg-gradient-to-r from-[#FF762C] via-[#FF3A5E] to-[#FF01A9]";
|
||||
export const InputClass =
|
||||
`w-full px-4 py-3 rounded-lg resize-y bg-transparent ${NoOutlineClass} ${inputBorderClass} placeholder-[${PlaceholderColor}] text-[${PrimaryTextColor}]`;
|
||||
|
@ -43,7 +43,7 @@
|
||||
|
||||
@media screen and (max-width: 1024px) {
|
||||
html {
|
||||
font-size: calc(1.618em * (1920 / 2560));
|
||||
font-size: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@
|
||||
|
||||
@media screen and (max-width: 1024px) {
|
||||
html {
|
||||
font-size: calc(1.618em * (1920 / 2560));
|
||||
font-size: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,8 +7,7 @@ import { SignIn } from "./signIn.tsx";
|
||||
|
||||
render(
|
||||
<SignIn
|
||||
eventBus={testEventBus}
|
||||
privateKey={PrivateKey.Generate().hex}
|
||||
emit={testEventBus.emit}
|
||||
/>,
|
||||
document.body,
|
||||
);
|
||||
|
308
UI/signIn.tsx
308
UI/signIn.tsx
@ -2,18 +2,24 @@
|
||||
import { Component, h } from "https://esm.sh/preact@10.17.1";
|
||||
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 { ButtonClass, CenterClass, LinearGradientsClass, NoOutlineClass } from "./components/tw.ts";
|
||||
import KeyView from "./key-view.tsx";
|
||||
import { PrivateKey } from "../lib/nostr-ts/key.ts";
|
||||
import { InMemoryAccountContext } from "../lib/nostr-ts/nostr.ts";
|
||||
import { emitFunc, EventEmitter } from "../event-bus.ts";
|
||||
import { InMemoryAccountContext, NostrAccountContext } from "../lib/nostr-ts/nostr.ts";
|
||||
import { emitFunc } from "../event-bus.ts";
|
||||
import {
|
||||
ErrorColor,
|
||||
HintLinkColor,
|
||||
HintTextColor,
|
||||
HoverButtonBackgroudColor,
|
||||
PrimaryBackgroundColor,
|
||||
PrimaryTextColor,
|
||||
SecondaryBackgroundColor,
|
||||
} from "./style/colors.ts";
|
||||
|
||||
export type SignInEvent = {
|
||||
type: "signin";
|
||||
privateKey?: PrivateKey; // undefined means sign in with extentions
|
||||
} | {
|
||||
type: "editSignInPrivateKey";
|
||||
privateKey: string;
|
||||
type: "SignInEvent";
|
||||
ctx: NostrAccountContext;
|
||||
};
|
||||
|
||||
const AlbyURL = "https://getalby.com/";
|
||||
@ -37,14 +43,14 @@ export function setSignInState(state: SignInState) {
|
||||
////////////////////////
|
||||
export async function getCurrentSignInCtx() {
|
||||
if (getSignInState() === "nip07") {
|
||||
const albyCtx = await Nip7ExtensionContext.New();
|
||||
if (albyCtx instanceof Error) {
|
||||
return albyCtx;
|
||||
const nip07Ctx = await Nip7ExtensionContext.New();
|
||||
if (nip07Ctx instanceof Error) {
|
||||
return nip07Ctx;
|
||||
}
|
||||
if (albyCtx === undefined) {
|
||||
if (nip07Ctx === undefined) {
|
||||
setSignInState("none");
|
||||
}
|
||||
return albyCtx;
|
||||
return nip07Ctx;
|
||||
}
|
||||
if (getSignInState() === "local") {
|
||||
const ctx = GetLocalStorageAccountContext();
|
||||
@ -60,174 +66,184 @@ export async function getCurrentSignInCtx() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export async function signInWithExtension() {
|
||||
const albyCtx = await Nip7ExtensionContext.New();
|
||||
if (albyCtx instanceof Error) {
|
||||
return albyCtx;
|
||||
}
|
||||
if (albyCtx === undefined) {
|
||||
open(AlbyURL);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
type Props = {
|
||||
eventBus: EventEmitter<SignInEvent>;
|
||||
} & SignInModel;
|
||||
|
||||
export type SignInModel = {
|
||||
privateKey: string;
|
||||
warningString?: string;
|
||||
emit: emitFunc<SignInEvent>;
|
||||
};
|
||||
|
||||
type State = { state: "newAccount" | "enterPrivateKey" };
|
||||
type State = {
|
||||
state: "newAccount" | "enterPrivateKey";
|
||||
privateKey: PrivateKey;
|
||||
privateKeyError: string;
|
||||
nip07Error: string;
|
||||
};
|
||||
export class SignIn extends Component<Props, State> {
|
||||
render() {
|
||||
const props = this.props;
|
||||
styles = {
|
||||
container:
|
||||
tw`h-screen w-screen bg-[${PrimaryBackgroundColor}] flex items-center justify-center p-4 overflow-y-auto`,
|
||||
form: tw`w-[30rem] flex flex-col h-full py-8`,
|
||||
logo: tw`w-32 h-32 mx-auto`,
|
||||
title: tw`text-[${PrimaryTextColor}] text-center text-4xl`,
|
||||
subTitle: tw`text-[${HintTextColor}] text-center`,
|
||||
input: tw`w-full px-4 py-2 focus-visible:outline-none rounded-lg mt-8`,
|
||||
hint: tw`text-[${HintTextColor}] text-sm mt-2`,
|
||||
block: tw`flex-1 desktop:hidden`,
|
||||
signInButton:
|
||||
tw`w-full mt-4 ${ButtonClass} ${LinearGradientsClass} hover:bg-gradient-to-l mobile:rounded-full font-bold`,
|
||||
ablyButton:
|
||||
tw`${ButtonClass} ${CenterClass} mt-4 bg-[#F8C455] text-[#313338] hover:bg-[#FFDF6F] py-0 mobile:rounded-full`,
|
||||
cancelButton:
|
||||
tw`${ButtonClass} ${CenterClass} mt-4 bg-[${SecondaryBackgroundColor}] text-[${PrimaryTextColor}] hover:bg-[${HoverButtonBackgroudColor}] mobile:rounded-full`,
|
||||
newButton:
|
||||
tw`text-[${HintLinkColor}] hover:underline mobile:text-[${PrimaryTextColor}] mobile:bg-[${HintLinkColor}] mobile:rounded mobile:px-2 mobile:py-1 ${NoOutlineClass}`,
|
||||
ablyIcon: tw`h-10`,
|
||||
isError: (error: string) => error ? tw`text-[${ErrorColor}]` : "",
|
||||
};
|
||||
|
||||
signInWithPrivateKey = () => {
|
||||
if (!this.state.privateKey) {
|
||||
this.setState({
|
||||
privateKeyError: "Invilid Private Key",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const ctx = InMemoryAccountContext.New(this.state.privateKey);
|
||||
localStorage.setItem("MPK", this.state.privateKey.hex);
|
||||
setSignInState("local");
|
||||
|
||||
this.props.emit({
|
||||
type: "SignInEvent",
|
||||
ctx: ctx,
|
||||
});
|
||||
};
|
||||
|
||||
signInWithExtension = async () => {
|
||||
const nip07Ctx = await Nip7ExtensionContext.New();
|
||||
if (nip07Ctx === undefined) {
|
||||
open(AlbyURL);
|
||||
return;
|
||||
}
|
||||
if (typeof nip07Ctx == "string") {
|
||||
this.setState({
|
||||
nip07Error: nip07Ctx,
|
||||
});
|
||||
} else if (nip07Ctx instanceof Error) {
|
||||
this.setState({
|
||||
nip07Error: nip07Ctx.message,
|
||||
});
|
||||
} else {
|
||||
this.props.emit({
|
||||
type: "SignInEvent",
|
||||
ctx: nip07Ctx,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
newAccount = () => {
|
||||
this.setState({
|
||||
privateKey: PrivateKey.Generate(),
|
||||
privateKeyError: "",
|
||||
state: "newAccount",
|
||||
});
|
||||
};
|
||||
|
||||
cancelNew = () => {
|
||||
this.setState({
|
||||
state: "enterPrivateKey",
|
||||
privateKey: undefined,
|
||||
});
|
||||
};
|
||||
|
||||
inputPrivateKey = (privateKeyStr: string) => {
|
||||
let privateKey = PrivateKey.FromHex(privateKeyStr);
|
||||
if (privateKey instanceof Error) {
|
||||
privateKey = PrivateKey.FromBech32(privateKeyStr);
|
||||
|
||||
if (privateKey instanceof Error) {
|
||||
this.setState({
|
||||
privateKeyError: "Invilid Private Key",
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.setState({
|
||||
privateKeyError: "",
|
||||
privateKey: privateKey,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.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]`}>
|
||||
<div class={this.styles.container}>
|
||||
<div class={this.styles.form}>
|
||||
<KeyView
|
||||
privateKey={privateKey}
|
||||
publicKey={privateKey.toPublicKey()}
|
||||
privateKey={this.state.privateKey}
|
||||
publicKey={this.state.privateKey.toPublicKey()}
|
||||
/>
|
||||
<p class={this.styles.hint}>
|
||||
Please back up your <strong>Private Key</strong>
|
||||
</p>
|
||||
<div class={this.styles.block}></div>
|
||||
<button
|
||||
onClick={on_BackToSignInPage_click(this.setState.bind(this))}
|
||||
class={tw`w-full mt-8 bg-[#404249] hover:bg-[#2B2D31] ${ButtonClass}`}
|
||||
onClick={this.cancelNew}
|
||||
class={this.styles.cancelButton}
|
||||
>
|
||||
Back
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
props.eventBus.emit({
|
||||
type: "signin",
|
||||
privateKey: privateKey,
|
||||
});
|
||||
}}
|
||||
class={tw`w-full mt-8 bg-[#ED4545] hover:bg-[#E03030] ${ButtonClass}`}
|
||||
onClick={this.signInWithPrivateKey}
|
||||
class={this.styles.signInButton}
|
||||
>
|
||||
Sign In
|
||||
Confirm
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let privateKey = PrivateKey.FromHex(props.privateKey);
|
||||
if (privateKey instanceof Error) {
|
||||
privateKey = PrivateKey.FromBech32(props.privateKey);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
class={tw`h-screen w-screen bg-[#313338] flex items-center justify-center p-4 overflow-y-auto`}
|
||||
>
|
||||
<div class={tw`w-[40rem]`}>
|
||||
<img
|
||||
class={tw`w-32 h-32 mx-auto`}
|
||||
src="logo.png"
|
||||
alt="Logo"
|
||||
/>
|
||||
<h1
|
||||
class={tw`text-[#F3F4EA] text-center text-[2rem]`}
|
||||
>
|
||||
Welcome to Blowater
|
||||
</h1>
|
||||
<div class={this.styles.container}>
|
||||
<div class={this.styles.form}>
|
||||
<img class={this.styles.logo} src="logo.png" alt="Logo" />
|
||||
<h1 class={this.styles.title}>Blowater</h1>
|
||||
<p class={this.styles.subTitle}>A delightful Nostr client that focuses on DM</p>
|
||||
<input
|
||||
onInput={on_EditSignInPrivateKey_clicked(props.eventBus.emit)}
|
||||
onInput={(e) => this.inputPrivateKey(e.currentTarget.value)}
|
||||
placeholder="Input your private key here"
|
||||
type="password"
|
||||
class={tw`w-full px-4 py-2 focus-visible:outline-none rounded-lg mt-8`}
|
||||
class={this.styles.input}
|
||||
autofocus
|
||||
/>
|
||||
{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}
|
||||
<p class={this.styles.hint}>
|
||||
<span class={this.styles.isError(this.state.privateKeyError)}>
|
||||
Private Key has to be <strong>64</strong> letters hex-decimal or{" "}
|
||||
<strong>63</strong> letters nsec string.
|
||||
</span>{" "}
|
||||
Don't have an account yet?{" "}
|
||||
<button onClick={this.newAccount} class={this.styles.newButton}>create one!</button>
|
||||
</p>
|
||||
<div class={this.styles.block}></div>
|
||||
<button
|
||||
onClick={on_SignIn_clicked(privateKey, props.eventBus.emit)}
|
||||
disabled={privateKey instanceof Error}
|
||||
class={tw`w-full bg-[#2B2D31] hover:bg-[#404249] mt-4 disabled:bg-[#404249] ${ButtonClass}`}
|
||||
onClick={this.signInWithPrivateKey}
|
||||
class={this.styles.signInButton}
|
||||
>
|
||||
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`}
|
||||
onClick={async () => await this.signInWithExtension()}
|
||||
class={this.styles.ablyButton}
|
||||
>
|
||||
<img
|
||||
class={tw`h-10`}
|
||||
src="alby-logo.svg"
|
||||
alt="Alby Logo"
|
||||
/>
|
||||
<img class={this.styles.ablyIcon} src="alby-logo.svg" alt="Alby Logo" />
|
||||
Sign in with Alby
|
||||
</button>
|
||||
<button
|
||||
onClick={on_CreateAccount_clicked(this.setState.bind(this))}
|
||||
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}
|
||||
<p class={this.styles.hint}>
|
||||
<span class={this.styles.isError(this.state.nip07Error)}>
|
||||
{this.state.nip07Error}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const on_CreateAccount_clicked = (setState: (state: State) => void) => () => {
|
||||
setState({ state: "newAccount" });
|
||||
};
|
||||
|
||||
const on_SignIn_clicked = (privateKey: PrivateKey | Error, emit: emitFunc<SignInEvent>) => () => {
|
||||
if (privateKey instanceof PrivateKey) {
|
||||
emit({
|
||||
type: "signin",
|
||||
privateKey: privateKey,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const on_EditSignInPrivateKey_clicked =
|
||||
(emit: emitFunc<SignInEvent>) => (e: h.JSX.TargetedEvent<HTMLInputElement, Event>) => {
|
||||
emit({
|
||||
type: "editSignInPrivateKey",
|
||||
privateKey: e.currentTarget.value,
|
||||
});
|
||||
};
|
||||
|
||||
const on_BackToSignInPage_click = (setState: (state: State) => void) => () => {
|
||||
setState({ state: "enterPrivateKey" });
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user