lint fix wip
This commit is contained in:
parent
cdd923e981
commit
b7d65a55c7
@ -456,6 +456,97 @@ Aucune bibliothèque ne doit être introduite si une capacité équivalente exis
|
||||
|
||||
Les noms, interfaces, types et contrats doivent être stables, explicites et orientés intention.
|
||||
|
||||
### Validation et précaution obligatoires
|
||||
|
||||
Avant toute implémentation ou modification, certaines décisions critiques doivent être validées explicitement par l'utilisateur.
|
||||
|
||||
#### Validation des choix de design
|
||||
|
||||
Tous les choix de design architecturaux, d'interface utilisateur ou d'expérience utilisateur doivent être validés avant implémentation.
|
||||
|
||||
Sont concernés :
|
||||
|
||||
* **Architecture** : Choix de patterns, structure de modules, organisation des couches
|
||||
* **Interface utilisateur** : Disposition des composants, flux de navigation, interactions utilisateur
|
||||
* **Expérience utilisateur** : Parcours utilisateur, workflows, processus métier
|
||||
* **Intégrations** : Choix d'API externes, protocoles de communication, formats d'échange
|
||||
* **Performance** : Stratégies d'optimisation, mécanismes de cache, techniques de chargement
|
||||
|
||||
Aucune implémentation ne doit être commencée sans validation explicite des choix de design proposés.
|
||||
|
||||
#### Validation des modifications du modèle de données
|
||||
|
||||
Toute modification affectant le modèle de données, la structure de la base de données ou les schémas de données doit être validée avant implémentation.
|
||||
|
||||
Sont concernés :
|
||||
|
||||
* **Schémas de base de données** : Ajout, modification ou suppression de tables, colonnes, index, contraintes
|
||||
* **Types de données** : Modifications de types, ajout de champs, changements de structure
|
||||
* **Relations** : Ajout ou modification de relations entre entités, clés étrangères
|
||||
* **Migrations** : Scripts de migration de base de données, transformations de données
|
||||
* **Contrats d'API** : Modifications des structures de données échangées via API
|
||||
* **Formats de stockage** : Changements de format de fichiers, structures JSON, schémas de validation
|
||||
|
||||
Toute modification du modèle de données doit être documentée avec :
|
||||
* L'impact sur les données existantes
|
||||
* Les migrations nécessaires
|
||||
* Les risques de régression
|
||||
* Les stratégies de rollback
|
||||
|
||||
Aucune modification du modèle de données ne doit être implémentée sans validation explicite.
|
||||
|
||||
#### Validation des commits et publications
|
||||
|
||||
Tous les commits et publications doivent être validés avant exécution.
|
||||
|
||||
**Commits :**
|
||||
|
||||
* Tous les commits doivent être présentés avec leur message structuré avant validation
|
||||
* Aucun commit ne doit être effectué sans validation explicite
|
||||
* Les commits doivent être atomiques et logiques
|
||||
* Les commits doivent respecter le format structuré défini dans les règles de commits
|
||||
|
||||
**Publications :**
|
||||
|
||||
* Toutes les publications (push, merge, release) doivent être validées avant exécution
|
||||
* Les publications vers les branches principales (main, master, production) nécessitent une validation explicite
|
||||
* Les déploiements doivent être validés avant exécution
|
||||
|
||||
Aucun commit ou publication ne doit être effectué automatiquement sans validation.
|
||||
|
||||
#### Validation des modifications des contrôles qualité et consignes
|
||||
|
||||
Toute modification des règles de qualité, des contrôles de linting, des configurations ESLint, des règles TypeScript ou des consignes à l'IA doit être validée avant application.
|
||||
|
||||
Sont concernés :
|
||||
|
||||
* **Règles de linting** : Ajout, modification ou suppression de règles ESLint
|
||||
* **Configuration TypeScript** : Modifications de `tsconfig.json`, règles de compilation
|
||||
* **Règles de qualité** : Modifications des fichiers de règles (`.cursor/rules/`, `quality.mdc`, etc.)
|
||||
* **Consignes à l'IA** : Modifications des instructions pour l'IA, changements de workflow
|
||||
* **Tests** : Modifications des stratégies de test, ajout ou suppression de frameworks de test
|
||||
* **CI/CD** : Modifications des pipelines, des scripts de déploiement, des vérifications automatiques
|
||||
|
||||
Ces modifications ont un impact systémique sur tout le projet et doivent être validées explicitement avant application.
|
||||
|
||||
#### Processus de validation
|
||||
|
||||
Le processus de validation doit suivre ces étapes :
|
||||
|
||||
1. **Proposition** : Présenter clairement les choix ou modifications proposés avec :
|
||||
* Le contexte et les motivations
|
||||
* Les alternatives considérées
|
||||
* Les impacts attendus
|
||||
* Les risques identifiés
|
||||
|
||||
2. **Attente de validation** : Attendre explicitement la validation de l'utilisateur avant de procéder
|
||||
|
||||
3. **Documentation** : Documenter les choix validés dans la documentation appropriée (`docs/`, `features/`, `fixKnowledge/`)
|
||||
|
||||
4. **Implémentation** : Procéder à l'implémentation uniquement après validation explicite
|
||||
|
||||
Aucune action ne doit être entreprise en supposant une validation implicite ou en anticipant une réponse.
|
||||
|
||||
### Documentation et contrats
|
||||
|
||||
Les fonctions publiques, classes et modules introduits doivent être auto-descriptifs par leur nommage et leur typage.
|
||||
|
||||
@ -3,39 +3,23 @@ import { syncProgressManager } from '@/lib/syncProgressManager'
|
||||
import type { SyncProgress } from '@/lib/userContentSync'
|
||||
import { t } from '@/lib/i18n'
|
||||
|
||||
export function GlobalSyncProgressBar(): React.ReactElement | null {
|
||||
const [progress, setProgress] = useState<SyncProgress | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = syncProgressManager.subscribe((newProgress) => {
|
||||
setProgress(newProgress)
|
||||
})
|
||||
|
||||
return () => {
|
||||
unsubscribe()
|
||||
}
|
||||
}, [])
|
||||
|
||||
if (!progress || progress.completed) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Extract relay name from URL (remove wss:// and truncate if too long)
|
||||
const getRelayDisplayName = (relayUrl?: string): string => {
|
||||
// Extract relay name from URL (remove wss:// and truncate if too long)
|
||||
function getRelayDisplayName(relayUrl?: string): string {
|
||||
if (!relayUrl) {
|
||||
return 'Connecting...'
|
||||
return ''
|
||||
}
|
||||
const cleaned = relayUrl.replace(/^wss?:\/\//, '').replace(/\/$/, '')
|
||||
if (cleaned.length > 50) {
|
||||
return `${cleaned.substring(0, 47)}...`
|
||||
}
|
||||
return cleaned
|
||||
}
|
||||
}
|
||||
|
||||
// Spinning sync icon
|
||||
const SyncIcon = (): React.ReactElement => (
|
||||
// Spinning sync icon
|
||||
function SyncIcon(): React.ReactElement {
|
||||
return (
|
||||
<svg
|
||||
className="animate-spin h-5 w-5 text-neon-cyan"
|
||||
className="animate-spin h-4 w-4 text-neon-cyan"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
@ -55,24 +39,49 @@ export function GlobalSyncProgressBar(): React.ReactElement | null {
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync status indicator component for display in header next to key icon
|
||||
* Shows sync icon + relay name when syncing
|
||||
*/
|
||||
export function SyncStatus(): React.ReactElement | null {
|
||||
const [progress, setProgress] = useState<SyncProgress | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = syncProgressManager.subscribe((newProgress) => {
|
||||
setProgress(newProgress)
|
||||
})
|
||||
|
||||
return () => {
|
||||
unsubscribe()
|
||||
}
|
||||
}, [])
|
||||
|
||||
if (!progress || progress.completed) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed top-0 left-0 right-0 z-50 bg-cyber-darker border-b border-neon-cyan/30 shadow-lg">
|
||||
<div className="container mx-auto px-4 py-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center gap-2 ml-2" title={progress.currentRelay ? getRelayDisplayName(progress.currentRelay) : t('settings.sync.connecting')}>
|
||||
<SyncIcon />
|
||||
{progress.currentRelay && (
|
||||
<span className="text-neon-cyan text-sm font-mono">
|
||||
{progress.currentRelay ? (
|
||||
<span className="text-neon-cyan text-xs font-mono max-w-[200px] truncate">
|
||||
{getRelayDisplayName(progress.currentRelay)}
|
||||
</span>
|
||||
)}
|
||||
{!progress.currentRelay && (
|
||||
<span className="text-cyber-accent/70 text-sm italic">
|
||||
) : (
|
||||
<span className="text-cyber-accent/70 text-xs italic">
|
||||
{t('settings.sync.connecting')}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Global sync progress bar (deprecated - kept for backward compatibility)
|
||||
* @deprecated Use SyncStatus component in KeyManagementManager instead
|
||||
*/
|
||||
export function GlobalSyncProgressBar(): React.ReactElement | null {
|
||||
return null
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ import { ConditionalPublishButton } from './ConditionalPublishButton'
|
||||
import { LanguageSelector } from './LanguageSelector'
|
||||
import { t } from '@/lib/i18n'
|
||||
import { KeyIndicator } from './KeyIndicator'
|
||||
import { SyncStatus } from './GlobalSyncProgressBar'
|
||||
|
||||
function GitIcon(): React.ReactElement {
|
||||
return (
|
||||
@ -89,6 +90,7 @@ export function PageHeader(): React.ReactElement {
|
||||
<GitIcon />
|
||||
</a>
|
||||
<KeyIndicator />
|
||||
<SyncStatus />
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<LanguageSelector />
|
||||
|
||||
@ -288,7 +288,8 @@ export async function fetchAuthorPresentationFromPool(
|
||||
}
|
||||
})
|
||||
|
||||
sub.on('eose', async (): Promise<void> => {
|
||||
sub.on('eose', (): void => {
|
||||
void (async (): Promise<void> => {
|
||||
// Get the latest version from all collected events
|
||||
const latestEvent = getLatestVersion(events)
|
||||
if (latestEvent) {
|
||||
@ -299,8 +300,10 @@ export async function fetchAuthorPresentationFromPool(
|
||||
}
|
||||
}
|
||||
await finalize(null)
|
||||
})()
|
||||
})
|
||||
setTimeout(async (): Promise<void> => {
|
||||
setTimeout((): void => {
|
||||
void (async (): Promise<void> => {
|
||||
// Get the latest version from all collected events
|
||||
const latestEvent = getLatestVersion(events)
|
||||
if (latestEvent) {
|
||||
@ -311,6 +314,7 @@ export async function fetchAuthorPresentationFromPool(
|
||||
}
|
||||
}
|
||||
await finalize(null)
|
||||
})()
|
||||
}, 5000).unref?.()
|
||||
})
|
||||
}
|
||||
|
||||
@ -89,7 +89,8 @@ export async function fetchAuthorByHashId(
|
||||
}
|
||||
})
|
||||
|
||||
sub.on('eose', async (): Promise<void> => {
|
||||
sub.on('eose', (): void => {
|
||||
void (async (): Promise<void> => {
|
||||
// Get the latest version from all collected events
|
||||
const latestEvent = getLatestVersion(events)
|
||||
if (latestEvent) {
|
||||
@ -100,8 +101,10 @@ export async function fetchAuthorByHashId(
|
||||
}
|
||||
}
|
||||
await finalize(null)
|
||||
})()
|
||||
})
|
||||
setTimeout(async (): Promise<void> => {
|
||||
setTimeout((): void => {
|
||||
void (async (): Promise<void> => {
|
||||
// Get the latest version from all collected events
|
||||
const timeoutLatestEvent = getLatestVersion(events)
|
||||
if (timeoutLatestEvent) {
|
||||
@ -112,6 +115,7 @@ export async function fetchAuthorByHashId(
|
||||
}
|
||||
}
|
||||
await finalize(null)
|
||||
})()
|
||||
}, 5000).unref?.()
|
||||
})
|
||||
}
|
||||
|
||||
@ -93,13 +93,17 @@ class PlatformSyncService {
|
||||
}
|
||||
})
|
||||
|
||||
sub.on('eose', async (): Promise<void> => {
|
||||
sub.on('eose', (): void => {
|
||||
void (async (): Promise<void> => {
|
||||
await finalize()
|
||||
})()
|
||||
})
|
||||
|
||||
// Timeout after SYNC_TIMEOUT_MS
|
||||
setTimeout(async (): Promise<void> => {
|
||||
setTimeout((): void => {
|
||||
void (async (): Promise<void> => {
|
||||
await finalize()
|
||||
})()
|
||||
}, this.SYNC_TIMEOUT_MS).unref?.()
|
||||
|
||||
this.syncSubscription = sub
|
||||
|
||||
@ -101,7 +101,9 @@ export async function getPurchaseById(purchaseId: string, timeoutMs: number = 50
|
||||
sub.on('eose', (): void => {
|
||||
void done(null)
|
||||
})
|
||||
setTimeout(() => done(null), timeoutMs).unref?.()
|
||||
setTimeout((): void => {
|
||||
void done(null)
|
||||
}, timeoutMs).unref?.()
|
||||
})
|
||||
}
|
||||
|
||||
@ -142,8 +144,12 @@ export function getPurchasesForArticle(articleId: string, timeoutMs: number = 50
|
||||
})()
|
||||
})
|
||||
|
||||
sub.on('eose', () => done())
|
||||
setTimeout(() => done(), timeoutMs).unref?.()
|
||||
sub.on('eose', (): void => {
|
||||
void done()
|
||||
})
|
||||
setTimeout((): void => {
|
||||
void done()
|
||||
}, timeoutMs).unref?.()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -108,7 +108,9 @@ export async function getReviewTipById(reviewTipId: string, timeoutMs: number =
|
||||
sub.on('eose', (): void => {
|
||||
void done(null)
|
||||
})
|
||||
setTimeout(() => done(null), timeoutMs).unref?.()
|
||||
setTimeout((): void => {
|
||||
void done(null)
|
||||
}, timeoutMs).unref?.()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -128,7 +128,8 @@ export async function getSeriesById(seriesId: string, timeoutMs: number = 5000):
|
||||
resolve(value)
|
||||
}
|
||||
|
||||
sub.on('event', async (event: Event): Promise<void> => {
|
||||
sub.on('event', (event: Event): void => {
|
||||
void (async (): Promise<void> => {
|
||||
const parsed = await parseSeriesFromEvent(event)
|
||||
if (parsed) {
|
||||
// Cache the parsed series
|
||||
@ -138,11 +139,14 @@ export async function getSeriesById(seriesId: string, timeoutMs: number = 5000):
|
||||
}
|
||||
await done(parsed)
|
||||
}
|
||||
})()
|
||||
})
|
||||
|
||||
sub.on('eose', (): void => {
|
||||
void done(null)
|
||||
})
|
||||
setTimeout(() => done(null), timeoutMs).unref?.()
|
||||
setTimeout((): void => {
|
||||
void done(null)
|
||||
}, timeoutMs).unref?.()
|
||||
})
|
||||
}
|
||||
|
||||
@ -108,7 +108,9 @@ export async function getSponsoringById(sponsoringId: string, timeoutMs: number
|
||||
sub.on('eose', (): void => {
|
||||
void done(null)
|
||||
})
|
||||
setTimeout(() => done(null), timeoutMs).unref?.()
|
||||
setTimeout((): void => {
|
||||
void done(null)
|
||||
}, timeoutMs).unref?.()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,6 @@ import { nostrAuthService } from '@/lib/nostrAuth'
|
||||
import { syncUserContentToCache } from '@/lib/userContentSync'
|
||||
import { getLastSyncDate, getCurrentTimestamp } from '@/lib/syncStorage'
|
||||
import { syncProgressManager } from '@/lib/syncProgressManager'
|
||||
import { GlobalSyncProgressBar } from '@/components/GlobalSyncProgressBar'
|
||||
import { relaySessionManager } from '@/lib/relaySessionManager'
|
||||
|
||||
function I18nProvider({ children }: { children: React.ReactNode }): React.ReactElement {
|
||||
@ -136,7 +135,6 @@ export default function App({ Component, pageProps }: AppProps): React.ReactElem
|
||||
|
||||
return (
|
||||
<I18nProvider>
|
||||
<GlobalSyncProgressBar />
|
||||
<Component {...pageProps} />
|
||||
</I18nProvider>
|
||||
)
|
||||
|
||||
@ -249,6 +249,7 @@ settings.sync.completed=Everything is synchronized
|
||||
settings.sync.ready=Ready to synchronize
|
||||
settings.sync.syncing=Synchronizing
|
||||
settings.sync.connecting=Connecting...
|
||||
settings.sync.status=Synchronizing
|
||||
settings.nip95.title=NIP-95 Upload Endpoints
|
||||
settings.nip95.loading=Loading...
|
||||
settings.nip95.error.loadFailed=Failed to load NIP-95 APIs
|
||||
|
||||
@ -249,6 +249,7 @@ settings.sync.completed=Tout est synchronisé
|
||||
settings.sync.ready=Prêt à synchroniser
|
||||
settings.sync.syncing=Synchronisation en cours
|
||||
settings.sync.connecting=Connexion...
|
||||
settings.sync.status=Synchronisation
|
||||
settings.language.title=Langue de préférence
|
||||
settings.language.description=Choisissez votre langue préférée pour l'interface
|
||||
settings.language.loading=Chargement...
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user