refactor(Index.tsx): extract note loading logic into loadNotesAroundDate function
This commit is contained in:
@@ -486,52 +486,8 @@ const Index = () => {
|
|||||||
setCurrentNoteIndex(targetIndex);
|
setCurrentNoteIndex(targetIndex);
|
||||||
setPreviousNote(notes[targetIndex]);
|
setPreviousNote(notes[targetIndex]);
|
||||||
} else {
|
} else {
|
||||||
// Fallback: fetch a date window around the target note
|
// Fallback: use balanced around-date loading
|
||||||
const targetMs = targetNote.epochTime;
|
await loadNotesAroundDate(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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading notes around target:', 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
|
// Search notes with debouncing
|
||||||
const searchNotes = async (query: string) => {
|
const searchNotes = async (query: string) => {
|
||||||
if (!query.trim()) {
|
if (!query.trim()) {
|
||||||
@@ -627,61 +653,9 @@ const Index = () => {
|
|||||||
});
|
});
|
||||||
return;
|
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);
|
setIsGotoOpen(false);
|
||||||
setGotoDateInput('');
|
setGotoDateInput('');
|
||||||
await loadNotesAroundNote(targetNote);
|
await loadNotesAroundDate(targetMs);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error going to date:', error);
|
console.error('Error going to date:', error);
|
||||||
toast({
|
toast({
|
||||||
|
Reference in New Issue
Block a user