Better lazy loading, and improve consistency between pages

pull/28628/head
Victor Chang 2026-04-01 07:25:50 +00:00
parent 23121331dd
commit 4c76cc141f
4 changed files with 39 additions and 4 deletions

View File

@ -1,5 +1,6 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/album/album.model.dart';
@ -34,14 +35,32 @@ class DriftActivitiesPage extends HookConsumerWidget {
scrollToBottom();
}
void loadMoreIfNeeded() {
if (activityNotifier.hasMore && !activityNotifier.isLoadingMore) {
activityNotifier.loadMore();
}
}
void checkIfViewportNotFilled() {
SchedulerBinding.instance.addPostFrameCallback((_) {
if (listViewScrollController.hasClients &&
listViewScrollController.position.maxScrollExtent <= 0) {
loadMoreIfNeeded();
}
});
}
// Auto-load more pages if content doesn't fill the viewport
ref.listen(albumActivityProvider(album.id, assetId), (_, __) {
checkIfViewportNotFilled();
});
useEffect(() {
void onScroll() {
// In a reversed ListView, scrolling toward older items means reaching maxScrollExtent
if (listViewScrollController.position.pixels >=
listViewScrollController.position.maxScrollExtent - 200) {
if (activityNotifier.hasMore && !activityNotifier.isLoadingMore) {
activityNotifier.loadMore();
}
loadMoreIfNeeded();
}
}
listViewScrollController.addListener(onScroll);

View File

@ -48,6 +48,7 @@ export class ActivityRepository {
.$if(!!before, (qb) => qb.where('activity.createdAt', '<', before!))
.$if(!!at, (qb) => qb.where('activity.createdAt', '=', at!))
.orderBy('activity.createdAt', take !== undefined ? 'desc' : 'asc')
.orderBy('activity.id', take !== undefined ? 'desc' : 'asc')
.$if(take !== undefined, (qb) => qb.limit(take!))
.execute();
}

View File

@ -40,7 +40,7 @@ export class ActivityService extends BaseService {
// Fetch all activities at exactly that timestamp and prepend any not already loaded.
if (results.length > 0) {
const boundaryTime = results[0].createdAt;
const loadedIds = new Set(results.filter((a) => +new Date(a.createdAt) === +new Date(boundaryTime)).map((a) => a.id));
const loadedIds = new Set(results.filter((a) => a.createdAt.getTime() === boundaryTime.getTime()).map((a) => a.id));
const extras = await this.activityRepository.search({ ...searchOptions, at: boundaryTime });
const newExtras = extras.map(mapActivity).filter((a) => !loadedIds.has(a.id));
return [...newExtras, ...results];

View File

@ -136,6 +136,21 @@
}
};
// Auto-load more pages if content doesn't fill the scroll container
$effect(() => {
// Track reactive dependencies
void activityManager.activities.length;
void activityManager.isLoadingMore;
if (!scrollContainer || !activityManager.hasMore || activityManager.isLoadingMore) return;
// After rendering, check if there's no scrollable overflow
tick().then(() => {
if (scrollContainer && scrollContainer.scrollHeight <= scrollContainer.clientHeight) {
void loadMoreAndPreserveScroll();
}
});
});
const onsubmit = async (event: Event) => {
event.preventDefault();
await handleSendComment();