From 73e27c5f5bc1c3fa710d99e2ea715ec8d540e572 Mon Sep 17 00:00:00 2001 From: PhatPhuckDave Date: Mon, 9 Jun 2025 09:39:12 +0200 Subject: [PATCH] Fix loading notes when approaching newest --- lib/main.dart | 160 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 146 insertions(+), 14 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index f4c0ed4..d2fd263 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -330,6 +330,12 @@ class MainPageState extends State with WindowListener { bool _canGoNext = false; bool _isSearching = false; List _searchResults = []; + + // Add flag to track if we've reached the newest note + bool _reachedNewestNote = false; + + // Track the newest note timestamp we know about + int? _newestNoteTimestamp; // Add network activity tracker bool _isNetworkActive = false; @@ -606,7 +612,20 @@ class MainPageState extends State with WindowListener { // Special case: we're at the latest note (previousNote is same as current) final isLatestNote = _currentlyDisplayedNote!.epochTime == previousNote?.epochTime; - if (!isLatestNote) { + if (isLatestNote) { + // If we're at the latest note, there's no next note by definition + canGoNext = false; + _reachedNewestNote = true; + + // Make sure the newest timestamp is set + if (_newestNoteTimestamp == null || _currentlyDisplayedNote!.epochTime > _newestNoteTimestamp!) { + _newestNoteTimestamp = _currentlyDisplayedNote!.epochTime; + } + } else if (_newestNoteTimestamp != null && _currentlyDisplayedNote!.epochTime >= _newestNoteTimestamp!) { + // If we're at or beyond the newest timestamp we know about, there's no next + canGoNext = false; + _reachedNewestNote = true; + } else if (!_reachedNewestNote) { final next = await _trackNetworkActivity( () => meili.getNextTo(_currentlyDisplayedNote!.epochTime), operation: "Checking for next notes", @@ -615,10 +634,20 @@ class MainPageState extends State with WindowListener { if (next != null) { // Add to cache _noteCache[next.epochTime] = next; + + // Update newest note timestamp if applicable + if (_newestNoteTimestamp == null || next.epochTime > _newestNoteTimestamp!) { + _newestNoteTimestamp = next.epochTime; + } + } else { + // If there's no next note, we've reached the newest + _reachedNewestNote = true; + + // Update newest note timestamp + if (_newestNoteTimestamp == null || _currentlyDisplayedNote!.epochTime > _newestNoteTimestamp!) { + _newestNoteTimestamp = _currentlyDisplayedNote!.epochTime; + } } - } else { - // If we're at the latest note, there's no next note by definition - canGoNext = false; } } @@ -682,7 +711,7 @@ class MainPageState extends State with WindowListener { ); // Update cache with the modified note - _noteCache[_currentlyDisplayedNote!.epochTime] = _currentlyDisplayedNote!; + _noteCache[_currentlyDisplayedNote!.epochTime] = _noteCache[_currentlyDisplayedNote!.epochTime] ?? _currentlyDisplayedNote!; } } @@ -695,15 +724,53 @@ class MainPageState extends State with WindowListener { _previousEntryController.text = nextNote.content; }); - // Prefetch more notes in the background if we're getting close to the edge of our cache - final timestamp = nextNote.epochTime; - final countAfter = _noteCache.keys.where((t) => t > timestamp).length; - - // Only prefetch if we have less than 25% of our desired cache size ahead - // This prevents excessive prefetching when we already have plenty of notes - if (countAfter < _cacheSizeAfter / 4) { - debugPrint("Only $countAfter notes ahead in cache, prefetching more"); - _loadNotesAfter(timestamp); + // Check if we've reached the newest note (latest note we initially loaded) + if (previousNote != null && nextNote.epochTime == previousNote!.epochTime) { + setState(() { + _reachedNewestNote = true; + }); + debugPrint("Reached newest note, won't try to load more ahead"); + } else { + // Reset the flag if we're not at the newest note + setState(() { + _reachedNewestNote = false; + }); + + // Prefetch more notes in the background if we're getting close to the edge of our cache + final timestamp = nextNote.epochTime; + + // Check if we already have all notes to the newest timestamp + bool haveAllNotesToNewest = false; + if (_newestNoteTimestamp != null) { + // Get all timestamps in our cache between current and newest + final cachedTimestamps = _noteCache.keys + .where((t) => t > timestamp && t <= _newestNoteTimestamp!) + .toList(); + + // Count notes ahead in cache + final countAfter = cachedTimestamps.length; + + // Check if we received fewer notes than the cache size in our last prefetch + // This indicates we already have all notes up to the newest + final haveFetchedAll = _noteCache.containsKey(_newestNoteTimestamp); + + debugPrint("Have $countAfter notes cached between current and newest timestamp"); + debugPrint("Newest timestamp is cached: $haveFetchedAll"); + + // We have all notes if we've fetched the newest timestamp + haveAllNotesToNewest = haveFetchedAll; + } + + // Only prefetch if: + // 1. The current timestamp is below the newest we know about AND + // 2. We don't already have all notes between current and newest + if (!haveAllNotesToNewest && + (_newestNoteTimestamp == null || timestamp < _newestNoteTimestamp!)) { + debugPrint("Don't have all notes to newest timestamp, prefetching more"); + _loadNotesAfter(timestamp); + } else { + debugPrint("Already have all notes to newest timestamp, skipping prefetch"); + } } await _checkNavigation(); @@ -750,6 +817,12 @@ class MainPageState extends State with WindowListener { previousNote = note; _currentlyDisplayedNote = note; _previousEntryController.text = _currentlyDisplayedNote?.content ?? ""; + + // Initialize the newest note timestamp + if (note != null) { + _newestNoteTimestamp = note.epochTime; + _reachedNewestNote = true; // We're starting at the newest note + } final scratch = await _trackNetworkActivity( () => meili.getLatestScratch(), @@ -1738,9 +1811,28 @@ class MainPageState extends State with WindowListener { // Load a batch of notes after the given timestamp Future _loadNotesAfter(int timestamp) async { if (_isCacheLoading) return; + + // Don't even attempt to load if we know this timestamp is at or beyond the newest note + if (_newestNoteTimestamp != null && timestamp >= _newestNoteTimestamp!) { + debugPrint("Skipping forward load - timestamp $timestamp is at or beyond newest note timestamp ${_newestNoteTimestamp}"); + return; + } + + // Check if we already have all notes to the newest timestamp + if (_newestNoteTimestamp != null && _noteCache.containsKey(_newestNoteTimestamp)) { + debugPrint("Skipping forward load - already have newest note in cache"); + return; + } + _isCacheLoading = true; try { + // Skip loading if we've reached the newest note + if (_reachedNewestNote) { + debugPrint("Skipping forward load - already at newest note"); + return; + } + // Use the proper cache size as configured _totalPrefetchOperations++; _forwardPrefetchOperations++; @@ -1753,10 +1845,33 @@ class MainPageState extends State with WindowListener { setState(() { for (var note in notes) { _noteCache[note.epochTime] = note; + + // Track the newest note we've seen + if (_newestNoteTimestamp == null || note.epochTime > _newestNoteTimestamp!) { + _newestNoteTimestamp = note.epochTime; + } + } + + // If we got fewer notes than requested, we've likely reached the end + if (notes.length < _cacheSizeAfter) { + _reachedNewestNote = true; + debugPrint("Received fewer notes than requested (${notes.length} < $_cacheSizeAfter), marking as reached newest"); + + // If we got any notes, the newest one is the newest timestamp + if (notes.isNotEmpty) { + final newestInBatch = notes.map((n) => n.epochTime).reduce((a, b) => a > b ? a : b); + if (_newestNoteTimestamp == null || newestInBatch > _newestNoteTimestamp!) { + _newestNoteTimestamp = newestInBatch; + } + } else if (_newestNoteTimestamp == null || timestamp > _newestNoteTimestamp!) { + // If we got no notes, then the timestamp we queried is the newest + _newestNoteTimestamp = timestamp; + } } }); debugPrint("Cached ${notes.length} notes after $timestamp (total prefetch ops: $_totalPrefetchOperations, forward: $_forwardPrefetchOperations)"); + debugPrint("Current newest note timestamp: $_newestNoteTimestamp"); } catch (e) { debugPrint("Error loading notes batch after $timestamp: $e"); } finally { @@ -1882,6 +1997,13 @@ class MainPageState extends State with WindowListener { return note; } + // If we know we've reached the newest note and the timestamp is already + // at or beyond the newest timestamp, don't even try to fetch + if (_newestNoteTimestamp != null && timestamp >= _newestNoteTimestamp!) { + debugPrint("Not fetching next note - already at or beyond newest note timestamp"); + return null; + } + // Not in cache, fetch from server _cacheMisses++; debugPrint("Next note cache miss (Hits: $_cacheHits, Misses: $_cacheMisses)"); @@ -1897,10 +2019,20 @@ class MainPageState extends State with WindowListener { debugPrint("Adding note from server to cache: ${note.epochTime}"); _noteCache[note.epochTime] = note; + // Update newest note timestamp if this is newer + if (_newestNoteTimestamp == null || note.epochTime > _newestNoteTimestamp!) { + _newestNoteTimestamp = note.epochTime; + } + // Don't automatically prefetch here - let the _goToNextNote method decide // This prevents redundant prefetching when we just need one note } else { debugPrint("No next note found on server"); + + // If we didn't find a next note, we've reached the newest + if (_newestNoteTimestamp == null || timestamp > _newestNoteTimestamp!) { + _newestNoteTimestamp = timestamp; + } } return note;