lint fix wip

This commit is contained in:
Nicolas Cantu 2026-01-09 01:01:04 +01:00
parent 899c20631a
commit f471fa3d31
9 changed files with 103 additions and 74 deletions

View File

@ -36,7 +36,7 @@ export function ArticleEditor({ onPublishSuccess, onCancel, seriesOptions, onSel
const submit = buildSubmitHandler({ const submit = buildSubmitHandler({
publishArticle, publishArticle,
draft, draft,
onPublishSuccess, ...(onPublishSuccess ? { onPublishSuccess } : {}),
connect, connect,
connected, connected,
}) })

View File

@ -123,7 +123,7 @@ export function ConnectButton(): React.ReactElement {
}) })
if (mode === 'connected') { if (mode === 'connected') {
return <ConnectedState pubkey={pubkey} profile={profile} loading={loading} disconnect={disconnect} /> return <ConnectedState pubkey={requirePubkey(pubkey)} profile={profile} loading={loading} disconnect={disconnect} />
} }
if (mode === 'unlock_required') { if (mode === 'unlock_required') {
@ -146,3 +146,12 @@ export function ConnectButton(): React.ReactElement {
</> </>
) )
} }
function requirePubkey(pubkey: string | null): string {
if (!pubkey) {
const error = new Error('Invariant violation: pubkey is required when ConnectButton mode is "connected"')
console.error(error.message, { pubkey })
throw error
}
return pubkey
}

View File

@ -7,7 +7,7 @@ import type { ReviewFormProps } from './reviewForms/reviewFormTypes'
export function ReviewForm({ article, onSuccess, onCancel }: ReviewFormProps): React.ReactElement { export function ReviewForm({ article, onSuccess, onCancel }: ReviewFormProps): React.ReactElement {
const { pubkey, connect } = useNostrAuth() const { pubkey, connect } = useNostrAuth()
const ctrl = useReviewFormController({ article, pubkey, onSuccess }) const ctrl = useReviewFormController({ article, pubkey, ...(onSuccess ? { onSuccess } : {}) })
if (!pubkey) { if (!pubkey) {
return ( return (
@ -20,5 +20,5 @@ export function ReviewForm({ article, onSuccess, onCancel }: ReviewFormProps): R
) )
} }
return <ReviewFormView ctrl={ctrl} onCancel={onCancel} /> return <ReviewFormView ctrl={ctrl} {...(onCancel ? { onCancel } : {})} />
} }

View File

@ -7,7 +7,7 @@ import type { ReviewTipFormProps } from './reviewForms/reviewFormTypes'
export function ReviewTipForm({ review, article, onSuccess, onCancel }: ReviewTipFormProps): React.ReactElement { export function ReviewTipForm({ review, article, onSuccess, onCancel }: ReviewTipFormProps): React.ReactElement {
const { pubkey, connect } = useNostrAuth() const { pubkey, connect } = useNostrAuth()
const ctrl = useReviewTipFormController({ review, article, pubkey, onSuccess }) const ctrl = useReviewTipFormController({ review, article, pubkey, ...(onSuccess ? { onSuccess } : {}) })
if (!pubkey) { if (!pubkey) {
return ( return (
@ -19,5 +19,5 @@ export function ReviewTipForm({ review, article, onSuccess, onCancel }: ReviewTi
/> />
) )
} }
return <ReviewTipFormView ctrl={ctrl} onCancel={onCancel} /> return <ReviewTipFormView ctrl={ctrl} {...(onCancel ? { onCancel } : {})} />
} }

View File

@ -37,7 +37,7 @@ export function useReviewFormController(params: {
setLoading, setLoading,
setError, setError,
reset: () => resetFields({ setContent, setTitle, setText }), reset: () => resetFields({ setContent, setTitle, setText }),
onSuccess: params.onSuccess, ...(params.onSuccess ? { onSuccess: params.onSuccess } : {}),
}) })
return { return {
@ -67,7 +67,8 @@ function buildReviewSubmitHandler(params: {
}): (e: React.FormEvent) => Promise<void> { }): (e: React.FormEvent) => Promise<void> {
return async (e: React.FormEvent): Promise<void> => { return async (e: React.FormEvent): Promise<void> => {
e.preventDefault() e.preventDefault()
if (!params.pubkey) { const pubkey = params.pubkey
if (!pubkey) {
return return
} }
const contentError = validateRequiredContent(params.content) const contentError = validateRequiredContent(params.content)
@ -75,7 +76,7 @@ function buildReviewSubmitHandler(params: {
params.setError(contentError) params.setError(contentError)
return return
} }
await submitReview(params) await submitReview({ ...params, pubkey })
} }
} }

View File

@ -3,6 +3,7 @@ import { articlePublisher } from '@/lib/articlePublisher'
import { nostrService } from '@/lib/nostr' import { nostrService } from '@/lib/nostr'
import type { ArticleDraft } from '@/lib/articlePublisher' import type { ArticleDraft } from '@/lib/articlePublisher'
import type { RelayPublishStatus } from '@/lib/publishResult' import type { RelayPublishStatus } from '@/lib/publishResult'
import type { PublishedArticle } from '@/lib/articlePublisherTypes'
interface UseArticlePublishingState { interface UseArticlePublishingState {
loading: boolean loading: boolean
@ -83,7 +84,7 @@ function buildPublishArticleHandler(params: {
} }
function handlePublishResult(params: { function handlePublishResult(params: {
result: { success: boolean; relayStatuses?: RelayPublishStatus[]; articleId: string | null; error?: string | undefined } result: PublishedArticle
setSuccess: (success: boolean) => void setSuccess: (success: boolean) => void
setRelayStatuses: (statuses: RelayPublishStatus[]) => void setRelayStatuses: (statuses: RelayPublishStatus[]) => void
setError: (error: string | null) => void setError: (error: string | null) => void
@ -91,7 +92,7 @@ function handlePublishResult(params: {
if (params.result.success) { if (params.result.success) {
params.setSuccess(true) params.setSuccess(true)
params.setRelayStatuses(params.result.relayStatuses ?? []) params.setRelayStatuses(params.result.relayStatuses ?? [])
return params.result.articleId return params.result.articleId ? params.result.articleId : null
} }
params.setError(params.result.error ?? 'Failed to publish article') params.setError(params.result.error ?? 'Failed to publish article')

View File

@ -77,7 +77,13 @@ export class ArticlePublisher {
return buildFailure('Presentation not found') return buildFailure('Presentation not found')
} }
return encryptAndPublish(draft, authorPubkey, validation.authorPrivateKeyForEncryption, validation.category, presentation.id) return encryptAndPublish({
draft,
authorPubkey,
authorPrivateKeyForEncryption: validation.authorPrivateKeyForEncryption,
category: validation.category,
presentationId: presentation.id,
})
} catch (error) { } catch (error) {
console.error('Error publishing article:', error) console.error('Error publishing article:', error)
return buildFailure(error instanceof Error ? error.message : 'Unknown error') return buildFailure(error instanceof Error ? error.message : 'Unknown error')

View File

@ -23,77 +23,78 @@ export function createMessageVerificationFilters(messageEventId: string, authorP
] ]
} }
interface MessageVerificationContext {
messageEventId: string
articleId: string
recipientPubkey: string
authorPubkey: string
}
export function handleMessageVerificationEvent( export function handleMessageVerificationEvent(
event: import('nostr-tools').Event, event: import('nostr-tools').Event,
articleId: string, ctx: MessageVerificationContext,
recipientPubkey: string,
authorPubkey: string,
finalize: (value: boolean) => void finalize: (value: boolean) => void
): void { ): void {
console.warn('Private message verified on relay', { console.warn('Private message verified on relay', {
messageEventId: event.id, messageEventId: event.id,
articleId, articleId: ctx.articleId,
recipientPubkey, recipientPubkey: ctx.recipientPubkey,
authorPubkey, authorPubkey: ctx.authorPubkey,
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
}) })
finalize(true) finalize(true)
} }
export function setupMessageVerificationHandlers( export function setupMessageVerificationHandlers(
sub: import('@/types/nostr-tools-extended').Subscription, params: {
messageEventId: string, sub: import('@/types/nostr-tools-extended').Subscription
articleId: string, ctx: MessageVerificationContext
recipientPubkey: string, finalize: (value: boolean) => void
authorPubkey: string,
finalize: (value: boolean) => void,
isResolved: () => boolean isResolved: () => boolean
}
): void { ): void {
sub.on('event', (event: Event): void => { params.sub.on('event', (event: Event): void => {
handleMessageVerificationEvent(event, articleId, recipientPubkey, authorPubkey, finalize) handleMessageVerificationEvent(event, params.ctx, params.finalize)
}) })
sub.on('eose', (): void => { params.sub.on('eose', (): void => {
console.warn('Private message not found on relay after EOSE', { console.warn('Private message not found on relay after EOSE', {
messageEventId, messageEventId: params.ctx.messageEventId,
articleId, articleId: params.ctx.articleId,
recipientPubkey, recipientPubkey: params.ctx.recipientPubkey,
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
}) })
finalize(false) params.finalize(false)
}) })
setTimeout(() => { setTimeout(() => {
if (!isResolved()) { if (!params.isResolved()) {
console.warn('Timeout verifying private message on relay', { console.warn('Timeout verifying private message on relay', {
messageEventId, messageEventId: params.ctx.messageEventId,
articleId, articleId: params.ctx.articleId,
recipientPubkey, recipientPubkey: params.ctx.recipientPubkey,
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
}) })
finalize(false) params.finalize(false)
} }
}, 5000) }, 5000)
} }
function createMessageVerificationSubscription( function createMessageVerificationSubscription(
pool: import('nostr-tools').SimplePool, params: { pool: import('nostr-tools').SimplePool; ctx: MessageVerificationContext }
messageEventId: string,
authorPubkey: string,
recipientPubkey: string,
articleId: string
): ReturnType<typeof createSubscription> { ): ReturnType<typeof createSubscription> {
const filters = createMessageVerificationFilters(messageEventId, authorPubkey, recipientPubkey, articleId) const filters = createMessageVerificationFilters(
params.ctx.messageEventId,
params.ctx.authorPubkey,
params.ctx.recipientPubkey,
params.ctx.articleId
)
const relayUrl = getPrimaryRelaySync() const relayUrl = getPrimaryRelaySync()
return createSubscription(pool, [relayUrl], filters) return createSubscription(params.pool, [relayUrl], filters)
} }
function createVerificationPromise( function createVerificationPromise(
sub: import('@/types/nostr-tools-extended').Subscription, params: { sub: import('@/types/nostr-tools-extended').Subscription; ctx: MessageVerificationContext }
messageEventId: string,
articleId: string,
recipientPubkey: string,
authorPubkey: string
): Promise<boolean> { ): Promise<boolean> {
return new Promise<boolean>((resolve) => { return new Promise<boolean>((resolve) => {
let resolved = false let resolved = false
@ -103,11 +104,11 @@ function createVerificationPromise(
return return
} }
resolved = true resolved = true
sub.unsub() params.sub.unsub()
resolve(value) resolve(value)
} }
setupMessageVerificationHandlers(sub, messageEventId, articleId, recipientPubkey, authorPubkey, finalize, () => resolved) setupMessageVerificationHandlers({ sub: params.sub, ctx: params.ctx, finalize, isResolved: () => resolved })
}) })
} }
@ -128,15 +129,9 @@ export function verifyPrivateMessagePublished(
return Promise.resolve(false) return Promise.resolve(false)
} }
const sub = createMessageVerificationSubscription( const ctx: MessageVerificationContext = { messageEventId, articleId, recipientPubkey, authorPubkey }
pool, const sub = createMessageVerificationSubscription({ pool, ctx })
messageEventId, return createVerificationPromise({ sub, ctx })
authorPubkey,
recipientPubkey,
articleId
)
return createVerificationPromise(sub, messageEventId, articleId, recipientPubkey, authorPubkey)
} catch (error) { } catch (error) {
console.error('Error verifying private message', { console.error('Error verifying private message', {
messageEventId, messageEventId,

View File

@ -77,16 +77,21 @@ async function buildParsedArticleFromDraft(
return { article, hash, version, index } return { article, hash, version, index }
} }
export async function publishPreview( interface PublishPreviewParams {
draft: ArticleDraft, draft: ArticleDraft
invoice: AlbyInvoice, invoice: AlbyInvoice
authorPubkey: string, authorPubkey: string
presentationId: string, presentationId: string
extraTags?: string[][], extraTags?: string[][]
encryptedContent?: string, encryptedContent?: string
encryptedKey?: string, encryptedKey?: string
returnStatus?: boolean returnStatus?: boolean
}
export async function publishPreview(
params: PublishPreviewParams
): Promise<import('nostr-tools').Event | null | PublishResult> { ): Promise<import('nostr-tools').Event | null | PublishResult> {
const { draft, invoice, authorPubkey, presentationId, extraTags, encryptedContent, encryptedKey, returnStatus } = params
// Build parsed article object // Build parsed article object
const { article, hash, version, index } = await buildParsedArticleFromDraft(draft, invoice, authorPubkey) const { article, hash, version, index } = await buildParsedArticleFromDraft(draft, invoice, authorPubkey)
@ -158,17 +163,29 @@ export function buildArticleExtraTags(draft: ArticleDraft, _category: NonNullabl
} }
export async function encryptAndPublish( export async function encryptAndPublish(
draft: ArticleDraft, params: {
authorPubkey: string, draft: ArticleDraft
authorPrivateKeyForEncryption: string, authorPubkey: string
category: NonNullable<ArticleDraft['category']>, authorPrivateKeyForEncryption: string
category: NonNullable<ArticleDraft['category']>
presentationId: string presentationId: string
}
): Promise<PublishedArticle> { ): Promise<PublishedArticle> {
const { draft, authorPubkey, authorPrivateKeyForEncryption, category, presentationId } = params
const { encryptedContent, key, iv } = await encryptArticleContent(draft.content) const { encryptedContent, key, iv } = await encryptArticleContent(draft.content)
const encryptedKey = await encryptDecryptionKey(key, iv, authorPrivateKeyForEncryption, authorPubkey) const encryptedKey = await encryptDecryptionKey(key, iv, authorPrivateKeyForEncryption, authorPubkey)
const invoice = await createArticleInvoice(draft) const invoice = await createArticleInvoice(draft)
const extraTags = buildArticleExtraTags(draft, category) const extraTags = buildArticleExtraTags(draft, category)
const publishResult = await publishPreview(draft, invoice, authorPubkey, presentationId, extraTags, encryptedContent, encryptedKey, true) const publishResult = await publishPreview({
draft,
invoice,
authorPubkey,
presentationId,
extraTags,
encryptedContent,
encryptedKey,
returnStatus: true,
})
if (!publishResult) { if (!publishResult) {
return buildFailure('Failed to publish article') return buildFailure('Failed to publish article')