From e54efe011be1dd4344424e746a7bb75635d41304 Mon Sep 17 00:00:00 2001 From: PhatPhuckDave Date: Mon, 11 Aug 2025 11:56:11 +0200 Subject: [PATCH] refactor(Index.tsx): extract note loading logic into loadNotesAroundDate function --- src/pages/Index.tsx | 172 +++++++++++++++++++------------------------- 1 file changed, 73 insertions(+), 99 deletions(-) diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx index 24b84c4..33087e1 100644 --- a/src/pages/Index.tsx +++ b/src/pages/Index.tsx @@ -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(); + 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({