You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

103 lines
4.9 KiB

/**
* Standalone React context for Nostr so HMR on `NostrProvider/index.tsx` does not recreate
* `createContext()` (which breaks `useNostr` in providers like InterestListProvider after Fast Refresh).
*/
import type {
TAccountPointer,
TDraftEvent,
TProfile,
TPublishOptions,
TRelayList
} from '@/types'
import { Event, VerifiedEvent } from 'nostr-tools'
import { createContext, useContext } from 'react'
export type TNostrContext = {
isInitialized: boolean
/** True while replaceable events + relay list are loading from cache/relays after login or account switch. */
isAccountSessionHydrating: boolean
pubkey: string | null
profile: TProfile | null
profileEvent: Event | null
relayList: TRelayList | null
cacheRelayListEvent: Event | null
/** Kind 10243 (HTTPS index relays); null if none, undefined while not loaded. */
httpRelayListEvent: Event | null | undefined
followListEvent: Event | null
muteListEvent: Event | null
bookmarkListEvent: Event | null
interestListEvent: Event | null
favoriteRelaysEvent: Event | null
blockedRelaysEvent: Event | null
userEmojiListEvent: Event | null
rssFeedListEvent: Event | null
account: TAccountPointer | null
accounts: TAccountPointer[]
nsec: string | null
ncryptsec: string | null
/** True when the session can sign (not read-only npub fallback). */
canSignEvents: boolean
/** Anonymous write session: fresh key per publish/sign/auth. */
isAnonSession: boolean
/** Stable identity features (profile, follow, mute, lists). False in anon write mode. */
canManageIdentity: boolean
/** Returns the new session pubkey on success, or `null` if logout / switch failed. */
switchAccount: (account: TAccountPointer | null) => Promise<string | null>
/** View an account read-only (notifications, relays) without matching the browser extension. */
viewAccountAsReadOnly: (account: TAccountPointer) => Promise<string | null>
/** Reconnect NIP-07 when the extension pubkey matches the stored preferred account. */
retryNip07SignerForPreferredAccount: () => Promise<boolean>
/** Sign in with whichever pubkey the browser extension exposes now. */
adoptExtensionNip07Identity: () => Promise<void>
/** True while the login modal must stay open for an in-flight NIP-07 authorize. */
isNip07LoginInFlight: boolean
nsecLogin: (nsec: string, password?: string, needSetup?: boolean) => Promise<string>
ncryptsecLogin: (ncryptsec: string) => Promise<string>
nip07Login: () => Promise<string | null>
bunkerLogin: (bunker: string) => Promise<string>
nostrConnectionLogin: (clientSecretKey: Uint8Array, connectionString: string) => Promise<string>
npubLogin(npub: string): Promise<string>
removeAccount: (account: TAccountPointer) => void
/** Remove locally stored nsec/ncryptsec; account becomes read-only npub until remote login. */
discardLocalPrivateKey: () => void
publish: (draftEvent: TDraftEvent, options?: TPublishOptions) => Promise<Event>
attemptDelete: (targetEvent: Event) => Promise<void>
signHttpAuth: (url: string, method: string) => Promise<string>
signEvent: (draftEvent: TDraftEvent) => Promise<VerifiedEvent>
nip04Encrypt: (pubkey: string, plainText: string) => Promise<string>
nip04Decrypt: (pubkey: string, cipherText: string) => Promise<string>
startLogin: () => void
checkLogin: <T>(cb?: () => T | Promise<T>) => Promise<T | void>
updateRelayListEvent: (relayListEvent: Event) => Promise<void>
updateCacheRelayListEvent: (cacheRelayListEvent: Event) => Promise<void>
updateHttpRelayListEvent: (httpRelayListEvent: Event) => Promise<void>
updateProfileEvent: (profileEvent: Event) => Promise<void>
updateFollowListEvent: (followListEvent: Event) => Promise<void>
updateMuteListEvent: (muteListEvent: Event, privateTags: string[][]) => Promise<void>
updateBookmarkListEvent: (bookmarkListEvent: Event) => Promise<void>
updateInterestListEvent: (interestListEvent: Event) => Promise<void>
updateFavoriteRelaysEvent: (favoriteRelaysEvent: Event) => Promise<void>
updateBlockedRelaysEvent: (blockedRelaysEvent: Event) => Promise<void>
updateRssFeedListEvent: (rssFeedListEvent: Event) => Promise<void>
updateUserEmojiListEvent: (userEmojiListEvent: Event) => Promise<void>
/**
* Re-run the full account network hydrate (relay lists + replaceable merge + prewarm), bypassing the
* 24h throttle. Resolves when the hydrate pass finishes. No-op when logged out.
*/
requestAccountNetworkHydrate: () => Promise<void>
}
export const NostrContext = createContext<TNostrContext | undefined>(undefined)
export function useNostr(): TNostrContext {
const context = useContext(NostrContext)
if (!context) {
throw new Error('useNostr must be used within a NostrProvider')
}
return context
}
/** Returns undefined when outside NostrProvider (e.g. embedded notes in createRoot trees). */
export function useNostrOptional(): TNostrContext | undefined {
return useContext(NostrContext)
}