refactor(Index.tsx): extract note loading logic into loadNotesAroundDate function
This commit is contained in:
@@ -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({
|
||||
|
Reference in New Issue
Block a user