lint fix wip

This commit is contained in:
Nicolas Cantu 2026-01-07 02:06:09 +01:00
parent 964f4aeb60
commit 9f7a0e1527
10 changed files with 535 additions and 97 deletions

View File

@ -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(

View File

@ -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[][] {

View File

@ -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`

View File

@ -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
}

View File

@ -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()
}

View File

@ -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,11 +57,70 @@ export async function publishRewardEvent(
},
reviewId: string
): Promise<void> {
const publishedEvent = await nostrService.publishEvent(updatedEvent)
if (publishedEvent) {
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: publishedEvent.id,
updatedEventId: event.id,
timestamp: new Date().toISOString(),
})
} else {
@ -93,6 +129,13 @@ export async function publishRewardEvent(
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(),
})
}
}
export async function updateReviewWithReward(reviewId: string, authorPrivateKey: string): Promise<void> {

View File

@ -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)
})

View File

@ -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
)

View File

@ -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,

View File

@ -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