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