Skip to content

Commit

Permalink
Merge pull request #35 from jiftechnify/improve-nip07-detection
Browse files Browse the repository at this point in the history
Simplify Nostr extension(NIP-07) detection
  • Loading branch information
jiftechnify authored Oct 4, 2023
2 parents 28c079a + 1eac64a commit 36d90b2
Show file tree
Hide file tree
Showing 8 changed files with 60 additions and 51 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"i18next-browser-languagedetector": "^7.1.0",
"jotai": "^2.4.2",
"lucide-react": "^0.277.0",
"nip07-awaiter": "^0.2.1",
"nostr-fetch": "^0.13.0",
"nostr-tools": "^1.15.0",
"react": "^18.2.0",
Expand Down
13 changes: 7 additions & 6 deletions src/components/LoginForm.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
import { css } from "@shadow-panda/styled-system/css";
import { vstack } from "@shadow-panda/styled-system/patterns";
import { useAtomValue } from "jotai";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { parsePrivkey, parsePubkey } from "../nostr";
import { useLoginWithPrivkey, useLoginWithPubkey, useNip07Availability } from "../states/nostr";
import { isNostrExtAvailableAtom, useLoginWithPrivkey, useLoginWithPubkey } from "../states/nostr";
import { button } from "../styles/recipes";
import { Input } from "./ui/input";

export const LoginForm: React.FC = () => {
const loginWithPubkey = useLoginWithPubkey();
const loginWithPrivkey = useLoginWithPrivkey();

const isNip07Available = useNip07Availability();
const isNostrExtAvailable = useAtomValue(isNostrExtAvailableAtom);
const [pubkeyInput, setPubkeyInput] = useState("");
const [nsecInput, setNsecInput] = useState("");

const { t } = useTranslation();

const onClickNip07Login = async () => {
const onClickNostrExtLogin = async () => {
const pubkey = await window.nostr.getPublicKey();
if (pubkey) {
loginWithPubkey(pubkey);
Expand All @@ -38,7 +39,7 @@ export const LoginForm: React.FC = () => {
const onClickNsecLogin = () => {
const hexPrivkey = parsePrivkey(nsecInput);
if (hexPrivkey === undefined) {
console.error("invalkid nsec");
console.error("invalid nsec");
window.alert("invalid nsec");
return;
}
Expand All @@ -48,8 +49,8 @@ export const LoginForm: React.FC = () => {

return (
<div className={vstack({ w: "300px", mt: "4", mx: "auto", gap: "10" })}>
<button className={button({ expand: true })} onClick={onClickNip07Login} disabled={!isNip07Available}>
{t("Login with NIP-07 Extension")}
<button className={button({ expand: true })} onClick={onClickNostrExtLogin} disabled={!isNostrExtAvailable}>
{t("Login with Nostr Extension")}
</button>

<div className={vstack({ w: "100%", gap: "1.5" })}>
Expand Down
7 changes: 2 additions & 5 deletions src/global.d.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import { RelayList } from "./nostr";
import { NostrExtension } from "./nostr";

declare global {
interface Window {
nostr: {
getPublicKey: () => Promise<string>;
getRelays: () => Promise<RelayList>;
};
nostr: NostrExtension;

nostrZap: {
initTarget: (targetEl: HTMLElement) => void;
Expand Down
2 changes: 1 addition & 1 deletion src/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"View Code on GitHub": "View Code on GitHub",
"Hard Reload": "Hard Reload",
"Logout": "Logout",
"Login with NIP-07 Extension": "Login with NIP-07 Extension",
"Login with Nostr Extension": "Login with Nostr Extension",
"npub or hex pubkey": "npub or hex pubkey",
"Login with Pubkey": "Login with Pubkey",
"Login with nsec": "Login with nsec",
Expand Down
2 changes: 1 addition & 1 deletion src/locales/ja/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"View Code on GitHub": "GitHub でコードを見る",
"Hard Reload": "ハードリロード",
"Logout": "ログアウト",
"Login with NIP-07 Extension": "NIP-07 拡張機能でログイン",
"Login with Nostr Extension": "Nostr 拡張機能でログイン",
"npub or hex pubkey": "npub または hex 形式の公開鍵",
"Login with Pubkey": "公開鍵でログイン",
"Login with nsec": "秘密鍵(nsec)でログイン",
Expand Down
6 changes: 6 additions & 0 deletions src/nostr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,9 @@ export const parsePrivkey = (nsec: string): string | undefined => {
return undefined;
}
};

/* NIP-07 extensions */
export type NostrExtension = {
getPublicKey: () => Promise<string>;
getRelays?: () => Promise<RelayList>;
};
68 changes: 30 additions & 38 deletions src/states/nostr.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
NostrExtension,
RelayList,
getFirstTagValueByName,
getTagValuesByName,
Expand All @@ -16,11 +17,12 @@ import {
userStatusCategories,
} from "./nostrModels";

import { atom, getDefaultStore, useAtom, useAtomValue, useSetAtom } from "jotai";
import { atom, getDefaultStore, useAtomValue, useSetAtom } from "jotai";
import { RESET, atomFamily, atomWithReset, atomWithStorage, loadable, selectAtom } from "jotai/utils";
import { useCallback, useEffect, useRef, useState } from "react";
import { useCallback, useEffect, useState } from "react";

import { rxNostrAdapter } from "@nostr-fetch/adapter-rx-nostr";
import "nip07-awaiter";
import { NostrEvent, NostrFetcher } from "nostr-fetch";
import { getPublicKey } from "nostr-tools";
import { createRxForwardReq, createRxNostr, getSignedEvent, uniq, verify } from "rx-nostr";
Expand Down Expand Up @@ -190,40 +192,34 @@ export const pubkeysOrderByLastStatusUpdateTimeAtom = atom((get) => {
.map((s) => s.pubkey);
});

const isNip07AvailableAtom = atom(false);

const MAX_NIP07_CHECKS = 5;
export const useNip07Availability = () => {
const [available, setAvailable] = useAtom(isNip07AvailableAtom);
const checkCnt = useRef(0);
const nostrExtensionAtom = atom<NostrExtension | undefined>(window.nostr);
nostrExtensionAtom.onMount = (set) => {
const updateNostrExt = (ev: CustomEvent<{ nostr: NostrExtension }>) => {
set(ev.detail.nostr);
};

useEffect(() => {
const nip07CheckInterval = setInterval(() => {
if (window.nostr) {
clearInterval(nip07CheckInterval);
setAvailable(true);
} else if (checkCnt.current > MAX_NIP07_CHECKS) {
clearInterval(nip07CheckInterval);
setAvailable(false);
} else {
checkCnt.current++;
}
}, 300);
return () => clearInterval(nip07CheckInterval);
}, [setAvailable]);
window.addEventListener("nostrloaded", updateNostrExt);
window.addEventListener("nostrupdated", updateNostrExt);

return available;
return () => {
window.removeEventListener("nostrloaded", updateNostrExt);
window.removeEventListener("nostrupdated", updateNostrExt);
};
};

export const usePubkeyInNip07 = () => {
const nip07Available = useNip07Availability();
export const isNostrExtAvailableAtom = atom((get) => {
return get(nostrExtensionAtom) !== undefined;
});

export const usePubkeyInNostrExt = () => {
const nostrExt = useAtomValue(nostrExtensionAtom);
const [pubkey, setPubkey] = useState<string | undefined>(undefined);

useEffect(() => {
const pollPubkey = async () => {
try {
if (window.nostr) {
const pubkey = await window.nostr.getPublicKey();
if (nostrExt) {
const pubkey = await nostrExt.getPublicKey();
setPubkey(pubkey);
} else {
setPubkey(undefined);
Expand All @@ -232,12 +228,8 @@ export const usePubkeyInNip07 = () => {
console.error(e);
}
};
if (nip07Available) {
pollPubkey().catch((e) => console.error(e));
} else {
setPubkey(undefined);
}
}, [nip07Available]);
pollPubkey().catch((e) => console.error(e));
}, [nostrExt]);

return pubkey;
};
Expand All @@ -250,9 +242,9 @@ export const useWriteOpsEnabled = () => {
const inputPrivkey = useAtomValue(inputPrivkeyAtom);

const inputPubkey = useAtomValue(inputPubkeyAtom);
const pubkeyInNip07 = usePubkeyInNip07();
const pubkeyInNostrExt = usePubkeyInNostrExt();

return inputPrivkey !== undefined || (inputPubkey !== undefined && inputPubkey === pubkeyInNip07);
return inputPrivkey !== undefined || (inputPubkey !== undefined && inputPubkey === pubkeyInNostrExt);
};

const bootstrapFetcher = NostrFetcher.init();
Expand All @@ -275,9 +267,9 @@ const getBootstrapRelays = async (): Promise<[string[], boolean]> => {
if (window.nostr === undefined || typeof window.nostr.getRelays !== "function") {
return [defaultBootstrapRelays, true];
}
const nip07Relays = await window.nostr.getRelays();
const nip07ReadRelays = nip07Relays !== undefined ? selectRelaysByUsage(nip07Relays, "read") : [];
return nip07ReadRelays.length > 0 ? [nip07ReadRelays, false] : [defaultBootstrapRelays, true];
const nostrExtRelays = await window.nostr.getRelays();
const nostrExtReadRelays = nostrExtRelays !== undefined ? selectRelaysByUsage(nostrExtRelays, "read") : [];
return nostrExtReadRelays.length > 0 ? [nostrExtReadRelays, false] : [defaultBootstrapRelays, true];
};

const extractRelayListOrDefault = (evs: (NostrEvent | undefined)[]): RelayList => {
Expand Down
12 changes: 12 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6560,6 +6560,13 @@ network-information-types@^0.1.1:
resolved "https://registry.yarnpkg.com/network-information-types/-/network-information-types-0.1.1.tgz#07e98d8050dd1e8ac787f0cc5ebaae4935393861"
integrity sha512-mLXNafJYOkiJB6IlF727YWssTRpXitR+tKSLyA5VAdBi3SOvLf5gtizHgxf241YHPWocnAO/fAhVrB/68tPHDw==

nip07-awaiter@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/nip07-awaiter/-/nip07-awaiter-0.2.1.tgz#de86f71fd2313c234704f05b00f05fe966c46659"
integrity sha512-JPaCwKhNLcc6YgI9MeEHSYfv9+MvEDmK1Sd3mJW5qb3KYxKLSBTZgWAqpw5306UMel1joBf6u+gbJn4diB0qSA==
dependencies:
nostr-typedef "^0.4.0"

nlcst-to-string@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/nlcst-to-string/-/nlcst-to-string-3.1.1.tgz#83b90f2e1ee2081e14701317efc26d3bbadc806e"
Expand Down Expand Up @@ -6630,6 +6637,11 @@ nostr-tools@^1.15.0:
"@scure/bip32" "1.3.1"
"@scure/bip39" "1.2.1"

nostr-typedef@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/nostr-typedef/-/nostr-typedef-0.4.0.tgz#0b5cc334a223cc8138b7bb83cd7f7a176622f516"
integrity sha512-0hixsTzPHQ0EWGR592mEQFunAk3+0DkgLTXFi5emViMbSpu2f8g5mjjgEgDwjiky047dTfiVw7qMKibZrI5DfQ==

now-and-later@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/now-and-later/-/now-and-later-2.0.1.tgz#8e579c8685764a7cc02cb680380e94f43ccb1f7c"
Expand Down

0 comments on commit 36d90b2

Please sign in to comment.