fix: resolve all linting and TypeScript issues

- Rename fetch import to fetchOpenGraph to avoid global variable conflict
- Replace any types with proper Record<string, unknown> types
- Add proper type guards for OpenGraph data extraction
- Remove unused CACHE_TTL variable
- Fix TypeScript null assignment error in opengraphEnhancer
- All linting rules and type checks now pass
This commit is contained in:
Gigi
2025-10-25 01:26:54 +02:00
parent 9f806afc45
commit 688d4285e3
3 changed files with 38 additions and 43 deletions

View File

@@ -4,7 +4,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faTimes, faSpinner } from '@fortawesome/free-solid-svg-icons'
import IconButton from './IconButton'
import { fetchReadableContent } from '../services/readerService'
import { fetch } from 'fetch-opengraph'
import { fetch as fetchOpenGraph } from 'fetch-opengraph'
interface AddBookmarkModalProps {
onClose: () => void
@@ -12,11 +12,11 @@ interface AddBookmarkModalProps {
}
// Helper to extract tags from OpenGraph data
function extractTagsFromOgData(ogData: any): string[] {
function extractTagsFromOgData(ogData: Record<string, unknown>): string[] {
const tags: string[] = []
// Extract keywords from OpenGraph data
if (ogData.keywords) {
if (ogData.keywords && typeof ogData.keywords === 'string') {
ogData.keywords.split(/[,;]/)
.map((k: string) => k.trim().toLowerCase())
.filter((k: string) => k.length > 0 && k.length < 30)
@@ -25,14 +25,17 @@ function extractTagsFromOgData(ogData: any): string[] {
// Extract article:tag from OpenGraph data
if (ogData['article:tag']) {
const articleTags = Array.isArray(ogData['article:tag'])
? ogData['article:tag']
: [ogData['article:tag']]
const articleTagValue = ogData['article:tag']
const articleTags = Array.isArray(articleTagValue)
? articleTagValue
: [articleTagValue]
articleTags.forEach((tag: string) => {
const cleanTag = tag.trim().toLowerCase()
if (cleanTag && cleanTag.length < 30) {
tags.push(cleanTag)
articleTags.forEach((tag: unknown) => {
if (typeof tag === 'string') {
const cleanTag = tag.trim().toLowerCase()
if (cleanTag && cleanTag.length < 30) {
tags.push(cleanTag)
}
}
})
}
@@ -82,7 +85,7 @@ const AddBookmarkModal: React.FC<AddBookmarkModalProps> = ({ onClose, onSave })
// Fetch both readable content and OpenGraph data in parallel
const [content, ogData] = await Promise.all([
fetchReadableContent(normalizedUrl),
fetch(normalizedUrl).catch(() => null) // Don't fail if OpenGraph fetch fails
fetchOpenGraph(normalizedUrl).catch(() => null) // Don't fail if OpenGraph fetch fails
])
console.log('🔍 Modal fetch debug:', {

View File

@@ -1,33 +1,18 @@
import { fetch } from 'fetch-opengraph'
import { fetch as fetchOpenGraph } from 'fetch-opengraph'
import { ReadItem } from './readsService'
// Cache for OpenGraph data to avoid repeated requests
const ogCache = new Map<string, any>()
const CACHE_TTL = 7 * 24 * 60 * 60 * 1000 // 7 days
const ogCache = new Map<string, Record<string, unknown>>()
interface CachedOgData {
data: any
timestamp: number
}
function getCachedOgData(url: string): any | null {
function getCachedOgData(url: string): Record<string, unknown> | null {
const cached = ogCache.get(url)
if (!cached) return null
const age = Date.now() - cached.timestamp
if (age > CACHE_TTL) {
ogCache.delete(url)
return null
}
return cached.data
return cached
}
function setCachedOgData(url: string, data: any): void {
ogCache.set(url, {
data,
timestamp: Date.now()
})
function setCachedOgData(url: string, data: Record<string, unknown>): void {
ogCache.set(url, data)
}
/**
@@ -48,8 +33,11 @@ export async function enhanceReadItemWithOpenGraph(item: ReadItem): Promise<Read
if (!ogData) {
// Fetch OpenGraph data
ogData = await fetch(item.url)
setCachedOgData(item.url, ogData)
const fetchedOgData = await fetchOpenGraph(item.url)
if (fetchedOgData) {
ogData = fetchedOgData
setCachedOgData(item.url, fetchedOgData)
}
}
if (!ogData) return item
@@ -59,17 +47,26 @@ export async function enhanceReadItemWithOpenGraph(item: ReadItem): Promise<Read
// Use OpenGraph title if we don't have a good title
if (!enhanced.title || enhanced.title === fallbackTitleFromUrl(item.url)) {
enhanced.title = ogData['og:title'] || ogData['twitter:title'] || ogData.title || enhanced.title
const ogTitle = ogData['og:title'] || ogData['twitter:title'] || ogData.title
if (typeof ogTitle === 'string') {
enhanced.title = ogTitle
}
}
// Use OpenGraph description if we don't have a summary
if (!enhanced.summary) {
enhanced.summary = ogData['og:description'] || ogData['twitter:description'] || ogData.description
const ogDescription = ogData['og:description'] || ogData['twitter:description'] || ogData.description
if (typeof ogDescription === 'string') {
enhanced.summary = ogDescription
}
}
// Use OpenGraph image if we don't have an image
if (!enhanced.image) {
enhanced.image = ogData['og:image'] || ogData['twitter:image'] || ogData.image
const ogImage = ogData['og:image'] || ogData['twitter:image'] || ogData.image
if (typeof ogImage === 'string') {
enhanced.image = ogImage
}
}
return enhanced

View File

@@ -40,11 +40,6 @@ export async function deriveLinksFromBookmarks(bookmarks: Bookmark[]): Promise<R
const summary = bookmark.tags.find(t => t[0] === 'summary')?.[1]
const image = bookmark.tags.find(t => t[0] === 'image')?.[1]
// For web bookmarks (kind:39701), description is stored in content field, not summary tag
const description = bookmark.kind === KINDS.WebBookmark && bookmark.content
? bookmark.content.trim()
: summary
// Create ReadItem for each unique URL
for (const url of [...new Set(urls)]) {
if (!linksMap.has(url)) {
@@ -54,7 +49,7 @@ export async function deriveLinksFromBookmarks(bookmarks: Bookmark[]): Promise<R
type: 'external',
url,
title: title || fallbackTitleFromUrl(url),
summary: description,
summary,
image,
readingProgress: 0,
readingTimestamp: bookmark.created_at ?? undefined