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 (
<div className="flex items-center gap-1 flex-shrink-0">
{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}
</span>
))}

View File

@ -47,7 +47,7 @@ export function AuthorOption({
<span className="flex-1 truncate text-cyber-accent">{displayName}</span>
<div className="flex items-center gap-1 flex-shrink-0">
{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}
</span>
))}

View File

@ -224,11 +224,15 @@ function PresentationForm({
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"
>
{loading ?? deleting
? t('publish.publishing')
: hasExistingPresentation === true
? t('presentation.update.button')
: t('publish.button')}
{(() => {
if (loading ?? deleting) {
return t('publish.publishing')
}
if (hasExistingPresentation === true) {
return t('presentation.update.button')
}
return t('publish.button')
})()}
</button>
</div>
{hasExistingPresentation && (
@ -279,7 +283,7 @@ function useAuthorPresentationState(pubkey: string | null, existingAuthorName?:
if (existingAuthorName && existingAuthorName !== draft.authorName && !existingPresentation) {
setDraft((prev) => ({ ...prev, authorName: existingAuthorName }))
}
}, [existingAuthorName, existingPresentation])
}, [existingAuthorName, existingPresentation, draft.authorName])
const handleSubmit = useCallback(
async (e: FormEvent<HTMLFormElement>) => {

View File

@ -25,7 +25,7 @@ export function RecoveryPhraseDisplay({
<div className="grid grid-cols-2 gap-4 mb-4">
{recoveryPhrase.map((word, index) => (
<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"
>
<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)
})
// Check current progress immediately
const currentProgress = syncProgressManager.getProgress()
if (currentProgress) {
setProgress(currentProgress)
}
return () => {
unsubscribe()
}
}, [])
if (!progress || progress.completed) {
// Show indicator if we have progress and it's not completed
if (!progress) {
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 (
<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 />
{progress.currentRelay ? (
{relayName ? (
<span className="text-neon-cyan text-xs font-mono max-w-[200px] truncate">
{getRelayDisplayName(progress.currentRelay)}
{relayName}
</span>
) : (
<span className="text-cyber-accent/70 text-xs italic">

View File

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

View File

@ -382,7 +382,7 @@ export function KeyManagementManager(): React.ReactElement {
<div className="grid grid-cols-2 gap-4 mb-4">
{recoveryPhrase.map((word, index) => (
<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"
>
<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">
{words.map((word, index) => (
<WordInputWithAutocomplete
key={index}
key={`word-input-${index}-${word}`}
index={index}
value={word}
onChange={(value) => onWordChange(index, value)}

View File

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

View File

@ -5,7 +5,6 @@ import React from 'react'
import { platformSyncService } from '@/lib/platformSync'
import { nostrAuthService } from '@/lib/nostrAuth'
import { syncUserContentToCache } from '@/lib/userContentSync'
import { getLastSyncDate, getCurrentTimestamp } from '@/lib/syncStorage'
import { syncProgressManager } from '@/lib/syncProgressManager'
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(() => {
let syncInProgress = false
const startUserSync = async (): Promise<void> => {
if (syncInProgress) {
console.log('[App] Sync already in progress, skipping')
return
}
const state = nostrAuthService.getState()
console.log('[App] Checking connection state:', { connected: state.connected, hasPubkey: Boolean(state.pubkey) })
if (!state.connected || !state.pubkey) {
return
}
// 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')
console.log('[App] Not connected or no pubkey, skipping sync')
return
}
@ -121,7 +113,16 @@ export default function App({ Component, pageProps }: AppProps): React.ReactElem
// Try to start sync immediately
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) => {
if (state.connected && state.pubkey && !syncInProgress) {
void startUserSync()
@ -129,6 +130,7 @@ export default function App({ Component, pageProps }: AppProps): React.ReactElem
})
return () => {
router.events?.off('routeChangeComplete', handleRouteChange)
unsubscribe()
}
}, [])