lint fix wip

This commit is contained in:
Nicolas Cantu 2026-01-06 16:10:08 +01:00
parent 303c0bf7df
commit ccf2fdf759
15 changed files with 77 additions and 48 deletions

View File

@ -34,7 +34,7 @@ export function LanguageSelector(): React.ReactElement {
const loadLocale = (): void => { const loadLocale = (): void => {
try { try {
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
const savedLocale = localStorage.getItem(LOCALE_STORAGE_KEY) as Locale | null const savedLocale = window.localStorage.getItem(LOCALE_STORAGE_KEY) as Locale | null
if (savedLocale && (savedLocale === 'fr' || savedLocale === 'en')) { if (savedLocale && (savedLocale === 'fr' || savedLocale === 'en')) {
setLocale(savedLocale) setLocale(savedLocale)
setCurrentLocale(savedLocale) setCurrentLocale(savedLocale)
@ -52,7 +52,7 @@ export function LanguageSelector(): React.ReactElement {
setCurrentLocale(locale) setCurrentLocale(locale)
try { try {
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
localStorage.setItem(LOCALE_STORAGE_KEY, locale) window.localStorage.setItem(LOCALE_STORAGE_KEY, locale)
} }
} catch (e) { } catch (e) {
console.error('Error saving locale:', e) console.error('Error saving locale:', e)

View File

@ -38,7 +38,7 @@ export function LanguageSettingsManager(): React.ReactElement {
setLoading(false) setLoading(false)
return return
} }
const savedLocale = localStorage.getItem(LOCALE_STORAGE_KEY) as Locale | null const savedLocale = window.localStorage.getItem(LOCALE_STORAGE_KEY) as Locale | null
if (savedLocale && (savedLocale === 'fr' || savedLocale === 'en')) { if (savedLocale && (savedLocale === 'fr' || savedLocale === 'en')) {
setLocale(savedLocale) setLocale(savedLocale)
setCurrentLocale(savedLocale) setCurrentLocale(savedLocale)
@ -57,7 +57,7 @@ export function LanguageSettingsManager(): React.ReactElement {
setCurrentLocale(locale) setCurrentLocale(locale)
try { try {
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
localStorage.setItem(LOCALE_STORAGE_KEY, locale) window.localStorage.setItem(LOCALE_STORAGE_KEY, locale)
} }
} catch (e) { } catch (e) {
console.error('Error saving locale:', e) console.error('Error saving locale:', e)

View File

@ -17,7 +17,7 @@ export function subscribeWithTimeout<T>(
const resolved = { value: false } const resolved = { value: false }
const relayUrl = getPrimaryRelaySync() const relayUrl = getPrimaryRelaySync()
const sub = createSubscription(pool, [relayUrl], filters) const sub = createSubscription(pool, [relayUrl], filters)
let timeoutId: NodeJS.Timeout | null = null let timeoutId: ReturnType<typeof setTimeout> | null = null
const cleanup = (): void => { const cleanup = (): void => {
if (timeoutId) { if (timeoutId) {

View File

@ -1,6 +1,6 @@
import type { AuthorTags, SeriesTags, PublicationTags, QuoteTags, PaymentTags } from './nostrTagSystemTypes' import type { AuthorTags, SeriesTags, PublicationTags, QuoteTags, PaymentTags } from './nostrTagSystemTypes'
function buildBaseTags(tags: AuthorTags | SeriesTags | PublicationTags | QuoteTags): string[][] { function buildBaseTags(tags: AuthorTags | SeriesTags | PublicationTags | QuoteTags | PaymentTags): string[][] {
const result: string[][] = [] const result: string[][] = []
result.push([tags.type]) result.push([tags.type])
result.push([tags.category]) result.push([tags.category])

View File

@ -42,29 +42,47 @@ export function extractCommonTags(findTag: (key: string) => string | undefined,
reviewerPubkey?: string reviewerPubkey?: string
json?: string json?: string
} { } {
const id = findTag('id')
const service = findTag('service')
const title = findTag('title')
const preview = findTag('preview')
const description = findTag('description')
const mainnetAddress = findTag('mainnet_address')
const totalSponsoring = parseNumericTag(findTag, 'total_sponsoring')
const pictureUrl = findTag('picture')
const seriesId = findTag('series')
const coverUrl = findTag('cover')
const bannerUrl = findTag('banner')
const zapAmount = parseNumericTag(findTag, 'zap')
const invoice = findTag('invoice')
const paymentHash = findTag('payment_hash')
const encryptedKey = findTag('encrypted_key')
const articleId = findTag('article')
const reviewerPubkey = findTag('reviewer')
const json = findTag('json')
return { return {
id: findTag('id'), ...(id ? { id } : {}),
service: findTag('service'), ...(service ? { service } : {}),
version: parseNumericTag(findTag, 'version') ?? 0, // Default to 0 if not present version: parseNumericTag(findTag, 'version') ?? 0, // Default to 0 if not present
hidden: findTag('hidden') === 'true', // true only if tag exists and value is 'true' hidden: findTag('hidden') === 'true', // true only if tag exists and value is 'true'
paywall: hasTag('paywall'), paywall: hasTag('paywall'),
payment: hasTag('payment'), payment: hasTag('payment'),
title: findTag('title'), ...(title ? { title } : {}),
preview: findTag('preview'), ...(preview ? { preview } : {}),
description: findTag('description'), ...(description ? { description } : {}),
mainnetAddress: findTag('mainnet_address'), ...(mainnetAddress ? { mainnetAddress } : {}),
totalSponsoring: parseNumericTag(findTag, 'total_sponsoring'), ...(totalSponsoring !== undefined ? { totalSponsoring } : {}),
pictureUrl: findTag('picture'), ...(pictureUrl ? { pictureUrl } : {}),
seriesId: findTag('series'), ...(seriesId ? { seriesId } : {}),
coverUrl: findTag('cover'), ...(coverUrl ? { coverUrl } : {}),
bannerUrl: findTag('banner'), ...(bannerUrl ? { bannerUrl } : {}),
zapAmount: parseNumericTag(findTag, 'zap'), ...(zapAmount !== undefined ? { zapAmount } : {}),
invoice: findTag('invoice'), ...(invoice ? { invoice } : {}),
paymentHash: findTag('payment_hash'), ...(paymentHash ? { paymentHash } : {}),
encryptedKey: findTag('encrypted_key'), ...(encryptedKey ? { encryptedKey } : {}),
articleId: findTag('article'), ...(articleId ? { articleId } : {}),
reviewerPubkey: findTag('reviewer'), ...(reviewerPubkey ? { reviewerPubkey } : {}),
json: findTag('json'), // JSON metadata stored in tag (for all object types) ...(json ? { json } : {}),
} }
} }

View File

@ -4,7 +4,7 @@
* One database per object type * One database per object type
*/ */
import type { Event } from 'nostr-tools' import type { Event as NostrEvent } from 'nostr-tools'
import type { AuthorPresentationArticle } from '@/types/nostr' import type { AuthorPresentationArticle } from '@/types/nostr'
import { buildObjectId } from './urlGenerator' import { buildObjectId } from './urlGenerator'
@ -15,7 +15,7 @@ interface CachedObject {
hash: string // SHA-256 hash of the object hash: string // SHA-256 hash of the object
hashId: string // Legacy field for backward compatibility hashId: string // Legacy field for backward compatibility
index: number // Index for duplicates index: number // Index for duplicates
event: Event event: NostrEvent
parsed: unknown // Parsed object (AuthorPresentationArticle, Series, etc.) parsed: unknown // Parsed object (AuthorPresentationArticle, Series, etc.)
version: number version: number
hidden: boolean hidden: boolean
@ -41,7 +41,7 @@ class ObjectCacheService {
} }
const dbName = `${DB_PREFIX}${objectType}` const dbName = `${DB_PREFIX}${objectType}`
const request = indexedDB.open(dbName, DB_VERSION) const request = window.indexedDB.open(dbName, DB_VERSION)
request.onerror = (): void => { request.onerror = (): void => {
reject(new Error(`Failed to open IndexedDB: ${request.error}`)) reject(new Error(`Failed to open IndexedDB: ${request.error}`))
@ -112,7 +112,7 @@ class ObjectCacheService {
async set( async set(
objectType: ObjectType, objectType: ObjectType,
hash: string, hash: string,
event: Event, event: NostrEvent,
parsed: unknown, parsed: unknown,
version: number, version: number,
hidden: boolean, hidden: boolean,
@ -175,7 +175,7 @@ class ObjectCacheService {
const request = hashIndex.openCursor(IDBKeyRange.only(hash)) const request = hashIndex.openCursor(IDBKeyRange.only(hash))
const objects: CachedObject[] = [] const objects: CachedObject[] = []
request.onsuccess = (event: Event): void => { request.onsuccess = (event: globalThis.Event): void => {
const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result
if (cursor) { if (cursor) {
const obj = cursor.value as CachedObject const obj = cursor.value as CachedObject
@ -246,7 +246,7 @@ class ObjectCacheService {
const request = store.openCursor() const request = store.openCursor()
const objects: CachedObject[] = [] const objects: CachedObject[] = []
request.onsuccess = (event: Event): void => { request.onsuccess = (event: globalThis.Event): void => {
const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result
if (cursor) { if (cursor) {
const obj = cursor.value as CachedObject const obj = cursor.value as CachedObject
@ -288,7 +288,7 @@ class ObjectCacheService {
const request = store.openCursor() const request = store.openCursor()
const objects: unknown[] = [] const objects: unknown[] = []
request.onsuccess = (event: Event): void => { request.onsuccess = (event: globalThis.Event): void => {
const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result
if (cursor) { if (cursor) {
const obj = cursor.value as CachedObject const obj = cursor.value as CachedObject

View File

@ -46,8 +46,8 @@ export async function publishPurchaseNote(params: {
payerPubkey: params.payerPubkey, payerPubkey: params.payerPubkey,
recipientPubkey: params.authorPubkey, recipientPubkey: params.authorPubkey,
paymentHash: params.paymentHash, paymentHash: params.paymentHash,
zapReceiptId: params.zapReceiptId,
articleId: params.articleId, articleId: params.articleId,
...(params.zapReceiptId ? { zapReceiptId: params.zapReceiptId } : {}),
...(params.seriesId ? { seriesId: params.seriesId } : {}), ...(params.seriesId ? { seriesId: params.seriesId } : {}),
}) })
@ -120,9 +120,9 @@ export async function publishReviewTipNote(params: {
payerPubkey: params.payerPubkey, payerPubkey: params.payerPubkey,
recipientPubkey: params.reviewerPubkey, recipientPubkey: params.reviewerPubkey,
paymentHash: params.paymentHash, paymentHash: params.paymentHash,
zapReceiptId: params.zapReceiptId,
articleId: params.articleId, articleId: params.articleId,
reviewId: params.reviewId, reviewId: params.reviewId,
...(params.zapReceiptId ? { zapReceiptId: params.zapReceiptId } : {}),
...(params.seriesId ? { seriesId: params.seriesId } : {}), ...(params.seriesId ? { seriesId: params.seriesId } : {}),
...(params.text ? { text: params.text } : {}), ...(params.text ? { text: params.text } : {}),
}) })

View File

@ -61,15 +61,16 @@ export async function sendPrivateContentAfterPayment(
} }
} }
const category = article.category === 'author-presentation' ? undefined : (article.category === 'science-fiction' || article.category === 'scientific-research' ? article.category : undefined)
await publishPurchaseNote({ await publishPurchaseNote({
articleId: article.id, articleId: article.id,
authorPubkey: article.pubkey, authorPubkey: article.pubkey,
payerPubkey: recipientPubkey, payerPubkey: recipientPubkey,
amount, amount,
paymentHash, paymentHash,
zapReceiptId, ...(zapReceiptId ? { zapReceiptId } : {}),
category: article.category, ...(category ? { category } : {}),
seriesId: article.seriesId, ...(article.seriesId ? { seriesId: article.seriesId } : {}),
payerPrivateKey, payerPrivateKey,
}) })
} }

View File

@ -28,6 +28,9 @@ function buildPurchaseFilters(articleId?: string, payerPubkey?: string, authorPu
kinds: number[] kinds: number[]
since: number since: number
'#kind_type': string[] '#kind_type': string[]
authors?: string[]
'#p'?: string[]
'#e'?: string[]
} = { } = {
kinds: [9735], // Zap receipt kinds: [9735], // Zap receipt
since: MIN_EVENT_DATE, since: MIN_EVENT_DATE,

View File

@ -22,6 +22,10 @@ function buildReviewTipFilters(articleId?: string, reviewId?: string, authorPubk
kinds: number[] kinds: number[]
since: number since: number
'#kind_type': string[] '#kind_type': string[]
'#p'?: string[]
'#e'?: string[]
'#review_id'?: string[]
'#reviewer'?: string[]
} = { } = {
kinds: [9735], // Zap receipt kinds: [9735], // Zap receipt
since: MIN_EVENT_DATE, since: MIN_EVENT_DATE,

View File

@ -22,6 +22,10 @@ function buildSponsoringFilters(authorPubkey?: string, payerPubkey?: string, ser
kinds: number[] kinds: number[]
since: number since: number
'#kind_type': string[] '#kind_type': string[]
authors?: string[]
'#p'?: string[]
'#series'?: string[]
'#article'?: string[]
} = { } = {
kinds: [9735], // Zap receipt kinds: [9735], // Zap receipt
since: MIN_EVENT_DATE, since: MIN_EVENT_DATE,

View File

@ -5,11 +5,11 @@ function toBase64(bytes: Uint8Array): string {
bytes.forEach((b) => { bytes.forEach((b) => {
binary += String.fromCharCode(b) binary += String.fromCharCode(b)
}) })
return btoa(binary) return window.btoa(binary)
} }
function fromBase64(value: string): Uint8Array { function fromBase64(value: string): Uint8Array {
const binary = atob(value) const binary = window.atob(value)
const bytes = new Uint8Array(binary.length) const bytes = new Uint8Array(binary.length)
for (let i = 0; i < binary.length; i += 1) { for (let i = 0; i < binary.length; i += 1) {
bytes[i] = binary.charCodeAt(i) bytes[i] = binary.charCodeAt(i)
@ -20,8 +20,8 @@ function fromBase64(value: string): Uint8Array {
async function importKey(secret: string): Promise<CryptoKey> { async function importKey(secret: string): Promise<CryptoKey> {
const encoder = new TextEncoder() const encoder = new TextEncoder()
const keyMaterial = encoder.encode(secret) const keyMaterial = encoder.encode(secret)
const hash = await crypto.subtle.digest('SHA-256', keyMaterial) const hash = await window.crypto.subtle.digest('SHA-256', keyMaterial)
return crypto.subtle.importKey('raw', hash, { name: 'AES-GCM' }, false, ['encrypt', 'decrypt']) return window.crypto.subtle.importKey('raw', hash, { name: 'AES-GCM' }, false, ['encrypt', 'decrypt'])
} }
export interface EncryptedPayload { export interface EncryptedPayload {
@ -31,10 +31,10 @@ export interface EncryptedPayload {
export async function encryptPayload(secret: string, value: unknown): Promise<EncryptedPayload> { export async function encryptPayload(secret: string, value: unknown): Promise<EncryptedPayload> {
const key = await importKey(secret) const key = await importKey(secret)
const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH)) const iv = window.crypto.getRandomValues(new Uint8Array(IV_LENGTH))
const encoder = new TextEncoder() const encoder = new TextEncoder()
const encoded = encoder.encode(JSON.stringify(value)) const encoded = encoder.encode(JSON.stringify(value))
const ciphertext = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, encoded) const ciphertext = await window.crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, encoded)
return { return {
iv: toBase64(iv), iv: toBase64(iv),
ciphertext: toBase64(new Uint8Array(ciphertext)), ciphertext: toBase64(new Uint8Array(ciphertext)),
@ -47,7 +47,7 @@ export async function decryptPayload<T>(secret: string, payload: EncryptedPayloa
const cipherBytes = fromBase64(payload.ciphertext) const cipherBytes = fromBase64(payload.ciphertext)
const ivBuffer = ivBytes.buffer as ArrayBuffer const ivBuffer = ivBytes.buffer as ArrayBuffer
const cipherBuffer = cipherBytes.buffer as ArrayBuffer const cipherBuffer = cipherBytes.buffer as ArrayBuffer
const decrypted = await crypto.subtle.decrypt({ name: 'AES-GCM', iv: ivBuffer }, key, cipherBuffer) const decrypted = await window.crypto.subtle.decrypt({ name: 'AES-GCM', iv: ivBuffer }, key, cipherBuffer)
const decoder = new TextDecoder() const decoder = new TextDecoder()
return JSON.parse(decoder.decode(decrypted)) as T return JSON.parse(decoder.decode(decrypted)) as T
} }

View File

@ -48,7 +48,7 @@ export class IndexedDBStorage {
return return
} }
const request = indexedDB.open(DB_NAME, DB_VERSION) const request = window.indexedDB.open(DB_NAME, DB_VERSION)
request.onerror = (): void => { request.onerror = (): void => {
reject(new Error(`Failed to open IndexedDB: ${request.error}`)) reject(new Error(`Failed to open IndexedDB: ${request.error}`))

View File

@ -11,7 +11,7 @@ function I18nProvider({ children }: { children: React.ReactNode }) {
return 'fr' return 'fr'
} }
try { try {
const savedLocale = localStorage.getItem('zapwall-locale') as 'fr' | 'en' | null const savedLocale = window.localStorage.getItem('zapwall-locale') as 'fr' | 'en' | null
if (savedLocale === 'fr' || savedLocale === 'en') { if (savedLocale === 'fr' || savedLocale === 'en') {
return savedLocale return savedLocale
} }

View File

@ -6,7 +6,6 @@
*/ */
const { execSync } = require('child_process') const { execSync } = require('child_process')
const path = require('path')
const projectRoot = process.cwd() const projectRoot = process.cwd()
@ -18,7 +17,7 @@ try {
cwd: projectRoot, cwd: projectRoot,
env: { ...process.env, PWD: projectRoot }, env: { ...process.env, PWD: projectRoot },
}) })
} catch (error) { } catch {
// If next lint fails, try eslint directly with flat config // If next lint fails, try eslint directly with flat config
console.log('Falling back to eslint directly...') console.log('Falling back to eslint directly...')
try { try {
@ -35,7 +34,7 @@ try {
cwd: projectRoot, cwd: projectRoot,
}) })
} }
} catch (eslintError) { } catch {
console.error('Both next lint and eslint failed') console.error('Both next lint and eslint failed')
process.exit(1) process.exit(1)
} }