Fix loading notes when approaching newest
This commit is contained in:
158
lib/main.dart
158
lib/main.dart
@@ -331,6 +331,12 @@ class MainPageState extends State<MainPage> with WindowListener {
|
||||
bool _isSearching = false;
|
||||
List<Note> _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;
|
||||
int _pendingNetworkRequests = 0;
|
||||
@@ -606,7 +612,20 @@ class MainPageState extends State<MainPage> 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<MainPage> 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<MainPage> 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<MainPage> 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;
|
||||
// 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;
|
||||
});
|
||||
|
||||
// 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);
|
||||
// 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();
|
||||
@@ -751,6 +818,12 @@ class MainPageState extends State<MainPage> with WindowListener {
|
||||
_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(),
|
||||
operation: "Loading latest scratch",
|
||||
@@ -1738,9 +1811,28 @@ class MainPageState extends State<MainPage> with WindowListener {
|
||||
// Load a batch of notes after the given timestamp
|
||||
Future<void> _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<MainPage> 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<MainPage> 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<MainPage> 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;
|
||||
|
Reference in New Issue
Block a user