refactor(Index.tsx): extract note loading logic into loadNotesAroundDate function

This commit is contained in:
2025-08-11 11:56:11 +02:00
parent d799df2dbd
commit e54efe011b

View File

@@ -486,52 +486,8 @@ const Index = () => {
setCurrentNoteIndex(targetIndex);
setPreviousNote(notes[targetIndex]);
} else {
// Fallback: fetch a date window around the target note
const targetMs = targetNote.epochTime;
const headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
} as const;
let windowMs = 1000 * 60 * 60 * 24 * 90; // 90 days
for (let i = 0; i < 3; i++) {
const start = targetMs - windowMs;
const end = targetMs + windowMs;
const resp = await fetch(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, {
method: 'POST',
headers,
body: JSON.stringify({
q: '',
filter: `date >= ${start} AND date <= ${end}`,
sort: ['date:desc'],
limit: 1000,
}),
});
if (resp.ok) {
const d = await resp.json();
const aroundNotes: Note[] = d.hits.map((hit: any) => ({
id: hit.id,
epochTime: hit.date,
dateISO: hit.dateISO,
content: hit.content,
topLetter: hit.topLetter,
topLetterFrequency: hit.topLetterFrequency,
letterCount: hit.letterCount || 0,
}));
if (aroundNotes.length > 0) {
const exactIdx = aroundNotes.findIndex(n => n.id === targetNote.id);
const pickIdx = exactIdx >= 0
? exactIdx
: aroundNotes.reduce((bestIdx, n, idx) => (
Math.abs(n.epochTime - targetMs) < Math.abs(aroundNotes[bestIdx].epochTime - targetMs) ? idx : bestIdx
), 0);
setNoteCache(aroundNotes);
setCurrentNoteIndex(pickIdx);
setPreviousNote(aroundNotes[pickIdx]);
return;
}
}
windowMs *= 2;
}
// Fallback: use balanced around-date loading
await loadNotesAroundDate(targetNote.epochTime);
}
} catch (error) {
console.error('Error loading notes around target:', error);
@@ -545,6 +501,76 @@ const Index = () => {
}
};
// Load notes balanced around a timestamp (grabs before and after sets and picks nearest)
const loadNotesAroundDate = async (targetMs: number, beforeLimit = 600, afterLimit = 600) => {
try {
setIsLoading(true);
const headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
} as const;
const [beforeRes, afterRes] = await Promise.all([
fetch(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, {
method: 'POST',
headers,
body: JSON.stringify({ q: '', filter: `date <= ${targetMs}`, sort: ['date:desc'], limit: beforeLimit }),
}),
fetch(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, {
method: 'POST',
headers,
body: JSON.stringify({ q: '', filter: `date >= ${targetMs}`, sort: ['date:asc'], limit: afterLimit }),
}),
]);
const [beforeData, afterData] = await Promise.all([
beforeRes.ok ? beforeRes.json() : Promise.resolve({ hits: [] }),
afterRes.ok ? afterRes.json() : Promise.resolve({ hits: [] }),
]);
const before: Note[] = (beforeData.hits || []).map((hit: any) => ({
id: hit.id,
epochTime: hit.date,
dateISO: hit.dateISO,
content: hit.content,
topLetter: hit.topLetter,
topLetterFrequency: hit.topLetterFrequency,
letterCount: hit.letterCount || 0,
}));
const after: Note[] = (afterData.hits || []).map((hit: any) => ({
id: hit.id,
epochTime: hit.date,
dateISO: hit.dateISO,
content: hit.content,
topLetter: hit.topLetter,
topLetterFrequency: hit.topLetterFrequency,
letterCount: hit.letterCount || 0,
}));
// Combine, remove duplicates by id, and sort desc by date
const combinedMap = new Map<string, Note>();
for (const n of before) combinedMap.set(n.id, n);
for (const n of after) combinedMap.set(n.id, n);
const combined = Array.from(combinedMap.values()).sort((a, b) => b.epochTime - a.epochTime);
if (combined.length === 0) {
toast({ title: 'No notes found', description: 'There are no notes near that date.' });
return;
}
const closestIndex = combined.reduce((bestIdx, n, idx) => (
Math.abs(n.epochTime - targetMs) < Math.abs(combined[bestIdx].epochTime - targetMs) ? idx : bestIdx
), 0);
setNoteCache(combined);
setCurrentNoteIndex(closestIndex);
setPreviousNote(combined[closestIndex]);
} finally {
setIsLoading(false);
}
};
// Search notes with debouncing
const searchNotes = async (query: string) => {
if (!query.trim()) {
@@ -627,61 +653,9 @@ const Index = () => {
});
return;
}
const headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${MEILISEARCH_API_KEY}`,
} as const;
const [prevRes, nextRes] = await Promise.all([
fetch(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, {
method: 'POST',
headers,
body: JSON.stringify({ q: '', filter: `date <= ${targetMs}`, sort: ['date:desc'], limit: 1 }),
}),
fetch(`${MEILISEARCH_ENDPOINT}/indexes/${NOTE_INDEX}/search`, {
method: 'POST',
headers,
body: JSON.stringify({ q: '', filter: `date >= ${targetMs}`, sort: ['date:asc'], limit: 1 }),
}),
]);
const [prevData, nextData] = await Promise.all([
prevRes.ok ? prevRes.json() : Promise.resolve({ hits: [] }),
nextRes.ok ? nextRes.json() : Promise.resolve({ hits: [] }),
]);
const candidates: any[] = [];
if (prevData.hits && prevData.hits.length > 0) candidates.push(prevData.hits[0]);
if (nextData.hits && nextData.hits.length > 0) candidates.push(nextData.hits[0]);
if (candidates.length === 0) {
toast({
title: 'No notes found',
description: 'There are no notes near that date.',
});
return;
}
const closest = candidates.reduce((best, hit) => {
const bestDiff = Math.abs((best?.date ?? best?.epochTime ?? 0) - targetMs);
const hitDiff = Math.abs((hit.date ?? 0) - targetMs);
return hitDiff < bestDiff ? hit : best;
}, candidates[0]);
const targetNote: Note = {
id: closest.id,
epochTime: closest.date,
dateISO: closest.dateISO,
content: closest.content,
topLetter: closest.topLetter,
topLetterFrequency: closest.topLetterFrequency,
letterCount: closest.letterCount || 0,
};
setIsGotoOpen(false);
setGotoDateInput('');
await loadNotesAroundNote(targetNote);
await loadNotesAroundDate(targetMs);
} catch (error) {
console.error('Error going to date:', error);
toast({