Use debouncer for home, owned and shared albums tabs and gallery. (#1554)
This commit is contained in:
commit
bdac20a711
|
@ -37,8 +37,8 @@ class _AllSectionsExamplesProviderState
|
|||
final _logger = Logger("AllSectionsExamplesProvider");
|
||||
|
||||
final _debouncer = Debouncer(
|
||||
const Duration(seconds: 4),
|
||||
executionIntervalInMilliSeconds: 15000,
|
||||
const Duration(seconds: 3),
|
||||
executionInterval: const Duration(seconds: 12),
|
||||
);
|
||||
|
||||
@override
|
||||
|
|
|
@ -12,6 +12,7 @@ import 'package:photos/models/device_collection.dart';
|
|||
import "package:photos/ui/collections/device/device_folder_item.dart";
|
||||
import 'package:photos/ui/common/loading_widget.dart';
|
||||
import 'package:photos/ui/viewer/gallery/empty_state.dart';
|
||||
import "package:photos/utils/debouncer.dart";
|
||||
|
||||
class DeviceFoldersGridView extends StatefulWidget {
|
||||
const DeviceFoldersGridView({
|
||||
|
@ -27,6 +28,10 @@ class _DeviceFoldersGridViewState extends State<DeviceFoldersGridView> {
|
|||
StreamSubscription<LocalPhotosUpdatedEvent>? _localFilesSubscription;
|
||||
String _loadReason = "init";
|
||||
final _logger = Logger((_DeviceFoldersGridViewState).toString());
|
||||
final _debouncer = Debouncer(
|
||||
const Duration(seconds: 2),
|
||||
executionInterval: const Duration(seconds: 5),
|
||||
);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -39,8 +44,12 @@ class _DeviceFoldersGridViewState extends State<DeviceFoldersGridView> {
|
|||
});
|
||||
_localFilesSubscription =
|
||||
Bus.instance.on<LocalPhotosUpdatedEvent>().listen((event) {
|
||||
_loadReason = event.reason;
|
||||
setState(() {});
|
||||
_debouncer.run(() async {
|
||||
if (mounted) {
|
||||
_loadReason = event.reason;
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
super.initState();
|
||||
|
@ -93,6 +102,7 @@ class _DeviceFoldersGridViewState extends State<DeviceFoldersGridView> {
|
|||
void dispose() {
|
||||
_backupFoldersUpdatedEvent?.cancel();
|
||||
_localFilesSubscription?.cancel();
|
||||
_debouncer.cancelDebounce();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import 'package:photos/models/device_collection.dart';
|
|||
import "package:photos/ui/collections/device/device_folder_item.dart";
|
||||
import 'package:photos/ui/common/loading_widget.dart';
|
||||
import 'package:photos/ui/viewer/gallery/empty_state.dart';
|
||||
import "package:photos/utils/debouncer.dart";
|
||||
|
||||
class DeviceFolderVerticalGridView extends StatelessWidget {
|
||||
final Widget? appTitle;
|
||||
|
@ -58,6 +59,10 @@ class _DeviceFolderVerticalGridViewBodyState
|
|||
StreamSubscription<LocalPhotosUpdatedEvent>? _localFilesSubscription;
|
||||
String _loadReason = "init";
|
||||
final logger = Logger((_DeviceFolderVerticalGridViewBodyState).toString());
|
||||
final _debouncer = Debouncer(
|
||||
const Duration(milliseconds: 1500),
|
||||
executionInterval: const Duration(seconds: 4),
|
||||
);
|
||||
/*
|
||||
Aspect ratio 1:1 Max width 224 Fixed gap 8
|
||||
Width changes dynamically with screen width such that we can fit 2 in one row.
|
||||
|
@ -78,8 +83,12 @@ class _DeviceFolderVerticalGridViewBodyState
|
|||
});
|
||||
_localFilesSubscription =
|
||||
Bus.instance.on<LocalPhotosUpdatedEvent>().listen((event) {
|
||||
_loadReason = event.reason;
|
||||
setState(() {});
|
||||
_debouncer.run(() async {
|
||||
if (mounted) {
|
||||
_loadReason = event.reason;
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
@ -149,6 +158,7 @@ class _DeviceFolderVerticalGridViewBodyState
|
|||
void dispose() {
|
||||
_backupFoldersUpdatedEvent?.cancel();
|
||||
_localFilesSubscription?.cancel();
|
||||
_debouncer.cancelDebounce();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,6 +81,8 @@ class HomeGalleryWidget extends StatelessWidget {
|
|||
footer: footer,
|
||||
// scrollSafe area -> SafeArea + Preserver more + Nav Bar buttons
|
||||
scrollBottomSafeArea: bottomSafeArea + 180,
|
||||
reloadDebounceTime: const Duration(seconds: 2),
|
||||
reloadDebounceExecutionInterval: const Duration(seconds: 5),
|
||||
);
|
||||
return Stack(
|
||||
alignment: Alignment.bottomCenter,
|
||||
|
|
|
@ -42,7 +42,7 @@ class _MapViewState extends State<MapView> {
|
|||
late List<Marker> _markers;
|
||||
final _debouncer = Debouncer(
|
||||
const Duration(milliseconds: 300),
|
||||
executionIntervalInMilliSeconds: 750,
|
||||
executionInterval: const Duration(milliseconds: 750),
|
||||
);
|
||||
|
||||
@override
|
||||
|
|
|
@ -19,6 +19,7 @@ import "package:photos/ui/components/models/button_type.dart";
|
|||
import 'package:photos/ui/tabs/section_title.dart';
|
||||
import "package:photos/ui/tabs/shared/empty_state.dart";
|
||||
import "package:photos/ui/tabs/shared/quick_link_album_item.dart";
|
||||
import "package:photos/utils/debouncer.dart";
|
||||
import "package:photos/utils/navigation_util.dart";
|
||||
import "package:photos/utils/share_util.dart";
|
||||
|
||||
|
@ -36,18 +37,31 @@ class _SharedCollectionsTabState extends State<SharedCollectionsTab>
|
|||
late StreamSubscription<CollectionUpdatedEvent>
|
||||
_collectionUpdatesSubscription;
|
||||
late StreamSubscription<UserLoggedOutEvent> _loggedOutEvent;
|
||||
final _debouncer = Debouncer(
|
||||
const Duration(seconds: 2),
|
||||
executionInterval: const Duration(seconds: 5),
|
||||
);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_localFilesSubscription =
|
||||
Bus.instance.on<LocalPhotosUpdatedEvent>().listen((event) {
|
||||
debugPrint("SetState Shared Collections on ${event.reason}");
|
||||
setState(() {});
|
||||
_debouncer.run(() async {
|
||||
if (mounted) {
|
||||
debugPrint("SetState Shared Collections on ${event.reason}");
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
});
|
||||
_collectionUpdatesSubscription =
|
||||
Bus.instance.on<CollectionUpdatedEvent>().listen((event) {
|
||||
setState(() {});
|
||||
_debouncer.run(() async {
|
||||
if (mounted) {
|
||||
debugPrint("SetState Shared Collections on ${event.reason}");
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
});
|
||||
_loggedOutEvent = Bus.instance.on<UserLoggedOutEvent>().listen((event) {
|
||||
setState(() {});
|
||||
|
@ -262,6 +276,7 @@ class _SharedCollectionsTabState extends State<SharedCollectionsTab>
|
|||
_localFilesSubscription.cancel();
|
||||
_collectionUpdatesSubscription.cancel();
|
||||
_loggedOutEvent.cancel();
|
||||
_debouncer.cancelDebounce();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import 'package:photos/ui/components/buttons/icon_button_widget.dart';
|
|||
import "package:photos/ui/tabs/section_title.dart";
|
||||
import "package:photos/ui/viewer/actions/delete_empty_albums.dart";
|
||||
import "package:photos/ui/viewer/gallery/empty_state.dart";
|
||||
import "package:photos/utils/debouncer.dart";
|
||||
import 'package:photos/utils/local_settings.dart';
|
||||
import "package:photos/utils/navigation_util.dart";
|
||||
|
||||
|
@ -44,19 +45,31 @@ class _UserCollectionsTabState extends State<UserCollectionsTab>
|
|||
AlbumSortKey? sortKey;
|
||||
String _loadReason = "init";
|
||||
final _scrollController = ScrollController();
|
||||
final _debouncer = Debouncer(
|
||||
const Duration(seconds: 2),
|
||||
executionInterval: const Duration(seconds: 5),
|
||||
);
|
||||
|
||||
static const int _kOnEnteItemLimitCount = 10;
|
||||
@override
|
||||
void initState() {
|
||||
_localFilesSubscription =
|
||||
Bus.instance.on<LocalPhotosUpdatedEvent>().listen((event) {
|
||||
_loadReason = event.reason;
|
||||
setState(() {});
|
||||
_debouncer.run(() async {
|
||||
if (mounted) {
|
||||
_loadReason = event.reason;
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
});
|
||||
_collectionUpdatesSubscription =
|
||||
Bus.instance.on<CollectionUpdatedEvent>().listen((event) {
|
||||
_loadReason = event.reason;
|
||||
setState(() {});
|
||||
_debouncer.run(() async {
|
||||
if (mounted) {
|
||||
_loadReason = event.reason;
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
});
|
||||
_loggedOutEvent = Bus.instance.on<UserLoggedOutEvent>().listen((event) {
|
||||
_loadReason = event.reason;
|
||||
|
@ -268,6 +281,7 @@ class _UserCollectionsTabState extends State<UserCollectionsTab>
|
|||
_collectionUpdatesSubscription.cancel();
|
||||
_loggedOutEvent.cancel();
|
||||
_scrollController.dispose();
|
||||
_debouncer.cancelDebounce();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import "package:photos/ui/viewer/gallery/component/multiple_groups_gallery_view.
|
|||
import 'package:photos/ui/viewer/gallery/empty_state.dart';
|
||||
import "package:photos/ui/viewer/gallery/state/gallery_context_state.dart";
|
||||
import 'package:photos/utils/date_time_util.dart';
|
||||
import "package:photos/utils/debouncer.dart";
|
||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||
|
||||
typedef GalleryLoader = Future<FileLoadResult> Function(
|
||||
|
@ -43,6 +44,8 @@ class Gallery extends StatefulWidget {
|
|||
final bool enableFileGrouping;
|
||||
final Widget loadingWidget;
|
||||
final bool disableScroll;
|
||||
final Duration reloadDebounceTime;
|
||||
final Duration reloadDebounceExecutionInterval;
|
||||
|
||||
/// When true, selection will be limited to one item. Tapping on any item
|
||||
/// will select even when no other item is selected.
|
||||
|
@ -78,6 +81,8 @@ class Gallery extends StatefulWidget {
|
|||
this.sortAsyncFn,
|
||||
this.showSelectAllByDefault = true,
|
||||
this.isScrollablePositionedList = true,
|
||||
this.reloadDebounceTime = const Duration(milliseconds: 500),
|
||||
this.reloadDebounceExecutionInterval = const Duration(seconds: 2),
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
|
@ -89,6 +94,7 @@ class Gallery extends StatefulWidget {
|
|||
|
||||
class GalleryState extends State<Gallery> {
|
||||
static const int kInitialLoadLimit = 100;
|
||||
late final Debouncer _debouncer;
|
||||
|
||||
late Logger _logger;
|
||||
List<List<EnteFile>> currentGroupedFiles = [];
|
||||
|
@ -106,20 +112,26 @@ class GalleryState extends State<Gallery> {
|
|||
"Gallery_${widget.tagPrefix}${kDebugMode ? "_" + widget.albumName! : ""}";
|
||||
_logger = Logger(_logTag);
|
||||
_logger.finest("init Gallery");
|
||||
_debouncer = Debouncer(
|
||||
widget.reloadDebounceTime,
|
||||
executionInterval: widget.reloadDebounceExecutionInterval,
|
||||
);
|
||||
_sortOrderAsc = widget.sortAsyncFn != null ? widget.sortAsyncFn!() : false;
|
||||
_itemScroller = ItemScrollController();
|
||||
if (widget.reloadEvent != null) {
|
||||
_reloadEventSubscription = widget.reloadEvent!.listen((event) async {
|
||||
// In soft refresh, setState is called for entire gallery only when
|
||||
// number of child change
|
||||
_logger.finest("Soft refresh all files on ${event.reason} ");
|
||||
final result = await _loadFiles();
|
||||
final bool hasReloaded = _onFilesLoaded(result.files);
|
||||
if (hasReloaded && kDebugMode) {
|
||||
_logger.finest(
|
||||
"Reloaded gallery on soft refresh all files on ${event.reason}",
|
||||
);
|
||||
}
|
||||
_debouncer.run(() async {
|
||||
// In soft refresh, setState is called for entire gallery only when
|
||||
// number of child change
|
||||
_logger.finest("Soft refresh all files on ${event.reason} ");
|
||||
final result = await _loadFiles();
|
||||
final bool hasReloaded = _onFilesLoaded(result.files);
|
||||
if (hasReloaded && kDebugMode) {
|
||||
_logger.finest(
|
||||
"Reloaded gallery on soft refresh all files on ${event.reason}",
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
_tabDoubleTapEvent =
|
||||
|
@ -137,11 +149,13 @@ class GalleryState extends State<Gallery> {
|
|||
for (final event in widget.forceReloadEvents!) {
|
||||
_forceReloadEventSubscriptions.add(
|
||||
event.listen((event) async {
|
||||
_logger.finest("Force refresh all files on ${event.reason}");
|
||||
_sortOrderAsc =
|
||||
widget.sortAsyncFn != null ? widget.sortAsyncFn!() : false;
|
||||
final result = await _loadFiles();
|
||||
_setFilesAndReload(result.files);
|
||||
_debouncer.run(() async {
|
||||
_logger.finest("Force refresh all files on ${event.reason}");
|
||||
_sortOrderAsc =
|
||||
widget.sortAsyncFn != null ? widget.sortAsyncFn!() : false;
|
||||
final result = await _loadFiles();
|
||||
_setFilesAndReload(result.files);
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
@ -219,6 +233,7 @@ class GalleryState extends State<Gallery> {
|
|||
for (final subscription in _forceReloadEventSubscriptions) {
|
||||
subscription.cancel();
|
||||
}
|
||||
_debouncer.cancelDebounce();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
|
@ -12,19 +12,19 @@ class Debouncer {
|
|||
/// If executionIntervalInSeconds is not null, then the debouncer will execute the
|
||||
/// current callback it has in run() method repeatedly in the given interval.
|
||||
/// This is useful for example when you want to execute a callback every 5 seconds
|
||||
final int? executionIntervalInMilliSeconds;
|
||||
final Duration? executionInterval;
|
||||
Timer? _debounceTimer;
|
||||
|
||||
Debouncer(this._duration, {this.executionIntervalInMilliSeconds});
|
||||
Debouncer(this._duration, {this.executionInterval});
|
||||
|
||||
final Stopwatch _stopwatch = Stopwatch();
|
||||
|
||||
void run(FutureVoidCallback fn) {
|
||||
bool shouldRunImmediately = false;
|
||||
if (executionIntervalInMilliSeconds != null) {
|
||||
if (executionInterval != null) {
|
||||
// ensure the stop watch is running
|
||||
_stopwatch.start();
|
||||
if (_stopwatch.elapsedMilliseconds > executionIntervalInMilliSeconds!) {
|
||||
if (_stopwatch.elapsedMilliseconds > executionInterval!.inMilliseconds) {
|
||||
shouldRunImmediately = true;
|
||||
_stopwatch.stop();
|
||||
_stopwatch.reset();
|
||||
|
|
Loading…
Reference in a new issue