lint fix wip

This commit is contained in:
Nicolas Cantu 2026-01-06 16:35:15 +01:00
parent ccf2fdf759
commit cc0f3816fa
23 changed files with 103 additions and 222 deletions

View File

@ -1,143 +0,0 @@
# Résumé du déploiement - zapwall.fr
## ✅ Actions effectuées
1. **Déploiement initial** : Application déployée sur `/var/www/zapwall.fr`
2. **Service systemd** : Service `zapwall.service` créé et actif
3. **Configuration nginx** : Reverse proxy configuré dans le conteneur Docker
4. **HTTPS** : Configuration HTTPS avec redirection automatique HTTP → HTTPS
5. **Firewall** : Ports 80 et 443 ouverts
## 📍 État actuel
- **Service** : ✅ Actif sur le port 3001
- **Répertoire** : `/var/www/zapwall.fr` (correct)
- **HTTPS** : ✅ Configuré avec certificats auto-signés
- **Certificats Let's Encrypt** : ⚠️ Non obtenus (bug certbot avec Python 3.11)
## 🔧 Problèmes identifiés et solutions
### 1. Certificats Let's Encrypt
**Problème** : Certbot présente un bug (AttributeError) avec Python 3.11
**Solutions** :
- Utiliser certbot via snap (recommandé) :
```bash
ssh debian@92.243.27.35
sudo snap install certbot --classic
sudo docker stop lecoffre_nginx_test
sudo certbot certonly --standalone -d zapwall.fr --non-interactive --agree-tos --email admin@zapwall.fr
sudo docker start lecoffre_nginx_test
```
### 2. Configuration nginx
**Vérification** : La configuration pointe bien vers le port 3001 (correct)
- `proxy_pass http://172.17.0.1:3001;`
**Note** : La configuration principale nginx.conf contient aussi une config pour `test-lecoffreio.4nkweb.com`, mais elle ne devrait pas interférer car elle utilise un `server_name` différent.
## 📝 Mise à jour du site depuis Git
### Méthode recommandée : Script automatique
Depuis votre machine locale, dans le répertoire du projet :
```bash
# Mise à jour depuis la branche actuelle
./update-from-git.sh
# Ou spécifier une branche
./update-from-git.sh main
```
Le script :
1. Transfère les fichiers depuis votre dépôt Git local
2. Installe les dépendances (`npm ci`)
3. Construit l'application (`npm run build`)
4. Redémarre le service
5. Vérifie que tout fonctionne
### Méthode manuelle
Si vous préférez faire manuellement :
```bash
# 1. Transférer les fichiers
tar --exclude='node_modules' \
--exclude='.next' \
--exclude='.git' \
--exclude='*.tsbuildinfo' \
--exclude='.env*.local' \
--exclude='.cursor' \
-czf - . | ssh debian@92.243.27.35 "cd /var/www/zapwall.fr && tar -xzf -"
# 2. Sur le serveur
ssh debian@92.243.27.35
cd /var/www/zapwall.fr
npm ci
npm run build
sudo systemctl restart zapwall
```
### Initialiser un dépôt Git sur le serveur (optionnel)
Si vous voulez pouvoir faire `git pull` directement sur le serveur :
```bash
ssh debian@92.243.35
cd /var/www/zapwall.fr
git init
git remote add origin https://git.4nkweb.com/4nk/story-research-zapwall.git
git fetch origin
git checkout main # ou la branche souhaitée
```
Ensuite, vous pourrez utiliser :
```bash
ssh debian@92.243.27.35 'cd /var/www/zapwall.fr && git pull && npm ci && npm run build && sudo systemctl restart zapwall'
```
## 🔍 Commandes de vérification
### Vérifier le service
```bash
ssh debian@92.243.27.35 'sudo systemctl status zapwall'
```
### Voir les logs
```bash
ssh debian@92.243.27.35 'sudo journalctl -u zapwall -f'
```
### Vérifier le port
```bash
ssh debian@92.243.27.35 'sudo ss -tuln | grep 3001'
```
### Vérifier la configuration nginx
```bash
ssh debian@92.243.27.35 'sudo docker exec lecoffre_nginx_test cat /etc/nginx/conf.d/zapwall.fr.conf'
```
## 📚 Fichiers de documentation créés
- `README-DEPLOYMENT.md` : Guide complet de déploiement et mise à jour
- `update-from-git.sh` : Script de mise à jour automatique
- `fix-nginx-config.sh` : Script de correction de la configuration
- `check-deployment-status.sh` : Script de vérification de l'état
## 🚀 Prochaines étapes recommandées
1. **Obtenir les certificats Let's Encrypt** via snap (voir ci-dessus)
2. **Configurer le renouvellement automatique** des certificats
3. **Tester l'accès** au site en HTTPS
4. **Configurer un dépôt Git sur le serveur** pour faciliter les mises à jour
## ⚠️ Notes importantes
- Le site fonctionne actuellement avec des certificats auto-signés (avertissement navigateur)
- Les modifications de code nécessitent un rebuild et un redémarrage du service
- Le service doit être actif pour que le site soit accessible
- Nginx fait un reverse proxy vers le port 3001 où tourne l'application Next.js

View File

@ -109,7 +109,7 @@ export function ArticleFiltersComponent({
}: ArticleFiltersProps): React.ReactElement {
const data = useFiltersData(articles)
const handleClearFilters = () => {
const handleClearFilters = (): void => {
onFiltersChange({
authorPubkey: null,
sortBy: 'newest',

View File

@ -64,7 +64,7 @@ export function AllAuthorsOption({
value: string | null
onChange: (value: string | null) => void
setIsOpen: (open: boolean) => void
}) {
}): React.ReactElement {
return (
<button
type="button"

View File

@ -23,7 +23,7 @@ interface AuthorPresentationDraft {
const ADDRESS_PATTERN = /^(1|3|bc1)[a-zA-Z0-9]{25,62}$/
function SuccessNotice({ pubkey }: { pubkey: string | null }) {
function SuccessNotice({ pubkey }: { pubkey: string | null }): React.ReactElement {
return (
<div className="border border-neon-green/50 rounded-lg p-6 bg-neon-green/10">
<h3 className="text-lg font-semibold text-neon-green mb-2">{t('presentation.success')}</h3>
@ -44,7 +44,7 @@ function SuccessNotice({ pubkey }: { pubkey: string | null }) {
)
}
function ValidationError({ message }: { message: string | null }) {
function ValidationError({ message }: { message: string | null }): React.ReactElement | null {
if (!message) {
return null
}
@ -61,7 +61,7 @@ function PresentationField({
}: {
draft: AuthorPresentationDraft
onChange: (next: AuthorPresentationDraft) => void
}) {
}): React.ReactElement {
return (
<ArticleField
id="presentation"
@ -83,7 +83,7 @@ function ContentDescriptionField({
}: {
draft: AuthorPresentationDraft
onChange: (next: AuthorPresentationDraft) => void
}) {
}): React.ReactElement {
return (
<ArticleField
id="contentDescription"
@ -105,7 +105,7 @@ function MainnetAddressField({
}: {
draft: AuthorPresentationDraft
onChange: (next: AuthorPresentationDraft) => void
}) {
}): React.ReactElement {
return (
<ArticleField
id="mainnetAddress"
@ -126,7 +126,7 @@ function AuthorNameField({
}: {
draft: AuthorPresentationDraft
onChange: (next: AuthorPresentationDraft) => void
}) {
}): React.ReactElement {
return (
<ArticleField
id="authorName"
@ -147,7 +147,7 @@ function PictureField({
}: {
draft: AuthorPresentationDraft
onChange: (next: AuthorPresentationDraft) => void
}) {
}): React.ReactElement {
return (
<ImageUploadField
id="picture"
@ -163,7 +163,7 @@ const PresentationFields = ({
}: {
draft: AuthorPresentationDraft
onChange: (next: AuthorPresentationDraft) => void
}) => (
}): React.ReactElement => (
<div className="space-y-4">
<AuthorNameField draft={draft} onChange={onChange} />
<PictureField draft={draft} onChange={onChange} />
@ -173,7 +173,7 @@ const PresentationFields = ({
</div>
)
function DeleteButton({ onDelete, deleting }: { onDelete: () => void; deleting: boolean }) {
function DeleteButton({ onDelete, deleting }: { onDelete: () => void; deleting: boolean }): React.ReactElement {
return (
<button
type="button"
@ -206,7 +206,7 @@ function PresentationForm({
deleting: boolean
handleDelete: () => void
hasExistingPresentation: boolean
}) {
}): React.ReactElement {
return (
<form
onSubmit={(e: FormEvent<HTMLFormElement>) => {
@ -239,7 +239,17 @@ function PresentationForm({
)
}
function useAuthorPresentationState(pubkey: string | null, existingAuthorName?: string, existingPresentation?: Article | null) {
function useAuthorPresentationState(pubkey: string | null, existingAuthorName?: string, existingPresentation?: Article | null): {
draft: AuthorPresentationDraft
setDraft: (next: AuthorPresentationDraft) => void
validationError: string | null
error: string | null
loading: boolean
handleSubmit: (e: FormEvent<HTMLFormElement>) => Promise<void>
deleting: boolean
handleDelete: () => Promise<void>
success: boolean
} {
const { loading, error, success, publishPresentation, deletePresentation } = useAuthorPresentation(pubkey)
const router = useRouter()
const [draft, setDraft] = useState<AuthorPresentationDraft>(() => {
@ -310,7 +320,7 @@ function useAuthorPresentationState(pubkey: string | null, existingAuthorName?:
}
}, [existingPresentation, deletePresentation, router])
return { loading, error, success, draft, setDraft, validationError, handleSubmit, deleting, handleDelete, existingPresentation }
return { loading, error, success, draft, setDraft, validationError, handleSubmit, deleting, handleDelete }
}
function NoAccountActionButtons({
@ -319,7 +329,7 @@ function NoAccountActionButtons({
}: {
onGenerate: () => void
onImport: () => void
}) {
}): React.ReactElement {
return (
<div className="flex flex-col gap-3 w-full max-w-xs">
<button
@ -338,7 +348,7 @@ function NoAccountActionButtons({
)
}
function NoAccountView() {
function NoAccountView(): React.ReactElement {
const [showImportModal, setShowImportModal] = useState(false)
const [showRecoveryStep, setShowRecoveryStep] = useState(false)
const [showUnlockModal, setShowUnlockModal] = useState(false)
@ -347,7 +357,7 @@ function NoAccountView() {
const [generating, setGenerating] = useState(false)
const [error, setError] = useState<string | null>(null)
const handleGenerate = async () => {
const handleGenerate = async (): Promise<void> => {
setGenerating(true)
setError(null)
try {
@ -363,12 +373,12 @@ function NoAccountView() {
}
}
const handleRecoveryContinue = () => {
const handleRecoveryContinue = (): void => {
setShowRecoveryStep(false)
setShowUnlockModal(true)
}
const handleUnlockSuccess = () => {
const handleUnlockSuccess = (): void => {
setShowUnlockModal(false)
setRecoveryPhrase([])
setNpub('')
@ -422,13 +432,13 @@ function AuthorPresentationFormView({
}: {
pubkey: string | null
profile: { name?: string; pubkey: string } | null
}) {
}): React.ReactElement {
const { checkPresentationExists } = useAuthorPresentation(pubkey)
const [existingPresentation, setExistingPresentation] = useState<Article | null>(null)
const [loadingPresentation, setLoadingPresentation] = useState(true)
useEffect(() => {
const load = async () => {
const load = async (): Promise<void> => {
if (!pubkey) {
setLoadingPresentation(false)
return
@ -478,7 +488,7 @@ function AuthorPresentationFormView({
)
}
function useAutoLoadPubkey(accountExists: boolean | null, pubkey: string | null, connect: () => Promise<void>) {
function useAutoLoadPubkey(accountExists: boolean | null, pubkey: string | null, connect: () => Promise<void>): void {
useEffect(() => {
if (accountExists === true && !pubkey) {
void connect()
@ -486,7 +496,7 @@ function useAutoLoadPubkey(accountExists: boolean | null, pubkey: string | null,
}, [accountExists, pubkey, connect])
}
export function AuthorPresentationEditor() {
export function AuthorPresentationEditor(): React.ReactElement {
const { pubkey, profile, accountExists, connect } = useNostrAuth()
useAutoLoadPubkey(accountExists, pubkey ?? null, connect)
return <AuthorPresentationFormView pubkey={pubkey ?? null} profile={profile} />

View File

@ -1,6 +1,6 @@
import { t } from '@/lib/i18n'
export function RecoveryWarning() {
export function RecoveryWarning(): React.ReactElement {
return (
<div className="bg-yellow-900/20 border border-yellow-400/50 rounded-lg p-4 mb-6">
<p className="text-yellow-400 font-semibold mb-2">{t('account.create.recovery.warning.title')}</p>

View File

@ -27,7 +27,7 @@ interface HomeViewProps {
unlockedArticles: Set<string>
}
function HomeHead() {
function HomeHead(): React.ReactElement {
return (
<Head>
<title>zapwall.fr</title>

View File

@ -39,6 +39,10 @@ export function SyncProgressBar(): React.ReactElement | null {
}
}
useEffect(() => {
void loadSyncStatus()
}, [])
async function startSync(): Promise<void> {
try {
const state = nostrAuthService.getState()
@ -47,7 +51,7 @@ export function SyncProgressBar(): React.ReactElement | null {
}
setIsSyncing(true)
setSyncProgress({ currentDay: 0, totalDays, completed: false })
setSyncProgress({ currentStep: 0, totalSteps: 6, completed: false })
await syncUserContentToCache(state.pubkey, (progress) => {
setSyncProgress(progress)
@ -78,8 +82,8 @@ export function SyncProgressBar(): React.ReactElement | null {
return null
}
const progressPercentage = syncProgress && totalDays > 0
? Math.min(100, (syncProgress.currentDay / totalDays) * 100)
const progressPercentage = syncProgress && syncProgress.totalSteps > 0
? Math.min(100, (syncProgress.currentStep / syncProgress.totalSteps) * 100)
: 0
const formatDate = (timestamp: number): string => {
@ -133,8 +137,8 @@ export function SyncProgressBar(): React.ReactElement | null {
<div className="flex items-center justify-between text-sm">
<span className="text-cyber-accent">
{t('settings.sync.progress', {
current: syncProgress.currentDay,
total: syncProgress.totalDays,
current: syncProgress.currentStep,
total: syncProgress.totalSteps,
})}
</span>
<span className="text-neon-cyan font-semibold">

View File

@ -160,7 +160,7 @@ export class AlbyService {
const startTime = Date.now()
return new Promise((resolve) => {
const checkPayment = () => {
const checkPayment = (): void => {
try {
const status = this.checkPaymentStatus(paymentHash)

View File

@ -16,7 +16,12 @@ export function prepareKeyData(storedContent: { content: string; decryptionKey?:
return storedContent.content // Fallback to old behavior if keys are not available
}
export function buildPrivateMessageEvent(recipientPubkey: string, articleId: string, encryptedKey: string) {
export function buildPrivateMessageEvent(recipientPubkey: string, articleId: string, encryptedKey: string): {
kind: number
created_at: number
tags: string[][]
content: string
} {
return {
kind: 4,
created_at: Math.floor(Date.now() / 1000),

View File

@ -22,7 +22,7 @@ export class AutomaticTransferService {
* Transfer author portion after article payment
* Creates a Lightning invoice from the platform to the author
*/
private logTransferRequired(type: 'article' | 'review', id: string, pubkey: string, amount: number, recipient: string, platformCommission: number) {
private logTransferRequired(type: 'article' | 'review', id: string, pubkey: string, amount: number, recipient: string, platformCommission: number): void {
const logData = {
[type === 'article' ? 'articleId' : 'reviewId']: id,
[type === 'article' ? 'articlePubkey' : 'reviewerPubkey']: pubkey,

View File

@ -15,7 +15,14 @@ export interface ContentDeliveryStatus {
* Verify that private content was successfully delivered to recipient
* Checks multiple aspects to ensure delivery certainty
*/
function createContentDeliveryFilters(authorPubkey: string, recipientPubkey: string, articleId: string, messageEventId: string) {
function createContentDeliveryFilters(authorPubkey: string, recipientPubkey: string, articleId: string, messageEventId: string): Array<{
kinds: number[]
ids?: string[]
authors: string[]
'#p': string[]
'#e': string[]
limit: number
}> {
const filters: Array<{
kinds: number[]
ids?: string[]
@ -76,7 +83,7 @@ function createContentDeliverySubscription(
recipientPubkey: string,
articleId: string,
messageEventId: string
) {
): import('@/types/nostr-tools-extended').Subscription {
const filters = createContentDeliveryFilters(authorPubkey, recipientPubkey, articleId, messageEventId)
const relayUrl = getPrimaryRelaySync()
return createSubscription(pool, [relayUrl], filters)
@ -89,7 +96,7 @@ function createContentDeliveryPromise(
return new Promise((resolve) => {
let resolved = false
const finalize = (result: ContentDeliveryStatus) => {
const finalize = (result: ContentDeliveryStatus): void => {
if (resolved) {
return
}

View File

@ -3,7 +3,7 @@ import type { TransactionVerificationResult } from './mempoolSpaceTypes'
import { getTransaction } from './mempoolSpaceApi'
import { verifySponsoringTransaction } from './mempoolSpaceVerification'
export function scheduleNextCheck(checkConfirmation: () => void, interval: number) {
export function scheduleNextCheck(checkConfirmation: () => void, interval: number): void {
setTimeout(() => {
void checkConfirmation()
}, interval)
@ -53,7 +53,7 @@ export async function waitForConfirmation(
const startTime = Date.now()
return new Promise((resolve) => {
const checkConfirmation = async () => {
const checkConfirmation = async (): Promise<void> => {
await checkTransactionStatus(txid, startTime, timeout, interval, resolve, checkConfirmation)
}
void checkConfirmation()

View File

@ -111,8 +111,8 @@ export function extractTagsFromEvent(event: { tags: string[][] }): {
json?: string | undefined
[key: string]: unknown
} {
const findTag = (key: string) => event.tags.find((tag) => tag[0] === key)?.[1]
const hasTag = (key: string) => event.tags.some((tag) => tag[0] === key || (tag.length === 1 && tag[0] === key))
const findTag = (key: string): string | undefined => event.tags.find((tag) => tag[0] === key)?.[1]
const hasTag = (key: string): boolean => event.tags.some((tag) => tag[0] === key || (tag.length === 1 && tag[0] === key))
const typeCategory = extractTypeAndCategory(event)
const commonTags = extractCommonTags(findTag, hasTag)

View File

@ -390,8 +390,8 @@ async function fetchAndCacheReviewTips(
}
export interface SyncProgress {
currentDay: number
totalDays: number
currentStep: number
totalSteps: number
completed: boolean
}
@ -399,7 +399,7 @@ export interface SyncProgress {
* Synchronize all user content to IndexedDB cache
* Fetches profile, series, publications, purchases, sponsoring, and review tips and caches them
* @param userPubkey - The user's public key
* @param onProgress - Optional callback to report progress (currentDay, totalDays, completed)
* @param onProgress - Optional callback to report progress (currentStep, totalSteps, completed)
*/
export async function syncUserContentToCache(
userPubkey: string,
@ -414,59 +414,59 @@ export async function syncUserContentToCache(
const poolWithSub = pool as unknown as SimplePoolWithSub
// Get last sync date and calculate days
const { getLastSyncDate, setLastSyncDate, getCurrentTimestamp, calculateDaysBetween } = await import('./syncStorage')
const lastSyncDate = await getLastSyncDate()
// Get current timestamp for last sync date
const { setLastSyncDate, getCurrentTimestamp } = await import('./syncStorage')
const currentTimestamp = getCurrentTimestamp()
const totalDays = calculateDaysBetween(lastSyncDate, currentTimestamp)
const TOTAL_STEPS = 6
// Report initial progress
if (onProgress) {
onProgress({ currentDay: 0, totalDays, completed: false })
onProgress({ currentStep: 0, totalSteps: TOTAL_STEPS, completed: false })
}
let currentDay = 0
let currentStep = 0
// Fetch and cache author profile (already caches itself)
await fetchAuthorPresentationFromPool(poolWithSub, userPubkey)
currentDay++
currentStep++
if (onProgress) {
onProgress({ currentDay, totalDays, completed: false })
onProgress({ currentStep, totalSteps: TOTAL_STEPS, completed: false })
}
// Fetch and cache all series
await fetchAndCacheSeries(poolWithSub, userPubkey)
currentDay++
currentStep++
if (onProgress) {
onProgress({ currentDay, totalDays, completed: false })
onProgress({ currentStep, totalSteps: TOTAL_STEPS, completed: false })
}
// Fetch and cache all publications
await fetchAndCachePublications(poolWithSub, userPubkey)
currentDay++
currentStep++
if (onProgress) {
onProgress({ currentDay, totalDays, completed: false })
onProgress({ currentStep, totalSteps: TOTAL_STEPS, completed: false })
}
// Fetch and cache all purchases (as payer)
await fetchAndCachePurchases(poolWithSub, userPubkey)
currentDay++
currentStep++
if (onProgress) {
onProgress({ currentDay, totalDays, completed: false })
onProgress({ currentStep, totalSteps: TOTAL_STEPS, completed: false })
}
// Fetch and cache all sponsoring (as author)
await fetchAndCacheSponsoring(poolWithSub, userPubkey)
currentDay++
currentStep++
if (onProgress) {
onProgress({ currentDay, totalDays, completed: false })
onProgress({ currentStep, totalSteps: TOTAL_STEPS, completed: false })
}
// Fetch and cache all review tips (as author)
await fetchAndCacheReviewTips(poolWithSub, userPubkey)
currentDay++
currentStep++
if (onProgress) {
onProgress({ currentDay, totalDays, completed: true })
onProgress({ currentStep, totalSteps: TOTAL_STEPS, completed: true })
}
// Store the current timestamp as last sync date

View File

@ -12,7 +12,12 @@ interface ZapAggregationFilter {
timeoutMs?: number
}
function buildFilters(params: ZapAggregationFilter) {
function buildFilters(params: ZapAggregationFilter): Array<{
kinds: number[]
since?: number
'#p'?: string[]
'#e'?: string[]
}> {
const filters = [
{
kinds: [9735],
@ -59,7 +64,7 @@ function collectZap(
let total = 0
let finished = false
const done = () => {
const done = (): void => {
if (finished) {
return
}
@ -68,7 +73,7 @@ function collectZap(
resolve(total)
}
const onError = (err: unknown) => {
const onError = (err: unknown): void => {
if (finished) {
return
}

View File

@ -26,7 +26,7 @@ export async function getZapReceiptById(zapReceiptId: string): Promise<Event | n
return new Promise<Event | null>((resolve) => {
let resolved = false
const finalize = (value: Event | null) => {
const finalize = (value: Event | null): void => {
if (resolved) {
return
}

View File

@ -4,7 +4,7 @@ import { useI18n } from '@/hooks/useI18n'
import React from 'react'
import { platformSyncService } from '@/lib/platformSync'
function I18nProvider({ children }: { children: React.ReactNode }) {
function I18nProvider({ children }: { children: React.ReactNode }): React.ReactElement {
// Get saved locale from localStorage or default to French
const getInitialLocale = (): 'fr' | 'en' => {
if (typeof window === 'undefined') {

View File

@ -4,7 +4,7 @@ import path from 'path'
const DOCS_DIR = path.join(process.cwd(), 'docs')
export default function handler(req: NextApiRequest, res: NextApiResponse) {
export default function handler(req: NextApiRequest, res: NextApiResponse): void {
const { file } = req.query
const locale = (req.query.locale as string) || 'fr' // Default to French

View File

@ -137,12 +137,12 @@ function useHomeController(): {
const handleUnlock = useUnlockHandler(loadArticleContent, setUnlockedArticles)
// Get authors by category
const allAuthors = useMemo(() => {
const allAuthors = useMemo((): Article[] => {
const authorsArray = Array.from(presentationArticles.values())
return sortAuthors(authorsArray)
}, [presentationArticles])
const authors = useMemo(() => {
const authors = useMemo((): Article[] => {
return getAuthorsByCategory(presentationArticles, selectedCategory)
}, [presentationArticles, selectedCategory])

View File

@ -6,7 +6,7 @@ import { useNostrAuth } from '@/hooks/useNostrAuth'
import { getSeriesByAuthor } from '@/lib/seriesQueries'
import { t } from '@/lib/i18n'
function PublishHeader() {
function PublishHeader(): React.ReactElement {
return (
<Head>
<title>{t('publish.title')} - {t('home.title')}</title>

View File

@ -239,7 +239,7 @@ settings.keyManagement.recovery.done=Done
settings.sync.title=Notes Synchronization
settings.sync.start=Start Synchronization
settings.sync.daysRange=From {{startDate}} to {{endDate}} ({{days}} days)
settings.sync.progress=Day {{current}} of {{total}}
settings.sync.progress=Step {{current}} of {{total}}
settings.sync.completed=Everything is synchronized
settings.nip95.title=NIP-95 Upload Endpoints
settings.nip95.loading=Loading...

View File

@ -239,7 +239,7 @@ settings.keyManagement.recovery.done=Terminé
settings.sync.title=Synchronisation des notes
settings.sync.start=Démarrer la synchronisation
settings.sync.daysRange=Du {{startDate}} au {{endDate}} ({{days}} jours)
settings.sync.progress=Jour {{current}} sur {{total}}
settings.sync.progress=Étape {{current}} sur {{total}}
settings.sync.completed=Tout est synchronisé
settings.language.title=Langue de préférence
settings.language.description=Choisissez votre langue préférée pour l'interface

View File

@ -157,13 +157,6 @@ export interface ReviewTip {
kindType?: KindType
}
export interface ZapRequest {
event: Event
amount: number
targetPubkey: string
targetEventId?: string
}
export interface NostrConnectState {
connected: boolean
pubkey: string | null