RBJin's picture
Upload 20 files
81cb6e0 verified
import { Highlight, Category, FavoritePaper } from '../types';
const KEYS = {
highlights: 'arxiv_explorer_highlights',
categories: 'arxiv_explorer_categories',
favorites: 'arxiv_explorer_favorites',
cachedSections: 'arxiv_explorer_sections_cache',
};
function loadJson<T>(key: string, fallback: T): T {
try {
const data = localStorage.getItem(key);
return data ? JSON.parse(data) : fallback;
} catch {
return fallback;
}
}
function saveJson<T>(key: string, data: T) {
try {
localStorage.setItem(key, JSON.stringify(data));
} catch (e) {
console.warn('Failed to save to localStorage:', e);
}
}
// ==================== Highlights ====================
export function getHighlights(): Highlight[] {
return loadJson<Highlight[]>(KEYS.highlights, []);
}
export function addHighlight(highlight: Omit<Highlight, 'id' | 'timestamp'>): Highlight {
const highlights = getHighlights();
const newHighlight: Highlight = {
...highlight,
id: `hl_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
timestamp: Date.now(),
};
highlights.unshift(newHighlight);
saveJson(KEYS.highlights, highlights);
return newHighlight;
}
export function removeHighlight(id: string) {
const highlights = getHighlights().filter((h) => h.id !== id);
saveJson(KEYS.highlights, highlights);
}
// ==================== Categories ====================
const defaultCategories: Category[] = [
{ id: 'uncategorized', name: '未分类 / Uncategorized', color: '#6b7280' },
];
export function getCategories(): Category[] {
return loadJson<Category[]>(KEYS.categories, defaultCategories);
}
export function addCategory(name: string, color: string): Category {
const categories = getCategories();
const newCat: Category = {
id: `cat_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`,
name,
color,
};
categories.push(newCat);
saveJson(KEYS.categories, categories);
return newCat;
}
export function removeCategory(id: string) {
if (id === 'uncategorized') return;
const categories = getCategories().filter((c) => c.id !== id);
saveJson(KEYS.categories, categories);
// Move papers in deleted category to uncategorized
const favorites = getFavorites();
favorites.forEach((f) => {
if (f.categoryId === id) f.categoryId = 'uncategorized';
});
saveJson(KEYS.favorites, favorites);
}
export function updateCategory(id: string, name: string, color: string) {
const categories = getCategories().map((c) =>
c.id === id ? { ...c, name, color } : c
);
saveJson(KEYS.categories, categories);
}
// ==================== Favorites ====================
export function getFavorites(): FavoritePaper[] {
return loadJson<FavoritePaper[]>(KEYS.favorites, []);
}
export function addFavorite(
paperId: string,
paperTitle: string,
paperAuthors: string[],
categoryId: string = 'uncategorized'
): FavoritePaper {
const favorites = getFavorites();
const existing = favorites.find((f) => f.paperId === paperId);
if (existing) return existing;
const fav: FavoritePaper = {
paperId,
paperTitle,
paperAuthors,
categoryId,
timestamp: Date.now(),
};
favorites.unshift(fav);
saveJson(KEYS.favorites, favorites);
return fav;
}
export function removeFavorite(paperId: string) {
const favorites = getFavorites().filter((f) => f.paperId !== paperId);
saveJson(KEYS.favorites, favorites);
}
export function moveFavoriteToCategory(paperId: string, categoryId: string) {
const favorites = getFavorites().map((f) =>
f.paperId === paperId ? { ...f, categoryId } : f
);
saveJson(KEYS.favorites, favorites);
}
export function isFavorited(paperId: string): boolean {
return getFavorites().some((f) => f.paperId === paperId);
}
// ==================== Section Cache ====================
interface CachedSections {
[arxivId: string]: {
introduction?: string;
relatedWork?: string;
methods?: string;
references?: Array<{ key: string; number: string; text: string; arxivId?: string }>;
timestamp: number;
};
}
export function getCachedSections(arxivId: string) {
const cache = loadJson<CachedSections>(KEYS.cachedSections, {});
const entry = cache[arxivId];
if (entry && Date.now() - entry.timestamp < 7 * 24 * 60 * 60 * 1000) {
return entry;
}
return null;
}
export function setCachedSections(
arxivId: string,
data: {
introduction?: string;
relatedWork?: string;
methods?: string;
references?: Array<{ key: string; number: string; text: string; arxivId?: string }>;
}
) {
const cache = loadJson<CachedSections>(KEYS.cachedSections, {});
// Limit cache size
const keys = Object.keys(cache);
if (keys.length > 100) {
const sorted = keys.sort((a, b) => (cache[a].timestamp || 0) - (cache[b].timestamp || 0));
sorted.slice(0, 20).forEach((k) => delete cache[k]);
}
cache[arxivId] = { ...data, timestamp: Date.now() };
saveJson(KEYS.cachedSections, cache);
}