lint fix wip

This commit is contained in:
Nicolas Cantu 2026-01-06 21:44:43 +01:00
parent b7d65a55c7
commit e97d2b32cc
13 changed files with 99 additions and 49 deletions

View File

@ -5,7 +5,7 @@ export function AuthorMnemonicIcons({ value, getMnemonicIcons }: { value: string
return ( return (
<div className="flex items-center gap-1 flex-shrink-0"> <div className="flex items-center gap-1 flex-shrink-0">
{getMnemonicIcons(value).map((icon, idx) => ( {getMnemonicIcons(value).map((icon, idx) => (
<span key={idx} className="text-sm" title={`Mnemonic icon ${idx + 1}`}> <span key={`mnemonic-icon-${idx}-${icon}`} className="text-sm" title={`Mnemonic icon ${idx + 1}`}>
{icon} {icon}
</span> </span>
))} ))}

View File

@ -47,7 +47,7 @@ export function AuthorOption({
<span className="flex-1 truncate text-cyber-accent">{displayName}</span> <span className="flex-1 truncate text-cyber-accent">{displayName}</span>
<div className="flex items-center gap-1 flex-shrink-0"> <div className="flex items-center gap-1 flex-shrink-0">
{mnemonicIcons.map((icon, idx) => ( {mnemonicIcons.map((icon, idx) => (
<span key={idx} className="text-sm" title={`Mnemonic icon ${idx + 1}`}> <span key={`mnemonic-icon-${idx}-${icon}`} className="text-sm" title={`Mnemonic icon ${idx + 1}`}>
{icon} {icon}
</span> </span>
))} ))}

View File

@ -224,11 +224,15 @@ function PresentationForm({
disabled={loading ?? deleting} disabled={loading ?? deleting}
className="w-full px-4 py-2 bg-neon-cyan/20 hover:bg-neon-cyan/30 text-neon-cyan rounded-lg font-medium transition-all border border-neon-cyan/50 hover:shadow-glow-cyan disabled:opacity-50 disabled:cursor-not-allowed" className="w-full px-4 py-2 bg-neon-cyan/20 hover:bg-neon-cyan/30 text-neon-cyan rounded-lg font-medium transition-all border border-neon-cyan/50 hover:shadow-glow-cyan disabled:opacity-50 disabled:cursor-not-allowed"
> >
{loading ?? deleting {(() => {
? t('publish.publishing') if (loading ?? deleting) {
: hasExistingPresentation === true return t('publish.publishing')
? t('presentation.update.button') }
: t('publish.button')} if (hasExistingPresentation === true) {
return t('presentation.update.button')
}
return t('publish.button')
})()}
</button> </button>
</div> </div>
{hasExistingPresentation && ( {hasExistingPresentation && (
@ -279,7 +283,7 @@ function useAuthorPresentationState(pubkey: string | null, existingAuthorName?:
if (existingAuthorName && existingAuthorName !== draft.authorName && !existingPresentation) { if (existingAuthorName && existingAuthorName !== draft.authorName && !existingPresentation) {
setDraft((prev) => ({ ...prev, authorName: existingAuthorName })) setDraft((prev) => ({ ...prev, authorName: existingAuthorName }))
} }
}, [existingAuthorName, existingPresentation]) }, [existingAuthorName, existingPresentation, draft.authorName])
const handleSubmit = useCallback( const handleSubmit = useCallback(
async (e: FormEvent<HTMLFormElement>) => { async (e: FormEvent<HTMLFormElement>) => {

View File

@ -25,7 +25,7 @@ export function RecoveryPhraseDisplay({
<div className="grid grid-cols-2 gap-4 mb-4"> <div className="grid grid-cols-2 gap-4 mb-4">
{recoveryPhrase.map((word, index) => ( {recoveryPhrase.map((word, index) => (
<div <div
key={index} key={`recovery-word-${index}-${word}`}
className="bg-cyber-dark border border-neon-cyan/30 rounded-lg p-3 text-center font-mono text-lg" className="bg-cyber-dark border border-neon-cyan/30 rounded-lg p-3 text-center font-mono text-lg"
> >
<span className="text-cyber-accent/70 text-sm mr-2">{index + 1}.</span> <span className="text-cyber-accent/70 text-sm mr-2">{index + 1}.</span>

View File

@ -53,21 +53,35 @@ export function SyncStatus(): React.ReactElement | null {
setProgress(newProgress) setProgress(newProgress)
}) })
// Check current progress immediately
const currentProgress = syncProgressManager.getProgress()
if (currentProgress) {
setProgress(currentProgress)
}
return () => { return () => {
unsubscribe() unsubscribe()
} }
}, []) }, [])
if (!progress || progress.completed) { // Show indicator if we have progress and it's not completed
if (!progress) {
return null return null
} }
// Always show indicator during sync, even if completed is false (means sync is in progress)
if (progress.completed) {
return null
}
const relayName = progress.currentRelay ? getRelayDisplayName(progress.currentRelay) : null
return ( return (
<div className="flex items-center gap-2 ml-2" title={progress.currentRelay ? getRelayDisplayName(progress.currentRelay) : t('settings.sync.connecting')}> <div className="flex items-center gap-2 ml-2" title={relayName ?? t('settings.sync.connecting')}>
<SyncIcon /> <SyncIcon />
{progress.currentRelay ? ( {relayName ? (
<span className="text-neon-cyan text-xs font-mono max-w-[200px] truncate"> <span className="text-neon-cyan text-xs font-mono max-w-[200px] truncate">
{getRelayDisplayName(progress.currentRelay)} {relayName}
</span> </span>
) : ( ) : (
<span className="text-cyber-accent/70 text-xs italic"> <span className="text-cyber-accent/70 text-xs italic">

View File

@ -102,15 +102,19 @@ function HomeContent({
<ArticleFiltersComponent filters={filters} onFiltersChange={setFilters} articles={allArticles} /> <ArticleFiltersComponent filters={filters} onFiltersChange={setFilters} articles={allArticles} />
)} )}
{isInitialLoad ? ( {(() => {
if (isInitialLoad) {
return (
<div className="text-center py-12"> <div className="text-center py-12">
<p className="text-cyber-accent/70">{t('common.loading')}</p> <p className="text-cyber-accent/70">{t('common.loading')}</p>
</div> </div>
) : shouldShowAuthors ? ( )
<AuthorsList {...authorsListProps} /> }
) : ( if (shouldShowAuthors) {
<ArticlesList {...articlesListProps} /> return <AuthorsList {...authorsListProps} />
)} }
return <ArticlesList {...articlesListProps} />
})()}
</div> </div>
) )
} }

View File

@ -382,7 +382,7 @@ export function KeyManagementManager(): React.ReactElement {
<div className="grid grid-cols-2 gap-4 mb-4"> <div className="grid grid-cols-2 gap-4 mb-4">
{recoveryPhrase.map((word, index) => ( {recoveryPhrase.map((word, index) => (
<div <div
key={index} key={`recovery-word-${index}-${word}`}
className="bg-cyber-dark border border-neon-cyan/30 rounded-lg p-3 text-center font-mono text-lg" className="bg-cyber-dark border border-neon-cyan/30 rounded-lg p-3 text-center font-mono text-lg"
> >
<span className="text-cyber-accent/70 text-sm mr-2">{index + 1}.</span> <span className="text-cyber-accent/70 text-sm mr-2">{index + 1}.</span>

View File

@ -129,7 +129,7 @@ function WordInputs({
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
{words.map((word, index) => ( {words.map((word, index) => (
<WordInputWithAutocomplete <WordInputWithAutocomplete
key={index} key={`word-input-${index}-${word}`}
index={index} index={index}
value={word} value={word}
onChange={(value) => onWordChange(index, value)} onChange={(value) => onWordChange(index, value)}

View File

@ -193,6 +193,8 @@ export function getPurchasesByPayer(payerPubkey: string, timeoutMs: number = 500
sub.on('eose', (): void => { sub.on('eose', (): void => {
void done() void done()
}) })
setTimeout(() => done(), timeoutMs).unref?.() setTimeout((): void => {
void done()
}, timeoutMs).unref?.()
}) })
} }

View File

@ -154,7 +154,9 @@ export function getReviewTipsForArticle(articleId: string, timeoutMs: number = 5
sub.on('eose', (): void => { sub.on('eose', (): void => {
void done() void done()
}) })
setTimeout(() => done(), timeoutMs).unref?.() setTimeout((): void => {
void done()
}, timeoutMs).unref?.()
}) })
} }
@ -198,6 +200,8 @@ export function getReviewTipsForReview(reviewId: string, timeoutMs: number = 500
sub.on('eose', (): void => { sub.on('eose', (): void => {
void done() void done()
}) })
setTimeout(() => done(), timeoutMs).unref?.() setTimeout((): void => {
void done()
}, timeoutMs).unref?.()
}) })
} }

View File

@ -154,6 +154,8 @@ export function getSponsoringByAuthor(authorPubkey: string, timeoutMs: number =
sub.on('eose', (): void => { sub.on('eose', (): void => {
void done() void done()
}) })
setTimeout(() => done(), timeoutMs).unref?.() setTimeout((): void => {
void done()
}, timeoutMs).unref?.()
}) })
} }

View File

@ -13,7 +13,7 @@ import { getLatestVersion } from './versionManager'
import { buildTagFilter } from './nostrTagSystemFilter' import { buildTagFilter } from './nostrTagSystemFilter'
import { getPrimaryRelaySync } from './config' import { getPrimaryRelaySync } from './config'
import { tryWithRelayRotation } from './relayRotation' import { tryWithRelayRotation } from './relayRotation'
import { PLATFORM_SERVICE, MIN_EVENT_DATE } from './platformConfig' import { PLATFORM_SERVICE } from './platformConfig'
import { parseObjectId } from './urlGenerator' import { parseObjectId } from './urlGenerator'
import type { SimplePoolWithSub } from '@/types/nostr-tools-extended' import type { SimplePoolWithSub } from '@/types/nostr-tools-extended'
@ -24,6 +24,9 @@ async function fetchAndCachePublications(
pool: SimplePoolWithSub, pool: SimplePoolWithSub,
authorPubkey: string authorPubkey: string
): Promise<void> { ): Promise<void> {
const { getLastSyncDate } = await import('./syncStorage')
const lastSyncDate = await getLastSyncDate()
const filters = [ const filters = [
{ {
...buildTagFilter({ ...buildTagFilter({
@ -31,7 +34,7 @@ async function fetchAndCachePublications(
authorPubkey, authorPubkey,
service: PLATFORM_SERVICE, service: PLATFORM_SERVICE,
}), }),
since: MIN_EVENT_DATE, since: lastSyncDate,
limit: 1000, // Get all publications limit: 1000, // Get all publications
}, },
] ]
@ -148,6 +151,9 @@ async function fetchAndCacheSeries(
pool: SimplePoolWithSub, pool: SimplePoolWithSub,
authorPubkey: string authorPubkey: string
): Promise<void> { ): Promise<void> {
const { getLastSyncDate } = await import('./syncStorage')
const lastSyncDate = await getLastSyncDate()
// Fetch all events for series to cache them properly // Fetch all events for series to cache them properly
const filters = [ const filters = [
{ {
@ -156,7 +162,7 @@ async function fetchAndCacheSeries(
authorPubkey, authorPubkey,
service: PLATFORM_SERVICE, service: PLATFORM_SERVICE,
}), }),
since: MIN_EVENT_DATE, since: lastSyncDate,
limit: 1000, // Get all series events limit: 1000, // Get all series events
}, },
] ]
@ -278,12 +284,15 @@ async function fetchAndCachePurchases(
pool: SimplePoolWithSub, pool: SimplePoolWithSub,
payerPubkey: string payerPubkey: string
): Promise<void> { ): Promise<void> {
const { getLastSyncDate } = await import('./syncStorage')
const lastSyncDate = await getLastSyncDate()
const filters = [ const filters = [
{ {
kinds: [9735], // Zap receipt kinds: [9735], // Zap receipt
authors: [payerPubkey], authors: [payerPubkey],
'#kind_type': ['purchase'], '#kind_type': ['purchase'],
since: MIN_EVENT_DATE, since: lastSyncDate,
limit: 1000, limit: 1000,
}, },
] ]
@ -376,12 +385,15 @@ async function fetchAndCacheSponsoring(
pool: SimplePoolWithSub, pool: SimplePoolWithSub,
authorPubkey: string authorPubkey: string
): Promise<void> { ): Promise<void> {
const { getLastSyncDate } = await import('./syncStorage')
const lastSyncDate = await getLastSyncDate()
const filters = [ const filters = [
{ {
kinds: [9735], // Zap receipt kinds: [9735], // Zap receipt
'#p': [authorPubkey], '#p': [authorPubkey],
'#kind_type': ['sponsoring'], '#kind_type': ['sponsoring'],
since: MIN_EVENT_DATE, since: lastSyncDate,
limit: 1000, limit: 1000,
}, },
] ]
@ -474,12 +486,15 @@ async function fetchAndCacheReviewTips(
pool: SimplePoolWithSub, pool: SimplePoolWithSub,
authorPubkey: string authorPubkey: string
): Promise<void> { ): Promise<void> {
const { getLastSyncDate } = await import('./syncStorage')
const lastSyncDate = await getLastSyncDate()
const filters = [ const filters = [
{ {
kinds: [9735], // Zap receipt kinds: [9735], // Zap receipt
'#p': [authorPubkey], '#p': [authorPubkey],
'#kind_type': ['review_tip'], '#kind_type': ['review_tip'],
since: MIN_EVENT_DATE, since: lastSyncDate,
limit: 1000, limit: 1000,
}, },
] ]
@ -579,6 +594,9 @@ async function fetchAndCachePaymentNotes(
pool: SimplePoolWithSub, pool: SimplePoolWithSub,
userPubkey: string userPubkey: string
): Promise<void> { ): Promise<void> {
const { getLastSyncDate } = await import('./syncStorage')
const lastSyncDate = await getLastSyncDate()
// Payment notes are kind 1 with type='payment' // Payment notes are kind 1 with type='payment'
// They can be: as payer (authors) or as recipient (#recipient tag) // They can be: as payer (authors) or as recipient (#recipient tag)
const filters = [ const filters = [
@ -587,7 +605,7 @@ async function fetchAndCachePaymentNotes(
authors: [userPubkey], authors: [userPubkey],
'#payment': [''], '#payment': [''],
'#service': [PLATFORM_SERVICE], '#service': [PLATFORM_SERVICE],
since: MIN_EVENT_DATE, since: lastSyncDate,
limit: 1000, limit: 1000,
}, },
{ {
@ -595,7 +613,7 @@ async function fetchAndCachePaymentNotes(
'#recipient': [userPubkey], '#recipient': [userPubkey],
'#payment': [''], '#payment': [''],
'#service': [PLATFORM_SERVICE], '#service': [PLATFORM_SERVICE],
since: MIN_EVENT_DATE, since: lastSyncDate,
limit: 1000, limit: 1000,
}, },
] ]

View File

@ -5,7 +5,6 @@ import React from 'react'
import { platformSyncService } from '@/lib/platformSync' import { platformSyncService } from '@/lib/platformSync'
import { nostrAuthService } from '@/lib/nostrAuth' import { nostrAuthService } from '@/lib/nostrAuth'
import { syncUserContentToCache } from '@/lib/userContentSync' import { syncUserContentToCache } from '@/lib/userContentSync'
import { getLastSyncDate, getCurrentTimestamp } from '@/lib/syncStorage'
import { syncProgressManager } from '@/lib/syncProgressManager' import { syncProgressManager } from '@/lib/syncProgressManager'
import { relaySessionManager } from '@/lib/relaySessionManager' import { relaySessionManager } from '@/lib/relaySessionManager'
@ -74,27 +73,20 @@ export default function App({ Component, pageProps }: AppProps): React.ReactElem
} }
}, []) }, [])
// Start user content sync on app mount if connected // Start user content sync on app mount and on each page navigation if connected
React.useEffect(() => { React.useEffect(() => {
let syncInProgress = false let syncInProgress = false
const startUserSync = async (): Promise<void> => { const startUserSync = async (): Promise<void> => {
if (syncInProgress) { if (syncInProgress) {
console.log('[App] Sync already in progress, skipping')
return return
} }
const state = nostrAuthService.getState() const state = nostrAuthService.getState()
console.log('[App] Checking connection state:', { connected: state.connected, hasPubkey: Boolean(state.pubkey) })
if (!state.connected || !state.pubkey) { if (!state.connected || !state.pubkey) {
return console.log('[App] Not connected or no pubkey, skipping sync')
}
// Check if already recently synced (within last hour)
const storedLastSyncDate = await getLastSyncDate()
const currentTimestamp = getCurrentTimestamp()
const isRecentlySynced = storedLastSyncDate >= currentTimestamp - 3600
if (isRecentlySynced) {
console.log('[App] User content already synced recently, skipping')
return return
} }
@ -121,7 +113,16 @@ export default function App({ Component, pageProps }: AppProps): React.ReactElem
// Try to start sync immediately // Try to start sync immediately
void startUserSync() void startUserSync()
// Also listen to connection changes to sync when user connects // Also listen to connection changes and route changes to sync when user connects or navigates
const router = require('next/router').default
const handleRouteChange = (): void => {
if (!syncInProgress) {
void startUserSync()
}
}
router.events?.on('routeChangeComplete', handleRouteChange)
const unsubscribe = nostrAuthService.subscribe((state) => { const unsubscribe = nostrAuthService.subscribe((state) => {
if (state.connected && state.pubkey && !syncInProgress) { if (state.connected && state.pubkey && !syncInProgress) {
void startUserSync() void startUserSync()
@ -129,6 +130,7 @@ export default function App({ Component, pageProps }: AppProps): React.ReactElem
}) })
return () => { return () => {
router.events?.off('routeChangeComplete', handleRouteChange)
unsubscribe() unsubscribe()
} }
}, []) }, [])