Replace nos2x and NostrConnect with Alby authentication

- Remove nos2x and NostrConnect support
- Create new NostrAuthService using Alby (window.nostr NIP-07)
- Replace useNostrConnect with useNostrAuth in all components
- Update NostrRemoteSigner to use Alby for signing
- Delete NostrConnect-related files (nostrconnect.ts, handlers, etc.)
- Update documentation to reflect Alby-only authentication
- Remove NOSTRCONNECT_BRIDGE environment variable
- All TypeScript checks pass
This commit is contained in:
Nicolas Cantu 2025-12-27 23:54:34 +01:00
parent fd6ff4b6cc
commit cb7ee0cfd4
22 changed files with 195 additions and 352 deletions

View File

@ -4,7 +4,7 @@ Plateforme de publication d'articles scientifiques et de science-fiction avec sy
## Features
- **Nostr Wallet Integration**: Authenticate using nos2x extension (NIP-07) or NostrConnect bridge (NIP-46)
- **Nostr Authentication**: Authenticate using Alby browser extension (NIP-07)
- **Free Previews**: Public notes showing article previews
- **Paid Content**: Private notes containing full content, unlocked after 800 sats zap
- **Lightning Payments**: Integrated Alby/WebLN for Lightning payments (works with Alby and other Lightning wallets)
@ -28,15 +28,14 @@ npm run dev
## Environment Variables
- `NEXT_PUBLIC_NOSTR_RELAY_URL`: Nostr relay URL (default: wss://relay.damus.io)
- `NEXT_PUBLIC_NOSTRCONNECT_BRIDGE`: NostrConnect bridge URL (optional, nos2x extension is used by default)
## Lightning Wallet Setup
This project uses the WebLN standard for Lightning payments, which works with:
- **Alby** (recommended): Install the [Alby browser extension](https://getalby.com/)
- Other WebLN-compatible Lightning wallets
This project uses Alby browser extension for both Nostr authentication and Lightning payments:
- **Alby**: Install the [Alby browser extension](https://getalby.com/)
- Alby provides both Nostr authentication (NIP-07) and Lightning payments (WebLN)
Users need to have a Lightning wallet extension installed to make payments. The payment flow will prompt them to connect their wallet when needed.
Users need to have Alby installed to authenticate and make payments. The application will prompt them to connect when needed.
## Project Structure

View File

@ -1,5 +1,5 @@
import type { Article } from '@/types/nostr'
import { useNostrConnect } from '@/hooks/useNostrConnect'
import { useNostrAuth } from '@/hooks/useNostrAuth'
import { useArticlePayment } from '@/hooks/useArticlePayment'
import { ArticlePreview } from './ArticlePreview'
import { PaymentModal } from './PaymentModal'
@ -42,7 +42,7 @@ function ArticleMeta({
}
export function ArticleCard({ article, onUnlock }: ArticleCardProps) {
const { pubkey, connect } = useNostrConnect()
const { pubkey, connect } = useNostrAuth()
const {
loading,
error,

View File

@ -1,5 +1,5 @@
import { useState } from 'react'
import { useNostrConnect } from '@/hooks/useNostrConnect'
import { useNostrAuth } from '@/hooks/useNostrAuth'
import { useArticlePublishing } from '@/hooks/useArticlePublishing'
import type { ArticleDraft } from '@/lib/articlePublisher'
import { ArticleEditorForm } from './ArticleEditorForm'
@ -22,7 +22,7 @@ function SuccessMessage() {
}
export function ArticleEditor({ onPublishSuccess, onCancel, seriesOptions, onSelectSeries }: ArticleEditorProps) {
const { connected, pubkey, connect } = useNostrConnect()
const { connected, pubkey, connect } = useNostrAuth()
const { loading, error, success, publishArticle } = useArticlePublishing(pubkey ?? null)
const [draft, setDraft] = useState<ArticleDraft>({
title: '',

View File

@ -1,5 +1,5 @@
import { useState, useCallback } from 'react'
import { useNostrConnect } from '@/hooks/useNostrConnect'
import { useNostrAuth } from '@/hooks/useNostrAuth'
import { useAuthorPresentation } from '@/hooks/useAuthorPresentation'
import { ArticleField } from './ArticleField'
import { ArticleFormButtons } from './ArticleFormButtons'
@ -229,6 +229,6 @@ function AuthorPresentationFormView({
}
export function AuthorPresentationEditor() {
const { connected, pubkey, profile } = useNostrConnect()
const { connected, pubkey, profile } = useNostrAuth()
return <AuthorPresentationFormView pubkey={pubkey ?? null} connected={connected} profile={profile} />
}

View File

@ -1,11 +1,11 @@
import Link from 'next/link'
import { useNostrConnect } from '@/hooks/useNostrConnect'
import { useNostrAuth } from '@/hooks/useNostrAuth'
import { useAuthorPresentation } from '@/hooks/useAuthorPresentation'
import { useEffect, useState } from 'react'
import { t } from '@/lib/i18n'
export function ConditionalPublishButton() {
const { connected, pubkey } = useNostrConnect()
const { connected, pubkey } = useNostrAuth()
const { checkPresentationExists } = useAuthorPresentation(pubkey ?? null)
const [hasPresentation, setHasPresentation] = useState<boolean | null>(null)

View File

@ -1,4 +1,4 @@
import { useNostrConnect } from '@/hooks/useNostrConnect'
import { useNostrAuth } from '@/hooks/useNostrAuth'
import { ConnectedUserMenu } from './ConnectedUserMenu'
function ConnectForm({ onConnect, loading, error }: {
@ -23,7 +23,7 @@ function ConnectForm({ onConnect, loading, error }: {
}
export function ConnectButton() {
const { connected, pubkey, profile, loading, error, connect, disconnect } = useNostrConnect()
const { connected, pubkey, profile, loading, error, connect, disconnect } = useNostrAuth()
if (connected && pubkey) {
return (

View File

@ -38,7 +38,7 @@ Le sponsoring permet de soutenir directement un auteur avec **0.046 BTC** :
### Comment me connecter ?
Cliquez sur "Connect with Nostr" et autorisez la connexion avec votre portefeuille Nostr. L'application utilise l'extension nos2x (NIP-07) par défaut, ou un pont NostrConnect (NIP-46) si configuré.
Cliquez sur "Connect with Nostr" et autorisez la connexion avec Alby. L'application utilise l'extension Alby pour l'authentification Nostr (NIP-07) et les paiements Lightning (WebLN).
### J'ai besoin d'un compte ?

View File

@ -57,7 +57,6 @@ RIZFUL_API_URL=https://api.rizful.com
# Variables publiques (client-side)
NEXT_PUBLIC_NOSTR_RELAY_URL=wss://relay.damus.io
NEXT_PUBLIC_NOSTRCONNECT_BRIDGE= # Optional: nos2x extension is used by default
```
**⚠️ Important** :

View File

@ -62,7 +62,7 @@ Pour effectuer des paiements Lightning, vous devez installer une extension de po
1. Cliquez sur le bouton **"Connect with Nostr"** en haut à droite
2. Une fenêtre s'ouvrira pour vous connecter avec votre portefeuille Nostr
3. Par défaut, l'application utilise l'extension nos2x (NIP-07). Un pont NostrConnect (NIP-46) peut être configuré via la variable d'environnement `NEXT_PUBLIC_NOSTRCONNECT_BRIDGE`
3. L'application utilise l'extension Alby pour l'authentification Nostr (NIP-07) et les paiements Lightning (WebLN)
4. Autorisez la connexion dans votre portefeuille Nostr
### Que se passe-t-il après la connexion ?
@ -265,7 +265,7 @@ En tant qu'auteur, vous pouvez remercier un lecteur pour son avis :
**Je ne peux pas me connecter avec Nostr**
- Vérifiez que votre portefeuille Nostr est accessible
- Vérifiez que l'extension nos2x est installée, ou que le pont NostrConnect est configuré et accessible
- Vérifiez que l'extension Alby est installée et activée
- Essayez de rafraîchir la page
- Vérifiez votre connexion internet

View File

@ -7,7 +7,7 @@
### Nostr Paywall → zapwall4Science
- Publication d'articles avec aperçus gratuits et contenu payant
- Paiement Lightning via Alby/WebLN (remplacement de Rizful)
- Connexion via nos2x extension (NIP-07) ou NostrConnect bridge (NIP-46)
- Connexion via Alby extension (NIP-07) pour l'authentification Nostr et les paiements Lightning
- Interface TypeScript/Next.js
### Services principaux

View File

@ -1,14 +1,14 @@
import { useState, useEffect } from 'react'
import { nostrConnectService } from '@/lib/nostrconnect'
import { nostrAuthService } from '@/lib/nostrAuth'
import type { NostrConnectState } from '@/types/nostr'
export function useNostrConnect() {
const [state, setState] = useState<NostrConnectState>(nostrConnectService.getState())
export function useNostrAuth() {
const [state, setState] = useState<NostrConnectState>(nostrAuthService.getState())
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
useEffect(() => {
const unsubscribe = nostrConnectService.subscribe((newState) => {
const unsubscribe = nostrAuthService.subscribe((newState) => {
setState(newState)
})
@ -19,7 +19,7 @@ export function useNostrConnect() {
setLoading(true)
setError(null)
try {
await nostrConnectService.connect()
await nostrAuthService.connect()
} catch (e) {
setError(e instanceof Error ? e.message : 'Connection failed')
} finally {
@ -30,7 +30,7 @@ export function useNostrConnect() {
const disconnect = async () => {
setLoading(true)
try {
await nostrConnectService.disconnect()
nostrAuthService.disconnect()
} catch (e) {
setError(e instanceof Error ? e.message : 'Disconnection failed')
} finally {
@ -46,3 +46,4 @@ export function useNostrConnect() {
disconnect,
}
}

144
lib/nostrAuth.ts Normal file
View File

@ -0,0 +1,144 @@
import { nostrService } from './nostr'
import type { NostrConnectState } from '@/types/nostr'
/**
* Nostr authentication service using Alby (NIP-07)
* Alby exposes window.nostr API for Nostr authentication and signing
*/
export class NostrAuthService {
private state: NostrConnectState = {
connected: false,
pubkey: null,
profile: null,
}
private listeners: Set<(state: NostrConnectState) => void> = new Set()
constructor() {
if (typeof window !== 'undefined') {
this.loadStateFromStorage()
this.setupMessageListener()
}
}
subscribe(callback: (state: NostrConnectState) => void): () => void {
this.listeners.add(callback)
callback(this.state)
return () => {
this.listeners.delete(callback)
}
}
getState(): NostrConnectState {
return { ...this.state }
}
/**
* Check if Alby (window.nostr) is available
*/
isAvailable(): boolean {
return typeof window !== 'undefined' && typeof window.nostr !== 'undefined'
}
/**
* Connect using Alby (NIP-07)
*/
async connect(): Promise<void> {
if (!this.isAvailable()) {
throw new Error('Alby extension not available. Please install Alby browser extension.')
}
if (!window.nostr) {
throw new Error('window.nostr is not available. Please ensure Alby extension is installed and enabled.')
}
try {
const pubkey = await window.nostr.getPublicKey()
if (!pubkey) {
throw new Error('Failed to get public key from Alby')
}
this.state = {
connected: true,
pubkey,
profile: null,
}
nostrService.setPublicKey(pubkey)
this.saveStateToStorage()
this.notifyListeners()
void this.loadProfile()
} catch (e) {
console.error('Error connecting with Alby:', e)
throw new Error(`Failed to connect with Alby: ${e instanceof Error ? e.message : 'Unknown error'}`)
}
}
disconnect(): void {
this.state = {
connected: false,
pubkey: null,
profile: null,
}
this.saveStateToStorage()
this.notifyListeners()
}
private async loadProfile(): Promise<void> {
if (!this.state.pubkey) {
return
}
try {
const profile = await nostrService.getProfile(this.state.pubkey)
if (profile) {
this.state.profile = profile
this.saveStateToStorage()
this.notifyListeners()
}
} catch (e) {
console.error('Error loading profile:', e)
}
}
private setupMessageListener(): void {
window.addEventListener('storage', (e) => {
if (e.key === 'nostr_auth_state') {
this.loadStateFromStorage()
}
})
}
private loadStateFromStorage(): void {
try {
const stored = localStorage.getItem('nostr_auth_state')
if (stored) {
const parsed = JSON.parse(stored)
this.state = {
connected: parsed.connected ?? false,
pubkey: parsed.pubkey ?? null,
profile: parsed.profile ?? null,
}
if (this.state.pubkey) {
nostrService.setPublicKey(this.state.pubkey)
}
}
} catch (e) {
console.error('Error loading state from storage:', e)
}
}
private saveStateToStorage(): void {
try {
localStorage.setItem('nostr_auth_state', JSON.stringify(this.state))
} catch (e) {
console.error('Error saving state to storage:', e)
}
}
private notifyListeners(): void {
this.listeners.forEach((callback) => callback({ ...this.state }))
}
}
export const nostrAuthService = new NostrAuthService()

View File

@ -1,22 +1,21 @@
import type { EventTemplate, Event } from 'nostr-tools'
import { getEventHash, signEvent } from 'nostr-tools'
import { nostrConnectService } from './nostrconnect'
import { nostrAuthService } from './nostrAuth'
import { nostrService } from './nostr'
/**
* Remote signer using nos2x (NIP-07) or NostrConnect (NIP-46)
* Supports nos2x extension (window.nostr) and NostrConnect bridge
* Remote signer using Alby (NIP-07)
* Alby exposes window.nostr API for signing events
*/
export class NostrRemoteSigner {
/**
* Sign an event template
* Uses nos2x (NIP-07) if available, otherwise falls back to private key signing
* Sign an event template using Alby (window.nostr)
*/
async signEvent(eventTemplate: EventTemplate): Promise<Event | null> {
// Get the event hash first
const pubkey = nostrService.getPublicKey()
if (!pubkey) {
throw new Error('Public key required for signing. Please connect a Nostr wallet.')
throw new Error('Public key required for signing. Please connect with Alby.')
}
const unsignedEvent = {
@ -26,7 +25,7 @@ export class NostrRemoteSigner {
}
const eventId = getEventHash(unsignedEvent)
// Try nos2x (NIP-07) first
// Use Alby (window.nostr) for signing
if (typeof window !== 'undefined' && window.nostr) {
try {
const signedEvent = await window.nostr.signEvent({
@ -37,17 +36,17 @@ export class NostrRemoteSigner {
})
return signedEvent as Event
} catch (e) {
console.error('Error signing with nos2x:', e)
throw new Error('Failed to sign event with nos2x extension')
console.error('Error signing with Alby:', e)
throw new Error('Failed to sign event with Alby extension')
}
}
// Fallback to private key signing
// Fallback to private key signing (should not happen if Alby is properly connected)
const privateKey = nostrService.getPrivateKey()
if (!privateKey) {
throw new Error(
'Private key required for signing. ' +
'Please install nos2x extension or use a NostrConnect wallet that provides signing capabilities.'
'Alby extension required for signing. ' +
'Please install and connect Alby browser extension.'
)
}
@ -64,15 +63,15 @@ export class NostrRemoteSigner {
* Check if remote signing is available
*/
isAvailable(): boolean {
const state = nostrConnectService.getState()
const state = nostrAuthService.getState()
return state.connected && !!state.pubkey
}
/**
* Check if direct signing (with private key) is available
* Check if Alby is available
*/
isDirectSigningAvailable(): boolean {
return !!nostrService.getPrivateKey()
isAlbyAvailable(): boolean {
return typeof window !== 'undefined' && typeof window.nostr !== 'undefined'
}
}

View File

@ -1,190 +0,0 @@
import type { NostrConnectState } from '@/types/nostr'
import { nostrService } from './nostr'
import { handleNostrConnectMessage } from './nostrconnectHandler'
// Support for nos2x extension (NIP-07) and NostrConnect (NIP-46)
// nos2x uses window.nostr API directly
// NostrConnect uses a bridge for remote signing
const NOSTRCONNECT_BRIDGE = process.env.NEXT_PUBLIC_NOSTRCONNECT_BRIDGE ?? ''
export class NostrConnectService {
private state: NostrConnectState = {
connected: false,
pubkey: null,
profile: null,
}
private listeners: Set<(state: NostrConnectState) => void> = new Set()
private relayUrl: string = process.env.NEXT_PUBLIC_NOSTR_RELAY_URL ?? 'wss://relay.damus.io'
constructor() {
if (typeof window !== 'undefined') {
this.loadStateFromStorage()
this.setupMessageListener()
}
}
subscribe(callback: (state: NostrConnectState) => void): () => void {
this.listeners.add(callback)
callback(this.state)
return () => {
this.listeners.delete(callback)
}
}
getState(): NostrConnectState {
return { ...this.state }
}
private createConnectUrl(): string {
if (!NOSTRCONNECT_BRIDGE) {
throw new Error('NostrConnect bridge not configured')
}
const appName = 'zapwall4Science'
const appUrl = window.location.origin
const params = new URLSearchParams({
origin: appUrl,
name: appName,
relay: this.relayUrl,
})
return `${NOSTRCONNECT_BRIDGE}?${params.toString()}`
}
private cleanupPopup(popup: Window | null, checkClosed: number, messageHandler: (event: MessageEvent) => void) {
window.clearInterval(checkClosed)
window.removeEventListener('message', messageHandler)
if (popup && !popup.closed) {
popup.close()
}
}
private createMessageHandler(
resolve: () => void,
reject: (error: Error) => void,
cleanup: () => void
): (event: MessageEvent) => void {
return (event: MessageEvent) => {
handleNostrConnectMessage(
event,
this.state,
(pubkey, _privateKey) => {
this.state = {
connected: true,
pubkey,
profile: null,
}
this.saveStateToStorage()
this.notifyListeners()
void this.loadProfile()
cleanup()
resolve()
},
(error) => {
console.error('Connection error:', error)
cleanup()
reject(error)
}
)
}
}
connect(): Promise<void> {
return new Promise((resolve, reject) => {
const url = this.createConnectUrl()
// Open NostrConnect bridge in popup
const popup = window.open(url, 'nostrconnect', 'width=400,height=600,scrollbars=yes,resizable=yes')
if (!popup) {
reject(new Error('Popup blocked. Please allow popups for this site.'))
return
}
const checkClosed = window.setInterval(() => {
if (popup.closed) {
this.cleanupPopup(popup, checkClosed, messageHandler)
if (!this.state.connected) {
reject(new Error('Connection cancelled'))
}
}
}, 1000)
const cleanup = () => this.cleanupPopup(popup, checkClosed, messageHandler)
const messageHandler = this.createMessageHandler(resolve, reject, cleanup)
window.addEventListener('message', messageHandler)
})
}
disconnect(): void {
this.state = {
connected: false,
pubkey: null,
profile: null,
}
this.saveStateToStorage()
this.notifyListeners()
}
private async loadProfile(): Promise<void> {
if (!this.state.pubkey) {
return
}
try {
const profile = await nostrService.getProfile(this.state.pubkey)
if (profile) {
this.state.profile = profile
this.saveStateToStorage()
this.notifyListeners()
}
} catch (e) {
console.error('Error loading profile:', e)
}
}
private setupMessageListener(): void {
window.addEventListener('storage', (e) => {
if (e.key === 'nostrconnect_state') {
this.loadStateFromStorage()
}
})
}
private loadStateFromStorage(): void {
try {
const stored = localStorage.getItem('nostrconnect_state')
if (stored) {
const parsed = JSON.parse(stored)
this.state = {
connected: parsed.connected ?? false,
pubkey: parsed.pubkey ?? null,
profile: parsed.profile ?? null,
}
if (this.state.pubkey) {
nostrService.setPublicKey(this.state.pubkey)
}
}
} catch (e) {
console.error('Error loading state from storage:', e)
}
}
private saveStateToStorage(): void {
try {
localStorage.setItem('nostrconnect_state', JSON.stringify(this.state))
} catch (e) {
console.error('Error saving state to storage:', e)
}
}
private notifyListeners(): void {
this.listeners.forEach((callback) => callback({ ...this.state }))
}
}
export const nostrConnectService = new NostrConnectService()

View File

@ -1,2 +0,0 @@
export { handleNostrConnectMessage } from './nostrconnectMessageHandler'
export type { NostrConnectState } from '@/types/nostr'

View File

@ -1,106 +0,0 @@
import type { NostrConnectState } from '@/types/nostr'
import { nostrService } from './nostr'
const NOSTRCONNECT_BRIDGE = process.env.NEXT_PUBLIC_NOSTRCONNECT_BRIDGE ?? ''
interface MessageData {
type?: string
method?: string
action?: string
pubkey?: string
publicKey?: string
privateKey?: string
secretKey?: string
message?: string
error?: string
params?: {
pubkey?: string
publicKey?: string
privateKey?: string
secretKey?: string
}
}
function handleConnectMessage(
data: MessageData,
onSuccess: (pubkey: string, privateKey?: string) => void,
onError: (error: Error) => void
): boolean {
const pubkey = data.pubkey ?? data.publicKey
const privateKey = data.privateKey ?? data.secretKey
if (!pubkey) {
console.error('No pubkey in message data:', data)
onError(new Error('No pubkey received'))
return false
}
nostrService.setPublicKey(pubkey)
if (privateKey) {
nostrService.setPrivateKey(privateKey)
}
onSuccess(pubkey, privateKey)
return true
}
function handleAlternativeConnectMessage(
data: MessageData,
onSuccess: (pubkey: string, privateKey?: string) => void,
onError: (error: Error) => void
): boolean {
const pubkey =
data.pubkey ?? data.publicKey ?? data.params?.pubkey ?? data.params?.publicKey
const privateKey =
data.privateKey ?? data.secretKey ?? data.params?.privateKey ?? data.params?.secretKey
if (!pubkey) {
console.error('No pubkey in message data:', data)
onError(new Error('No pubkey received'))
return false
}
nostrService.setPublicKey(pubkey)
if (privateKey) {
nostrService.setPrivateKey(privateKey)
}
onSuccess(pubkey, privateKey)
return true
}
function handleErrorMessage(data: MessageData, onError: (error: Error) => void): void {
const errorMessage = data.message ?? data.error ?? 'Connection failed'
console.error('Connection error:', errorMessage)
onError(new Error(errorMessage))
}
/**
* Handle NostrConnect connection message
*/
export function handleNostrConnectMessage(
event: MessageEvent,
_state: NostrConnectState,
onSuccess: (pubkey: string, privateKey?: string) => void,
onError: (error: Error) => void
): void {
const bridgeOrigin = new URL(NOSTRCONNECT_BRIDGE).origin
if (event.origin !== bridgeOrigin) {
console.warn('Origin mismatch:', event.origin, 'expected:', bridgeOrigin)
return
}
const data = event.data as MessageData | undefined
if (!data) {
return
}
const messageType = data.type ?? data.method ?? data.action
if (messageType === 'nostrconnect:connect' || messageType === 'connect') {
handleConnectMessage(data, onSuccess, onError)
} else if (messageType === 'nostrconnect:error' || messageType === 'error') {
handleErrorMessage(data, onError)
} else if (data.method === 'connect' || data.action === 'connect') {
handleAlternativeConnectMessage(data, onSuccess, onError)
}
}

View File

@ -3,7 +3,6 @@ const nextConfig = {
reactStrictMode: true,
env: {
NOSTR_RELAY_URL: process.env.NEXT_PUBLIC_NOSTR_RELAY_URL || 'wss://relay.damus.io',
NOSTRCONNECT_BRIDGE: process.env.NEXT_PUBLIC_NOSTRCONNECT_BRIDGE || '',
},
}

View File

@ -1,6 +1,6 @@
import { useState, useEffect, useMemo, useCallback } from 'react'
import { useArticles } from '@/hooks/useArticles'
import { useNostrConnect } from '@/hooks/useNostrConnect'
import { useNostrAuth } from '@/hooks/useNostrAuth'
import { applyFiltersAndSort } from '@/lib/articleFiltering'
import type { Article } from '@/types/nostr'
import type { ArticleFilters } from '@/components/ArticleFilters'
@ -85,7 +85,7 @@ function useUnlockHandler(
}
function useHomeController() {
const { } = useNostrConnect()
const { } = useNostrAuth()
const {
searchQuery,
setSearchQuery,

View File

@ -4,7 +4,7 @@ import Head from 'next/head'
import { PageHeader } from '@/components/PageHeader'
import { Footer } from '@/components/Footer'
import { AuthorPresentationEditor } from '@/components/AuthorPresentationEditor'
import { useNostrConnect } from '@/hooks/useNostrConnect'
import { useNostrAuth } from '@/hooks/useNostrAuth'
import { useAuthorPresentation } from '@/hooks/useAuthorPresentation'
import { t } from '@/lib/i18n'
@ -59,7 +59,7 @@ function PresentationLayout() {
}
export default function PresentationPage() {
const { connected, pubkey } = useNostrConnect()
const { connected, pubkey } = useNostrAuth()
usePresentationRedirect(connected, pubkey)
return <PresentationLayout />
}

View File

@ -3,7 +3,7 @@ import { useRouter } from 'next/router'
import type { ArticleFilters } from '@/components/ArticleFilters'
import type { NostrProfile } from '@/types/nostr'
import { ProfileView } from '@/components/ProfileView'
import { useNostrConnect } from '@/hooks/useNostrConnect'
import { useNostrAuth } from '@/hooks/useNostrAuth'
import { useUserArticles } from '@/hooks/useUserArticles'
import { nostrService } from '@/lib/nostr'
@ -49,7 +49,7 @@ function useRedirectWhenDisconnected(connected: boolean, pubkey: string | null)
}
function useProfileController() {
const { connected, pubkey: currentPubkey } = useNostrConnect()
const { connected, pubkey: currentPubkey } = useNostrAuth()
const [searchQuery, setSearchQuery] = useState('')
const [filters, setFilters] = useState<ArticleFilters>({
authorPubkey: null,

View File

@ -2,7 +2,7 @@ import Head from 'next/head'
import { useRouter } from 'next/router'
import { ArticleEditor } from '@/components/ArticleEditor'
import { useEffect, useState } from 'react'
import { useNostrConnect } from '@/hooks/useNostrConnect'
import { useNostrAuth } from '@/hooks/useNostrAuth'
import { getSeriesByAuthor } from '@/lib/seriesQueries'
import { t } from '@/lib/i18n'
@ -33,7 +33,7 @@ function PublishHero({ onBack }: { onBack: () => void }) {
export default function PublishPage() {
const router = useRouter()
const { pubkey } = useNostrConnect()
const { pubkey } = useNostrAuth()
const [seriesOptions, setSeriesOptions] = useState<{ id: string; title: string }[]>([])
const handlePublishSuccess = () => {

View File

@ -1,4 +1,5 @@
// Type definitions for NIP-07 (nos2x extension)
// Type definitions for NIP-07 (Alby extension)
// Alby exposes window.nostr API for Nostr authentication and signing
declare global {
interface Window {
nostr?: {
@ -27,4 +28,3 @@ declare global {
}
export {}