blowater/UI/account-context.ts

131 lines
4.1 KiB
TypeScript
Raw Normal View History

import { chan, closed, sleep } from "https://raw.githubusercontent.com/BlowaterNostr/csp/master/csp.ts";
2023-08-28 17:58:05 +00:00
import { PrivateKey, PublicKey } from "../lib/nostr-ts/key.ts";
2023-06-30 14:05:57 +00:00
import {
InMemoryAccountContext,
NostrAccountContext,
NostrEvent,
NostrKind,
2023-06-30 14:05:57 +00:00
UnsignedNostrEvent,
2023-08-28 17:58:05 +00:00
} from "../lib/nostr-ts/nostr.ts";
2023-06-30 14:05:57 +00:00
2023-07-09 07:06:13 +00:00
type NIP07 = {
2023-06-30 14:05:57 +00:00
getPublicKey(): Promise<string>;
signEvent(event: UnsignedNostrEvent): Promise<NostrEvent>;
2023-07-09 07:06:13 +00:00
nip04: {
encrypt: (pubkey: string, plaintext: string) => Promise<string | Error>;
decrypt: (pubkey: string, ciphertext: string) => Promise<string | Error>;
};
2023-06-30 14:05:57 +00:00
enabled: boolean;
enable: () => Promise<{ enabled: boolean }>;
};
2023-07-09 07:06:13 +00:00
export class Nip7ExtensionContext implements NostrAccountContext {
2023-06-30 14:05:57 +00:00
private readonly operationChan = chan<
2023-10-21 05:04:04 +00:00
{
2023-06-30 14:05:57 +00:00
op: "encrypt";
pubkey: string;
plaintext: string;
} | {
op: "signEvent";
event: UnsignedNostrEvent;
}
>();
2023-10-21 05:04:04 +00:00
2023-06-30 14:05:57 +00:00
private readonly encryptChan = chan<string | Error>();
2023-10-21 05:04:04 +00:00
// private readonly decryptChan = chan<string | Error>();
2023-06-30 14:05:57 +00:00
private readonly signEventChan = chan<NostrEvent>();
2023-07-09 07:06:13 +00:00
static async New(): Promise<Nip7ExtensionContext | Error | undefined> {
async function getExtensionObject(): Promise<NIP07 | undefined> {
// wait for alby or nos2x init
await sleep(20);
2023-06-30 14:05:57 +00:00
if ("nostr" in window) {
2023-07-09 07:06:13 +00:00
return window.nostr as NIP07;
2023-06-30 14:05:57 +00:00
}
return undefined;
}
const ext = await getExtensionObject();
2023-06-30 14:05:57 +00:00
if (ext === undefined) {
return undefined;
}
let pubkey: string | undefined;
try {
pubkey = await ext.getPublicKey();
} catch (e) {
return e;
}
const pub = PublicKey.FromHex(pubkey);
if (pub instanceof Error) {
return pub;
}
2023-07-09 07:06:13 +00:00
return new Nip7ExtensionContext(ext, pub);
2023-06-30 14:05:57 +00:00
}
private constructor(
2023-07-09 07:06:13 +00:00
private alby: NIP07,
2023-06-30 14:05:57 +00:00
public publicKey: PublicKey,
) {
2023-10-21 05:04:04 +00:00
console.log(alby);
2023-06-30 14:05:57 +00:00
(async () => {
for await (const op of this.operationChan) {
2023-10-21 05:04:04 +00:00
if (op.op == "encrypt") {
2023-06-30 14:05:57 +00:00
try {
const res = await alby.nip04.encrypt(op.pubkey, op.plaintext);
await this.encryptChan.put(res);
} catch (e) {
await this.encryptChan.put(e as Error);
}
} else if (op.op === "signEvent") {
const res = await alby.signEvent(op.event);
await this.signEventChan.put(res);
}
}
})();
}
async signEvent<T extends NostrKind = NostrKind>(event: UnsignedNostrEvent<T>): Promise<NostrEvent<T>> {
2023-06-30 14:05:57 +00:00
await this.operationChan.put({ op: "signEvent", event: event });
const res = await this.signEventChan.pop();
if (res === closed) {
throw new Error("unreachable");
}
// @ts-ignore
2023-06-30 14:05:57 +00:00
return res;
}
2023-07-09 07:06:13 +00:00
encrypt = async (pubkey: string, plaintext: string) => {
await this.operationChan.put({
op: "encrypt",
pubkey,
plaintext,
});
const res = await this.encryptChan.pop();
if (res === closed) {
throw new Error("unreachable");
}
return res;
};
decrypt = async (pubkey: string, ciphertext: string) => {
2023-10-21 05:04:04 +00:00
try {
const res = await this.alby.nip04.decrypt(pubkey, ciphertext);
return res;
} catch (e) {
return e as Error;
2023-06-30 14:05:57 +00:00
}
};
}
export function GetLocalStorageAccountContext() {
const loginPrivateKey = localStorage.getItem("MPK");
if (loginPrivateKey) {
const priKey = PrivateKey.FromHex(loginPrivateKey);
if (!(priKey instanceof Error)) {
return InMemoryAccountContext.New(priKey);
}
console.error("the stored MPK is not a valid private, removing it");
localStorage.removeItem("MPK");
return undefined;
}
return undefined;
}