lint fix wip
This commit is contained in:
parent
964f4aeb60
commit
9f7a0e1527
@ -4,10 +4,13 @@ import { storePrivateContent, getStoredPrivateContent } from './articleStorage'
|
||||
import { buildTags } from './nostrTagSystem'
|
||||
import { PLATFORM_SERVICE } from './platformConfig'
|
||||
import { generateSeriesHashId, generatePublicationHashId } from './hashIdGenerator'
|
||||
import { parseObjectId } from './urlGenerator'
|
||||
import { buildObjectId } from './urlGenerator'
|
||||
import type { ArticleDraft, PublishedArticle } from './articlePublisher'
|
||||
import type { AlbyInvoice } from '@/types/alby'
|
||||
import type { Review, Series } from '@/types/nostr'
|
||||
import type { Article, Review, Series } from '@/types/nostr'
|
||||
import { writeOrchestrator } from './writeOrchestrator'
|
||||
import { finalizeEvent } from 'nostr-tools'
|
||||
import { hexToBytes } from 'nostr-tools/utils'
|
||||
|
||||
export interface ArticleUpdateResult extends PublishedArticle {
|
||||
originalArticleId: string
|
||||
@ -36,6 +39,55 @@ async function ensurePresentation(authorPubkey: string): Promise<string> {
|
||||
return presentation.id
|
||||
}
|
||||
|
||||
async function buildParsedArticleFromDraft(
|
||||
draft: ArticleDraft,
|
||||
invoice: AlbyInvoice,
|
||||
authorPubkey: string
|
||||
): Promise<{ article: Article; hash: string; version: number; index: number }> {
|
||||
const category = draft.category === 'science-fiction' ? 'sciencefiction' : draft.category === 'scientific-research' ? 'research' : 'sciencefiction'
|
||||
|
||||
const hashId = await generatePublicationHashId({
|
||||
pubkey: authorPubkey,
|
||||
title: draft.title,
|
||||
preview: draft.preview,
|
||||
category,
|
||||
seriesId: draft.seriesId ?? undefined,
|
||||
bannerUrl: draft.bannerUrl ?? undefined,
|
||||
zapAmount: draft.zapAmount,
|
||||
})
|
||||
|
||||
const hash = hashId
|
||||
const version = 0
|
||||
const index = 0
|
||||
const id = buildObjectId(hash, index, version)
|
||||
|
||||
const article: Article = {
|
||||
id,
|
||||
hash,
|
||||
version,
|
||||
index,
|
||||
pubkey: authorPubkey,
|
||||
title: draft.title,
|
||||
preview: draft.preview,
|
||||
content: '',
|
||||
description: draft.preview,
|
||||
contentDescription: draft.preview,
|
||||
createdAt: Math.floor(Date.now() / 1000),
|
||||
zapAmount: draft.zapAmount,
|
||||
paid: false,
|
||||
thumbnailUrl: draft.bannerUrl ?? '',
|
||||
invoice: invoice.invoice,
|
||||
paymentHash: invoice.paymentHash ?? undefined,
|
||||
category: draft.category,
|
||||
...(draft.seriesId ? { seriesId: draft.seriesId } : {}),
|
||||
...(draft.bannerUrl ? { bannerUrl: draft.bannerUrl } : {}),
|
||||
...(draft.pages && draft.pages.length > 0 ? { pages: draft.pages } : {}),
|
||||
kindType: 'article',
|
||||
}
|
||||
|
||||
return { article, hash, version, index }
|
||||
}
|
||||
|
||||
async function publishPreviewWithInvoice(
|
||||
draft: ArticleDraft,
|
||||
invoice: AlbyInvoice,
|
||||
@ -43,9 +95,48 @@ async function publishPreviewWithInvoice(
|
||||
presentationId: string,
|
||||
extraTags?: string[][]
|
||||
): Promise<import('nostr-tools').Event | null> {
|
||||
const previewEvent = await createPreviewEvent(draft, invoice, authorPubkey, presentationId, extraTags)
|
||||
const publishedEvent = await nostrService.publishEvent(previewEvent)
|
||||
return publishedEvent ?? null
|
||||
// Build parsed article object
|
||||
const { article, hash, version, index } = await buildParsedArticleFromDraft(draft, invoice, authorPubkey)
|
||||
|
||||
// Build event template
|
||||
const previewEventTemplate = await createPreviewEvent(draft, invoice, authorPubkey, presentationId, extraTags)
|
||||
|
||||
// Set private key in orchestrator
|
||||
const privateKey = nostrService.getPrivateKey()
|
||||
if (!privateKey) {
|
||||
throw new Error('Private key required for signing')
|
||||
}
|
||||
writeOrchestrator.setPrivateKey(privateKey)
|
||||
|
||||
// Finalize event
|
||||
const secretKey = hexToBytes(privateKey)
|
||||
const event = finalizeEvent(previewEventTemplate, secretKey)
|
||||
|
||||
// Get active relays
|
||||
const { relaySessionManager } = await import('./relaySessionManager')
|
||||
const activeRelays = await relaySessionManager.getActiveRelays()
|
||||
const { getPrimaryRelay } = await import('./config')
|
||||
const relays = activeRelays.length > 0 ? activeRelays : [await getPrimaryRelay()]
|
||||
|
||||
// Publish via writeOrchestrator (parallel network + local write)
|
||||
const result = await writeOrchestrator.writeAndPublish(
|
||||
{
|
||||
objectType: 'publication',
|
||||
hash,
|
||||
event,
|
||||
parsed: article,
|
||||
version,
|
||||
hidden: false,
|
||||
index,
|
||||
},
|
||||
relays
|
||||
)
|
||||
|
||||
if (!result.success) {
|
||||
return null
|
||||
}
|
||||
|
||||
return event
|
||||
}
|
||||
|
||||
export async function publishSeries(params: {
|
||||
@ -60,18 +151,27 @@ export async function publishSeries(params: {
|
||||
ensureKeys(params.authorPubkey, params.authorPrivateKey)
|
||||
const {category} = params
|
||||
requireCategory(category)
|
||||
const event = await buildSeriesEvent(params, category)
|
||||
const published = await nostrService.publishEvent(event)
|
||||
if (!published) {
|
||||
throw new Error('Failed to publish series')
|
||||
}
|
||||
const parsed = parseObjectId(published.id)
|
||||
const {hash: parsedHash, version: parsedVersion, index: parsedIndex} = parsed
|
||||
const hash = parsedHash ?? published.id
|
||||
const version = parsedVersion ?? 0
|
||||
const index = parsedIndex ?? 0
|
||||
return {
|
||||
id: published.id,
|
||||
|
||||
// Map category to new system
|
||||
const newCategory = category === 'science-fiction' ? 'sciencefiction' : 'research'
|
||||
|
||||
// Generate hash ID from series data
|
||||
const hashId = await generateSeriesHashId({
|
||||
pubkey: params.authorPubkey,
|
||||
title: params.title,
|
||||
description: params.description,
|
||||
category: newCategory,
|
||||
coverUrl: params.coverUrl ?? undefined,
|
||||
})
|
||||
|
||||
const hash = hashId
|
||||
const version = 0
|
||||
const index = 0
|
||||
const id = buildObjectId(hash, index, version)
|
||||
|
||||
// Build parsed Series object
|
||||
const parsedSeries: Series = {
|
||||
id,
|
||||
hash,
|
||||
version,
|
||||
index,
|
||||
@ -84,6 +184,46 @@ export async function publishSeries(params: {
|
||||
...(params.coverUrl ? { coverUrl: params.coverUrl } : {}),
|
||||
kindType: 'series',
|
||||
}
|
||||
|
||||
// Build event template
|
||||
const eventTemplate = await buildSeriesEvent(params, category)
|
||||
|
||||
// Set private key in orchestrator
|
||||
const privateKey = params.authorPrivateKey ?? nostrService.getPrivateKey()
|
||||
if (!privateKey) {
|
||||
throw new Error('Private key required for signing')
|
||||
}
|
||||
writeOrchestrator.setPrivateKey(privateKey)
|
||||
|
||||
// Finalize event
|
||||
const secretKey = hexToBytes(privateKey)
|
||||
const event = finalizeEvent(eventTemplate, secretKey)
|
||||
|
||||
// Get active relays
|
||||
const { relaySessionManager } = await import('./relaySessionManager')
|
||||
const activeRelays = await relaySessionManager.getActiveRelays()
|
||||
const { getPrimaryRelay } = await import('./config')
|
||||
const relays = activeRelays.length > 0 ? activeRelays : [await getPrimaryRelay()]
|
||||
|
||||
// Publish via writeOrchestrator (parallel network + local write)
|
||||
const result = await writeOrchestrator.writeAndPublish(
|
||||
{
|
||||
objectType: 'series',
|
||||
hash,
|
||||
event,
|
||||
parsed: parsedSeries,
|
||||
version,
|
||||
hidden: false,
|
||||
index,
|
||||
},
|
||||
relays
|
||||
)
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error('Failed to publish series')
|
||||
}
|
||||
|
||||
return parsedSeries
|
||||
}
|
||||
|
||||
async function buildSeriesEvent(
|
||||
@ -166,18 +306,28 @@ export async function publishReview(params: {
|
||||
ensureKeys(params.reviewerPubkey, params.authorPrivateKey)
|
||||
const {category} = params
|
||||
requireCategory(category)
|
||||
const event = await buildReviewEvent(params, category)
|
||||
const published = await nostrService.publishEvent(event)
|
||||
if (!published) {
|
||||
throw new Error('Failed to publish review')
|
||||
}
|
||||
const parsed = parseObjectId(published.id)
|
||||
const {hash: parsedHash, version: parsedVersion, index: parsedIndex} = parsed
|
||||
const hash = parsedHash ?? published.id
|
||||
const version = parsedVersion ?? 0
|
||||
const index = parsedIndex ?? 0
|
||||
return {
|
||||
id: published.id,
|
||||
|
||||
// Map category to new system
|
||||
const newCategory = category === 'science-fiction' ? 'sciencefiction' : 'research'
|
||||
|
||||
// Generate hash ID from review data
|
||||
const { generateReviewHashId } = await import('./hashIdGenerator')
|
||||
const hashId = await generateReviewHashId({
|
||||
pubkey: params.reviewerPubkey,
|
||||
articleId: params.articleId,
|
||||
reviewerPubkey: params.reviewerPubkey,
|
||||
content: params.content,
|
||||
...(params.title ? { title: params.title } : {}),
|
||||
})
|
||||
|
||||
const hash = hashId
|
||||
const version = 0
|
||||
const index = 0
|
||||
const id = buildObjectId(hash, index, version)
|
||||
|
||||
// Build parsed Review object
|
||||
const parsedReview: Review = {
|
||||
id,
|
||||
hash,
|
||||
version,
|
||||
index,
|
||||
@ -186,11 +336,51 @@ export async function publishReview(params: {
|
||||
reviewerPubkey: params.reviewerPubkey,
|
||||
content: params.content,
|
||||
description: params.content.substring(0, 200),
|
||||
createdAt: published.created_at,
|
||||
createdAt: Math.floor(Date.now() / 1000),
|
||||
...(params.title ? { title: params.title } : {}),
|
||||
...(params.text ? { text: params.text } : {}),
|
||||
kindType: 'review',
|
||||
}
|
||||
|
||||
// Build event template
|
||||
const eventTemplate = await buildReviewEvent(params, category)
|
||||
|
||||
// Set private key in orchestrator
|
||||
const privateKey = params.authorPrivateKey ?? nostrService.getPrivateKey()
|
||||
if (!privateKey) {
|
||||
throw new Error('Private key required for signing')
|
||||
}
|
||||
writeOrchestrator.setPrivateKey(privateKey)
|
||||
|
||||
// Finalize event
|
||||
const secretKey = hexToBytes(privateKey)
|
||||
const event = finalizeEvent(eventTemplate, secretKey)
|
||||
|
||||
// Get active relays
|
||||
const { relaySessionManager } = await import('./relaySessionManager')
|
||||
const activeRelays = await relaySessionManager.getActiveRelays()
|
||||
const { getPrimaryRelay } = await import('./config')
|
||||
const relays = activeRelays.length > 0 ? activeRelays : [await getPrimaryRelay()]
|
||||
|
||||
// Publish via writeOrchestrator (parallel network + local write)
|
||||
const result = await writeOrchestrator.writeAndPublish(
|
||||
{
|
||||
objectType: 'review',
|
||||
hash,
|
||||
event,
|
||||
parsed: parsedReview,
|
||||
version,
|
||||
hidden: false,
|
||||
index,
|
||||
},
|
||||
relays
|
||||
)
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error('Failed to publish review')
|
||||
}
|
||||
|
||||
return parsedReview
|
||||
}
|
||||
|
||||
async function buildReviewEvent(
|
||||
|
||||
@ -5,6 +5,12 @@ import { createArticleInvoice, createPreviewEvent } from './articleInvoice'
|
||||
import { encryptArticleContent, encryptDecryptionKey } from './articleEncryption'
|
||||
import { storePrivateContent } from './articleStorage'
|
||||
import type { PublishResult } from './publishResult'
|
||||
import { writeOrchestrator } from './writeOrchestrator'
|
||||
import { finalizeEvent } from 'nostr-tools'
|
||||
import { hexToBytes } from 'nostr-tools/utils'
|
||||
import { generatePublicationHashId } from './hashIdGenerator'
|
||||
import { buildObjectId } from './urlGenerator'
|
||||
import type { Article } from '@/types/nostr'
|
||||
|
||||
export function buildFailure(error?: string): PublishedArticle {
|
||||
const base: PublishedArticle = {
|
||||
@ -15,6 +21,55 @@ export function buildFailure(error?: string): PublishedArticle {
|
||||
return error ? { ...base, error } : base
|
||||
}
|
||||
|
||||
async function buildParsedArticleFromDraft(
|
||||
draft: ArticleDraft,
|
||||
invoice: AlbyInvoice,
|
||||
authorPubkey: string
|
||||
): Promise<{ article: Article; hash: string; version: number; index: number }> {
|
||||
const category = draft.category === 'science-fiction' ? 'sciencefiction' : draft.category === 'scientific-research' ? 'research' : 'sciencefiction'
|
||||
|
||||
const hashId = await generatePublicationHashId({
|
||||
pubkey: authorPubkey,
|
||||
title: draft.title,
|
||||
preview: draft.preview,
|
||||
category,
|
||||
seriesId: draft.seriesId ?? undefined,
|
||||
bannerUrl: draft.bannerUrl ?? undefined,
|
||||
zapAmount: draft.zapAmount,
|
||||
})
|
||||
|
||||
const hash = hashId
|
||||
const version = 0
|
||||
const index = 0
|
||||
const id = buildObjectId(hash, index, version)
|
||||
|
||||
const article: Article = {
|
||||
id,
|
||||
hash,
|
||||
version,
|
||||
index,
|
||||
pubkey: authorPubkey,
|
||||
title: draft.title,
|
||||
preview: draft.preview,
|
||||
content: '',
|
||||
description: draft.preview,
|
||||
contentDescription: draft.preview,
|
||||
createdAt: Math.floor(Date.now() / 1000),
|
||||
zapAmount: draft.zapAmount,
|
||||
paid: false,
|
||||
thumbnailUrl: draft.bannerUrl ?? '',
|
||||
invoice: invoice.invoice,
|
||||
paymentHash: invoice.paymentHash ?? undefined,
|
||||
category: draft.category,
|
||||
...(draft.seriesId ? { seriesId: draft.seriesId } : {}),
|
||||
...(draft.bannerUrl ? { bannerUrl: draft.bannerUrl } : {}),
|
||||
...(draft.pages && draft.pages.length > 0 ? { pages: draft.pages } : {}),
|
||||
kindType: 'article',
|
||||
}
|
||||
|
||||
return { article, hash, version, index }
|
||||
}
|
||||
|
||||
export async function publishPreview(
|
||||
draft: ArticleDraft,
|
||||
invoice: AlbyInvoice,
|
||||
@ -25,12 +80,64 @@ export async function publishPreview(
|
||||
encryptedKey?: string,
|
||||
returnStatus?: boolean
|
||||
): Promise<import('nostr-tools').Event | null | PublishResult> {
|
||||
const previewEvent = await createPreviewEvent(draft, invoice, authorPubkey, presentationId, extraTags, encryptedContent, encryptedKey)
|
||||
if (returnStatus) {
|
||||
return await nostrService.publishEvent(previewEvent, true)
|
||||
// Build parsed article object
|
||||
const { article, hash, version, index } = await buildParsedArticleFromDraft(draft, invoice, authorPubkey)
|
||||
|
||||
// Build event template
|
||||
const previewEventTemplate = await createPreviewEvent(draft, invoice, authorPubkey, presentationId, extraTags, encryptedContent, encryptedKey)
|
||||
|
||||
// Set private key in orchestrator
|
||||
const privateKey = nostrService.getPrivateKey()
|
||||
if (!privateKey) {
|
||||
throw new Error('Private key required for signing')
|
||||
}
|
||||
const publishedEvent = await nostrService.publishEvent(previewEvent, false)
|
||||
return publishedEvent ?? null
|
||||
writeOrchestrator.setPrivateKey(privateKey)
|
||||
|
||||
// Finalize event
|
||||
const secretKey = hexToBytes(privateKey)
|
||||
const event = finalizeEvent(previewEventTemplate, secretKey)
|
||||
|
||||
// Get active relays
|
||||
const { relaySessionManager } = await import('./relaySessionManager')
|
||||
const activeRelays = await relaySessionManager.getActiveRelays()
|
||||
const { getPrimaryRelay } = await import('./config')
|
||||
const relays = activeRelays.length > 0 ? activeRelays : [await getPrimaryRelay()]
|
||||
|
||||
// Publish via writeOrchestrator (parallel network + local write)
|
||||
const result = await writeOrchestrator.writeAndPublish(
|
||||
{
|
||||
objectType: 'publication',
|
||||
hash,
|
||||
event,
|
||||
parsed: article,
|
||||
version,
|
||||
hidden: false,
|
||||
index,
|
||||
},
|
||||
relays
|
||||
)
|
||||
|
||||
if (!result.success) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (returnStatus) {
|
||||
// Return PublishResult format
|
||||
const { publishResult } = await import('./publishResult')
|
||||
return {
|
||||
event,
|
||||
relayStatuses: relays.map((relayUrl, idx) => {
|
||||
const isSuccess = typeof result.published === 'object' && result.published.includes(relayUrl)
|
||||
return {
|
||||
relayUrl,
|
||||
success: isSuccess,
|
||||
error: isSuccess ? undefined : 'Failed to publish',
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
return event
|
||||
}
|
||||
|
||||
export function buildArticleExtraTags(draft: ArticleDraft, _category: NonNullable<ArticleDraft['category']>): string[][] {
|
||||
|
||||
@ -241,7 +241,7 @@ class NotificationDetector {
|
||||
/**
|
||||
* Get notification title based on type
|
||||
*/
|
||||
private getNotificationTitle(type: NotificationType, _obj: CachedObject): string {
|
||||
private _getNotificationTitle(type: NotificationType, _obj: CachedObject): string {
|
||||
switch (type) {
|
||||
case 'purchase':
|
||||
return 'Nouvel achat'
|
||||
@ -263,7 +263,7 @@ class NotificationDetector {
|
||||
/**
|
||||
* Get notification message based on type
|
||||
*/
|
||||
private getNotificationMessage(type: NotificationType, _obj: CachedObject): string {
|
||||
private _getNotificationMessage(type: NotificationType, _obj: CachedObject): string {
|
||||
switch (type) {
|
||||
case 'purchase':
|
||||
return `Vous avez acheté un article`
|
||||
|
||||
@ -150,6 +150,24 @@ export async function publishReviewTipNote(params: {
|
||||
|
||||
tags.push(['json', paymentJson])
|
||||
|
||||
// Build parsed ReviewTip object
|
||||
const parsedReviewTip: ReviewTip = {
|
||||
id,
|
||||
hash: hashId,
|
||||
version: 0,
|
||||
index: 0,
|
||||
payerPubkey: params.payerPubkey,
|
||||
articleId: params.articleId,
|
||||
reviewId: params.reviewId,
|
||||
reviewerPubkey: params.reviewerPubkey,
|
||||
authorPubkey: params.authorPubkey,
|
||||
amount: params.amount,
|
||||
paymentHash: params.paymentHash,
|
||||
createdAt: Math.floor(Date.now() / 1000),
|
||||
...(params.text ? { text: params.text } : {}),
|
||||
kindType: 'review_tip',
|
||||
}
|
||||
|
||||
const content = params.text
|
||||
? `Review tip confirmed: ${params.amount} sats for review ${params.reviewId}\n\n${params.text}`
|
||||
: `Review tip confirmed: ${params.amount} sats for review ${params.reviewId}`
|
||||
@ -162,7 +180,37 @@ export async function publishReviewTipNote(params: {
|
||||
}
|
||||
|
||||
nostrService.setPrivateKey(params.payerPrivateKey)
|
||||
return nostrService.publishEvent(eventTemplate)
|
||||
writeOrchestrator.setPrivateKey(params.payerPrivateKey)
|
||||
|
||||
// Finalize event
|
||||
const secretKey = hexToBytes(params.payerPrivateKey)
|
||||
const event = finalizeEvent(eventTemplate, secretKey)
|
||||
|
||||
// Get active relays
|
||||
const { relaySessionManager } = await import('./relaySessionManager')
|
||||
const activeRelays = await relaySessionManager.getActiveRelays()
|
||||
const { getPrimaryRelay } = await import('./config')
|
||||
const relays = activeRelays.length > 0 ? activeRelays : [await getPrimaryRelay()]
|
||||
|
||||
// Publish via writeOrchestrator (parallel network + local write)
|
||||
const result = await writeOrchestrator.writeAndPublish(
|
||||
{
|
||||
objectType: 'review_tip',
|
||||
hash: hashId,
|
||||
event,
|
||||
parsed: parsedReviewTip,
|
||||
version: 0,
|
||||
hidden: false,
|
||||
index: 0,
|
||||
},
|
||||
relays
|
||||
)
|
||||
|
||||
if (!result.success) {
|
||||
return null
|
||||
}
|
||||
|
||||
return event
|
||||
}
|
||||
|
||||
/**
|
||||
@ -237,6 +285,23 @@ export async function publishSponsoringNote(params: {
|
||||
|
||||
tags.push(['json', paymentJson])
|
||||
|
||||
// Build parsed Sponsoring object
|
||||
const parsedSponsoring: Sponsoring = {
|
||||
id,
|
||||
hash: hashId,
|
||||
version: 0,
|
||||
index: 0,
|
||||
payerPubkey: params.payerPubkey,
|
||||
authorPubkey: params.authorPubkey,
|
||||
amount: params.amount,
|
||||
paymentHash: params.paymentHash,
|
||||
createdAt: Math.floor(Date.now() / 1000),
|
||||
...(params.seriesId ? { seriesId: params.seriesId } : {}),
|
||||
...(params.articleId ? { articleId: params.articleId } : {}),
|
||||
...(params.text ? { text: params.text } : {}),
|
||||
kindType: 'sponsoring',
|
||||
}
|
||||
|
||||
const content = params.text
|
||||
? `Sponsoring confirmed: ${params.amount} sats for author ${params.authorPubkey.substring(0, 16)}...\n\n${params.text}`
|
||||
: `Sponsoring confirmed: ${params.amount} sats for author ${params.authorPubkey.substring(0, 16)}...`
|
||||
@ -249,5 +314,35 @@ export async function publishSponsoringNote(params: {
|
||||
}
|
||||
|
||||
nostrService.setPrivateKey(params.payerPrivateKey)
|
||||
return nostrService.publishEvent(eventTemplate)
|
||||
writeOrchestrator.setPrivateKey(params.payerPrivateKey)
|
||||
|
||||
// Finalize event
|
||||
const secretKey = hexToBytes(params.payerPrivateKey)
|
||||
const event = finalizeEvent(eventTemplate, secretKey)
|
||||
|
||||
// Get active relays
|
||||
const { relaySessionManager } = await import('./relaySessionManager')
|
||||
const activeRelays = await relaySessionManager.getActiveRelays()
|
||||
const { getPrimaryRelay } = await import('./config')
|
||||
const relays = activeRelays.length > 0 ? activeRelays : [await getPrimaryRelay()]
|
||||
|
||||
// Publish via writeOrchestrator (parallel network + local write)
|
||||
const result = await writeOrchestrator.writeAndPublish(
|
||||
{
|
||||
objectType: 'sponsoring',
|
||||
hash: hashId,
|
||||
event,
|
||||
parsed: parsedSponsoring,
|
||||
version: 0,
|
||||
hidden: false,
|
||||
index: 0,
|
||||
},
|
||||
relays
|
||||
)
|
||||
|
||||
if (!result.success) {
|
||||
return null
|
||||
}
|
||||
|
||||
return event
|
||||
}
|
||||
|
||||
@ -141,8 +141,9 @@ export class PlatformTrackingService {
|
||||
})
|
||||
|
||||
// Listen for EOSE via Service Worker messages
|
||||
const handleEOSE = (data: { relays: string[] }): void => {
|
||||
if (data.relays.includes(relayUrl) && !eoseReceived) {
|
||||
const handleEOSE = (data: unknown): void => {
|
||||
const eoseData = data as { relays: string[] }
|
||||
if (eoseData.relays.includes(relayUrl) && !eoseReceived) {
|
||||
eoseReceived = true
|
||||
finalize()
|
||||
}
|
||||
@ -214,8 +215,9 @@ export class PlatformTrackingService {
|
||||
})
|
||||
|
||||
// Listen for EOSE via Service Worker messages
|
||||
const handleEOSE = (data: { relays: string[] }): void => {
|
||||
if (data.relays.includes(relayUrl) && !eoseReceived) {
|
||||
const handleEOSE = (data: unknown): void => {
|
||||
const eoseData = data as { relays: string[] }
|
||||
if (eoseData.relays.includes(relayUrl) && !eoseReceived) {
|
||||
eoseReceived = true
|
||||
finalize()
|
||||
}
|
||||
|
||||
@ -1,44 +1,21 @@
|
||||
import { nostrService } from './nostr'
|
||||
import { PLATFORM_COMMISSIONS } from './platformCommissions'
|
||||
import type { Event } from 'nostr-tools'
|
||||
import { objectCache } from './objectCache'
|
||||
|
||||
export async function fetchOriginalReviewEvent(reviewId: string): Promise<Event | null> {
|
||||
const pool = nostrService.getPool()
|
||||
if (!pool) {
|
||||
throw new Error('Pool not initialized')
|
||||
// Read only from IndexedDB cache
|
||||
const parsed = await objectCache.getById('review', reviewId)
|
||||
if (parsed) {
|
||||
// Get the event from cache
|
||||
const event = await objectCache.getEventById('review', reviewId)
|
||||
if (event) {
|
||||
return event
|
||||
}
|
||||
}
|
||||
|
||||
const { getPrimaryRelaySync } = await import('./config')
|
||||
const { createSubscription } = await import('@/types/nostr-tools-extended')
|
||||
const relayUrl = getPrimaryRelaySync()
|
||||
const filters = [
|
||||
{
|
||||
kinds: [1],
|
||||
ids: [reviewId],
|
||||
limit: 1,
|
||||
},
|
||||
]
|
||||
|
||||
return new Promise<Event | null>((resolve) => {
|
||||
let resolved = false
|
||||
const sub = createSubscription(pool, [relayUrl], filters)
|
||||
|
||||
const finalize = (value: Event | null): void => {
|
||||
if (resolved) {
|
||||
return
|
||||
}
|
||||
resolved = true
|
||||
sub.unsub()
|
||||
resolve(value)
|
||||
}
|
||||
|
||||
sub.on('event', (event: Event) => {
|
||||
finalize(event)
|
||||
})
|
||||
|
||||
sub.on('eose', () => finalize(null))
|
||||
setTimeout(() => finalize(null), 5000)
|
||||
})
|
||||
// Not found in cache - return null (no network request)
|
||||
return null
|
||||
}
|
||||
|
||||
export function buildRewardEvent(originalEvent: Event, reviewId: string): {
|
||||
@ -72,7 +49,7 @@ export function checkIfAlreadyRewarded(originalEvent: Event, reviewId: string):
|
||||
}
|
||||
|
||||
export async function publishRewardEvent(
|
||||
updatedEvent: {
|
||||
updatedEventTemplate: {
|
||||
kind: number
|
||||
created_at: number
|
||||
tags: string[][]
|
||||
@ -80,16 +57,82 @@ export async function publishRewardEvent(
|
||||
},
|
||||
reviewId: string
|
||||
): Promise<void> {
|
||||
const publishedEvent = await nostrService.publishEvent(updatedEvent)
|
||||
if (publishedEvent) {
|
||||
console.warn('Review updated with reward tag', {
|
||||
reviewId,
|
||||
updatedEventId: publishedEvent.id,
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
} else {
|
||||
console.error('Failed to publish updated review event', {
|
||||
try {
|
||||
// Get original review to extract hash and parsed data
|
||||
const originalEvent = await fetchOriginalReviewEvent(reviewId)
|
||||
if (!originalEvent) {
|
||||
throw new Error('Original review event not found in cache')
|
||||
}
|
||||
|
||||
const { parseReviewFromEvent } = await import('./nostrEventParsing')
|
||||
const originalParsed = await parseReviewFromEvent(originalEvent)
|
||||
if (!originalParsed) {
|
||||
throw new Error('Failed to parse original review')
|
||||
}
|
||||
|
||||
// Increment version for update
|
||||
const newVersion = (originalParsed.version ?? 0) + 1
|
||||
const {hash} = originalParsed
|
||||
const index = originalParsed.index ?? 0
|
||||
|
||||
// Build updated parsed Review object
|
||||
const updatedParsed = {
|
||||
...originalParsed,
|
||||
version: newVersion,
|
||||
rewarded: true,
|
||||
rewardAmount: PLATFORM_COMMISSIONS.review.total,
|
||||
}
|
||||
|
||||
// Set private key in orchestrator
|
||||
const privateKey = nostrService.getPrivateKey()
|
||||
if (!privateKey) {
|
||||
throw new Error('Private key required for signing')
|
||||
}
|
||||
const { writeOrchestrator } = await import('./writeOrchestrator')
|
||||
writeOrchestrator.setPrivateKey(privateKey)
|
||||
|
||||
// Finalize event
|
||||
const { finalizeEvent } = await import('nostr-tools')
|
||||
const { hexToBytes } = await import('nostr-tools/utils')
|
||||
const secretKey = hexToBytes(privateKey)
|
||||
const event = finalizeEvent(updatedEventTemplate, secretKey)
|
||||
|
||||
// Get active relays
|
||||
const { relaySessionManager } = await import('./relaySessionManager')
|
||||
const activeRelays = await relaySessionManager.getActiveRelays()
|
||||
const { getPrimaryRelay } = await import('./config')
|
||||
const relays = activeRelays.length > 0 ? activeRelays : [await getPrimaryRelay()]
|
||||
|
||||
// Publish via writeOrchestrator (parallel network + local write)
|
||||
const result = await writeOrchestrator.writeAndPublish(
|
||||
{
|
||||
objectType: 'review',
|
||||
hash,
|
||||
event,
|
||||
parsed: updatedParsed,
|
||||
version: newVersion,
|
||||
hidden: false,
|
||||
index,
|
||||
},
|
||||
relays
|
||||
)
|
||||
|
||||
if (result.success) {
|
||||
console.warn('Review updated with reward tag', {
|
||||
reviewId,
|
||||
updatedEventId: event.id,
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
} else {
|
||||
console.error('Failed to publish updated review event', {
|
||||
reviewId,
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error publishing reward event', {
|
||||
reviewId,
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
import { SimplePool } from 'nostr-tools'
|
||||
import { swClient } from './swClient'
|
||||
import type { Event } from 'nostr-tools'
|
||||
import type { Event, Filter } from 'nostr-tools'
|
||||
|
||||
interface ConnectionState {
|
||||
relayUrl: string
|
||||
@ -220,8 +220,9 @@ class WebSocketService {
|
||||
this.updateConnectionState(relayUrl, true) // Assume connected when subscribing
|
||||
})
|
||||
|
||||
// Create subscription
|
||||
const sub = this.pool.subscribe(relays, filters, {
|
||||
// Create subscription - use first filter or empty filter
|
||||
const filter: Filter = (filters[0] as Filter) ?? {}
|
||||
const sub = this.pool.subscribe(relays, filter, {
|
||||
onevent: (event: Event): void => {
|
||||
// Notify Service Worker of new event via postMessage
|
||||
void swClient.sendMessage({
|
||||
@ -261,7 +262,7 @@ class WebSocketService {
|
||||
}
|
||||
|
||||
// Update all connection states
|
||||
this.connectionStates.forEach((state, relayUrl) => {
|
||||
this.connectionStates.forEach((_state, relayUrl) => {
|
||||
this.updateConnectionState(relayUrl, false)
|
||||
})
|
||||
|
||||
|
||||
@ -42,7 +42,7 @@ class WriteOrchestrator {
|
||||
params: WriteObjectParams,
|
||||
relays: string[]
|
||||
): Promise<{ success: boolean; eventId: string; published: false | string[] }> {
|
||||
const { objectType, hash, event, parsed, version, hidden, index, published = false } = params
|
||||
const { objectType, hash, event, parsed, version, hidden, index } = params
|
||||
|
||||
// Écriture en parallèle : réseau et local indépendamment
|
||||
const [networkResult, localResult] = await Promise.allSettled([
|
||||
@ -117,7 +117,7 @@ class WriteOrchestrator {
|
||||
parsed,
|
||||
version,
|
||||
hidden,
|
||||
index,
|
||||
...(index !== undefined ? { index } : {}),
|
||||
},
|
||||
relays
|
||||
)
|
||||
|
||||
@ -34,7 +34,7 @@ class WriteService {
|
||||
}
|
||||
|
||||
private createWorker(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
if (typeof window === 'undefined' || !window.Worker) {
|
||||
// Fallback: write directly if Worker not available
|
||||
console.warn('[WriteService] Web Workers not available, using direct writes')
|
||||
@ -240,8 +240,8 @@ class WriteService {
|
||||
}
|
||||
// Fallback: direct write
|
||||
const { notificationService } = await import('./notificationService')
|
||||
await notificationService.createNotificationDirect(
|
||||
type as Parameters<typeof notificationService.createNotificationDirect>[0],
|
||||
await notificationService.createNotification({
|
||||
type: type as Parameters<typeof notificationService.createNotification>[0]['type'],
|
||||
objectType,
|
||||
objectId,
|
||||
eventId,
|
||||
|
||||
@ -297,7 +297,7 @@ export default function AuthorPage(): React.ReactElement {
|
||||
const { presentation, series, totalSponsoring, loading, error, reload } = useAuthorData(hashIdOrPubkey ?? '')
|
||||
|
||||
if (!hashIdOrPubkey) {
|
||||
return null
|
||||
return <div />
|
||||
}
|
||||
|
||||
// Get the actual pubkey from presentation
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user