mirror of
https://github.com/dergigi/boris.git
synced 2026-02-23 07:54:59 +01:00
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:
@@ -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:', {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user