mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-09-29 04:52:59 +02:00
use signing service
This commit is contained in:
@@ -16,6 +16,7 @@ import { nostrPostAction, PostResult } from "../../classes/nostr-post-action";
|
|||||||
import { getReferences } from "../../helpers/nostr-event";
|
import { getReferences } from "../../helpers/nostr-event";
|
||||||
import { useWriteRelayUrls } from "../../hooks/use-client-relays";
|
import { useWriteRelayUrls } from "../../hooks/use-client-relays";
|
||||||
import { useIsMobile } from "../../hooks/use-is-mobile";
|
import { useIsMobile } from "../../hooks/use-is-mobile";
|
||||||
|
import { useSigningContext } from "../../providers/signing-provider";
|
||||||
import { DraftNostrEvent, NostrEvent } from "../../types/nostr-event";
|
import { DraftNostrEvent, NostrEvent } from "../../types/nostr-event";
|
||||||
import { NoteLink } from "../note-link";
|
import { NoteLink } from "../note-link";
|
||||||
import { PostResults } from "./post-results";
|
import { PostResults } from "./post-results";
|
||||||
@@ -39,6 +40,7 @@ export const PostModal = ({ isOpen, onClose, initialDraft }: PostModalProps) =>
|
|||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
const pad = isMobile ? "2" : "4";
|
const pad = isMobile ? "2" : "4";
|
||||||
|
|
||||||
|
const { requestSignature } = useSigningContext();
|
||||||
const writeRelays = useWriteRelayUrls();
|
const writeRelays = useWriteRelayUrls();
|
||||||
const [waiting, setWaiting] = useState(false);
|
const [waiting, setWaiting] = useState(false);
|
||||||
const [signedEvent, setSignedEvent] = useState<NostrEvent | null>(null);
|
const [signedEvent, setSignedEvent] = useState<NostrEvent | null>(null);
|
||||||
@@ -50,19 +52,17 @@ export const PostModal = ({ isOpen, onClose, initialDraft }: PostModalProps) =>
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
if (window.nostr) {
|
setWaiting(true);
|
||||||
setWaiting(true);
|
const updatedDraft: DraftNostrEvent = { ...draft, created_at: moment().unix() };
|
||||||
const updatedDraft: DraftNostrEvent = { ...draft, created_at: moment().unix() };
|
const event = await requestSignature(updatedDraft);
|
||||||
const event = await window.nostr.signEvent(updatedDraft);
|
setWaiting(false);
|
||||||
setWaiting(false);
|
if (!event) return;
|
||||||
setSignedEvent(event);
|
setSignedEvent(event);
|
||||||
|
|
||||||
const { results } = nostrPostAction(writeRelays, event);
|
const { results } = nostrPostAction(writeRelays, event);
|
||||||
|
results.subscribe((result) => {
|
||||||
results.subscribe((result) => {
|
resultsActions.push(result);
|
||||||
resultsActions.push(result);
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const refs = getReferences(draft);
|
const refs = getReferences(draft);
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { ChakraProvider, localStorageManager } from "@chakra-ui/react";
|
import { ChakraProvider, localStorageManager } from "@chakra-ui/react";
|
||||||
import theme from "../theme";
|
import theme from "../theme";
|
||||||
|
import { SigningProvider } from "./signing-provider";
|
||||||
|
|
||||||
export const Providers = ({ children }: { children: React.ReactNode }) => (
|
export const Providers = ({ children }: { children: React.ReactNode }) => (
|
||||||
<ChakraProvider theme={theme} colorModeManager={localStorageManager}>
|
<ChakraProvider theme={theme} colorModeManager={localStorageManager}>
|
||||||
{children}
|
<SigningProvider>{children}</SigningProvider>
|
||||||
</ChakraProvider>
|
</ChakraProvider>
|
||||||
);
|
);
|
||||||
|
41
src/providers/signing-provider.tsx
Normal file
41
src/providers/signing-provider.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { useToast } from "@chakra-ui/react";
|
||||||
|
import React, { useCallback, useContext, useMemo } from "react";
|
||||||
|
import signingService from "../services/signing";
|
||||||
|
import { DraftNostrEvent, NostrEvent } from "../types/nostr-event";
|
||||||
|
|
||||||
|
export type SigningContextType = {
|
||||||
|
requestSignature: (draft: DraftNostrEvent) => Promise<NostrEvent | undefined>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SigningContext = React.createContext<SigningContextType>({
|
||||||
|
requestSignature: () => {
|
||||||
|
throw new Error("not setup yet");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export function useSigningContext() {
|
||||||
|
return useContext(SigningContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SigningProvider = ({ children }: { children: React.ReactNode }) => {
|
||||||
|
const toast = useToast();
|
||||||
|
|
||||||
|
const requestSignature = useCallback(
|
||||||
|
async (draft: DraftNostrEvent) => {
|
||||||
|
try {
|
||||||
|
return await signingService.requestSignature(draft);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
toast({
|
||||||
|
status: "error",
|
||||||
|
description: e.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[toast]
|
||||||
|
);
|
||||||
|
const context = useMemo(() => ({ requestSignature }), [requestSignature]);
|
||||||
|
|
||||||
|
return <SigningContext.Provider value={context}>{children}</SigningContext.Provider>;
|
||||||
|
};
|
@@ -5,6 +5,7 @@ import { DraftNostrEvent, PTag } from "../types/nostr-event";
|
|||||||
import clientRelaysService from "./client-relays";
|
import clientRelaysService from "./client-relays";
|
||||||
import accountService from "./account";
|
import accountService from "./account";
|
||||||
import userContactsService, { UserContacts } from "./user-contacts";
|
import userContactsService, { UserContacts } from "./user-contacts";
|
||||||
|
import signingService from "./signing";
|
||||||
|
|
||||||
export type RelayDirectory = Record<string, { read: boolean; write: boolean }>;
|
export type RelayDirectory = Record<string, { read: boolean; write: boolean }>;
|
||||||
|
|
||||||
@@ -73,18 +74,16 @@ async function savePending() {
|
|||||||
const draft = pendingDraft.value;
|
const draft = pendingDraft.value;
|
||||||
if (!draft) return;
|
if (!draft) return;
|
||||||
|
|
||||||
if (window.nostr) {
|
savingDraft.next(true);
|
||||||
savingDraft.next(true);
|
const event = await signingService.requestSignature(draft);
|
||||||
const event = await window.nostr.signEvent(draft);
|
|
||||||
|
|
||||||
const results = nostrPostAction(clientRelaysService.getWriteUrls(), event);
|
const results = nostrPostAction(clientRelaysService.getWriteUrls(), event);
|
||||||
await results.onComplete;
|
await results.onComplete;
|
||||||
|
|
||||||
savingDraft.next(false);
|
savingDraft.next(false);
|
||||||
|
|
||||||
// pass new event to contact list service
|
// pass new event to contact list service
|
||||||
userContactsService.handleEvent(event);
|
userContactsService.handleEvent(event);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function addContact(pubkey: string, relay?: string) {
|
function addContact(pubkey: string, relay?: string) {
|
||||||
|
@@ -6,6 +6,7 @@ import accountService from "./account";
|
|||||||
import { RelayConfig, RelayMode } from "../classes/relay";
|
import { RelayConfig, RelayMode } from "../classes/relay";
|
||||||
import userRelaysService, { UserRelays } from "./user-relays";
|
import userRelaysService, { UserRelays } from "./user-relays";
|
||||||
import { PersistentSubject, Subject } from "../classes/subject";
|
import { PersistentSubject, Subject } from "../classes/subject";
|
||||||
|
import signingService from "./signing";
|
||||||
|
|
||||||
export type RelayDirectory = Record<string, { read: boolean; write: boolean }>;
|
export type RelayDirectory = Record<string, { read: boolean; write: boolean }>;
|
||||||
|
|
||||||
@@ -77,15 +78,13 @@ class ClientRelayService {
|
|||||||
const oldRelayUrls = this.relays.value.filter((r) => r.mode & RelayMode.WRITE).map((r) => r.url);
|
const oldRelayUrls = this.relays.value.filter((r) => r.mode & RelayMode.WRITE).map((r) => r.url);
|
||||||
const writeUrls = unique([...oldRelayUrls, ...newRelayUrls]);
|
const writeUrls = unique([...oldRelayUrls, ...newRelayUrls]);
|
||||||
|
|
||||||
if (window.nostr) {
|
const event = await signingService.requestSignature(draft);
|
||||||
const event = await window.nostr.signEvent(draft);
|
|
||||||
|
|
||||||
const results = nostrPostAction(writeUrls, event);
|
const results = nostrPostAction(writeUrls, event);
|
||||||
await results.onComplete;
|
await results.onComplete;
|
||||||
|
|
||||||
// pass new event to the user relay service
|
// pass new event to the user relay service
|
||||||
userRelaysService.handleEvent(event);
|
userRelaysService.handleEvent(event);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getWriteUrls() {
|
getWriteUrls() {
|
||||||
|
@@ -82,7 +82,7 @@ async function pruneCache() {
|
|||||||
const keys = await db.getAllKeysFromIndex(
|
const keys = await db.getAllKeysFromIndex(
|
||||||
"dnsIdentifiers",
|
"dnsIdentifiers",
|
||||||
"updated",
|
"updated",
|
||||||
IDBKeyRange.upperBound(moment().subtract(1, "hour").unix())
|
IDBKeyRange.upperBound(moment().subtract(1, "day").unix())
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const pubkey of keys) {
|
for (const pubkey of keys) {
|
||||||
|
32
src/services/signing.tsx
Normal file
32
src/services/signing.tsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { DraftNostrEvent, NostrEvent } from "../types/nostr-event";
|
||||||
|
import accountService from "./account";
|
||||||
|
import { signEvent, getEventHash, getPublicKey } from "nostr-tools";
|
||||||
|
|
||||||
|
class SigningService {
|
||||||
|
async requestSignature(draft: DraftNostrEvent) {
|
||||||
|
const account = accountService.current.value;
|
||||||
|
|
||||||
|
if (account?.readonly) throw new Error("cant sign in readonly mode");
|
||||||
|
if (account?.useExtension) {
|
||||||
|
if (window.nostr) {
|
||||||
|
const signed = await window.nostr.signEvent(draft);
|
||||||
|
if (signed.pubkey !== account.pubkey) throw new Error("signed with the wrong pubkey!");
|
||||||
|
return signed;
|
||||||
|
} else throw new Error("missing nostr extension");
|
||||||
|
} else if (account?.secKey) {
|
||||||
|
const tmpDraft = { ...draft, pubkey: getPublicKey(account.secKey) };
|
||||||
|
const signature = signEvent(tmpDraft, account.secKey);
|
||||||
|
const event: NostrEvent = {
|
||||||
|
...tmpDraft,
|
||||||
|
id: getEventHash(tmpDraft),
|
||||||
|
sig: signature,
|
||||||
|
};
|
||||||
|
|
||||||
|
return event;
|
||||||
|
} else throw new Error("no signing method");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const signingService = new SigningService();
|
||||||
|
|
||||||
|
export default signingService;
|
@@ -26,7 +26,7 @@ export const LoginStartView = () => {
|
|||||||
relays = Object.keys(extRelays).filter((url) => extRelays[url].read);
|
relays = Object.keys(extRelays).filter((url) => extRelays[url].read);
|
||||||
}
|
}
|
||||||
|
|
||||||
accountService.addAccount({ pubkey, relays });
|
accountService.addAccount({ pubkey, relays, useExtension: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
accountService.switchAccount(pubkey);
|
accountService.switchAccount(pubkey);
|
||||||
|
@@ -17,10 +17,8 @@ export const UserCard = ({ pubkey, relay }: { pubkey: string; relay?: string })
|
|||||||
<Box>
|
<Box>
|
||||||
<Link as={ReactRouterLink} to={`/u/${normalizeToBech32(pubkey, Bech32Prefix.Pubkey)}`}>
|
<Link as={ReactRouterLink} to={`/u/${normalizeToBech32(pubkey, Bech32Prefix.Pubkey)}`}>
|
||||||
<Heading size="sm">{getUserDisplayName(metadata, pubkey)}</Heading>
|
<Heading size="sm">{getUserDisplayName(metadata, pubkey)}</Heading>
|
||||||
<Text>
|
|
||||||
<UserDnsIdentityIcon pubkey={pubkey} />
|
|
||||||
</Text>
|
|
||||||
</Link>
|
</Link>
|
||||||
|
<UserDnsIdentityIcon pubkey={pubkey} />
|
||||||
{relay && <Text>{relay}</Text>}
|
{relay && <Text>{relay}</Text>}
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@@ -16,11 +16,13 @@ export const UserProfileMenu = ({ pubkey, ...props }: { pubkey: string } & Omit<
|
|||||||
|
|
||||||
const loginAsUser = () => {
|
const loginAsUser = () => {
|
||||||
const readRelays = userRelays?.relays.filter((r) => r.mode === RelayMode.READ).map((r) => r.url) ?? [];
|
const readRelays = userRelays?.relays.filter((r) => r.mode === RelayMode.READ).map((r) => r.url) ?? [];
|
||||||
accountService.addAccount({
|
if (!accountService.hasAccount(pubkey)) {
|
||||||
pubkey,
|
accountService.addAccount({
|
||||||
relays: readRelays,
|
pubkey,
|
||||||
readonly: true,
|
relays: readRelays,
|
||||||
});
|
readonly: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
accountService.switchAccount(pubkey);
|
accountService.switchAccount(pubkey);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user