diff --git a/mobile/lib/domain/services/timeline.service.dart b/mobile/lib/domain/services/timeline.service.dart index 0d31f06e74..7176ffaae2 100644 --- a/mobile/lib/domain/services/timeline.service.dart +++ b/mobile/lib/domain/services/timeline.service.dart @@ -10,6 +10,7 @@ import 'package:immich_mobile/domain/services/setting.service.dart'; import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart'; import 'package:immich_mobile/utils/async_mutex.dart'; +import 'package:logging/logging.dart'; typedef TimelineAssetSource = Future> Function( int index, @@ -77,6 +78,7 @@ class TimelineService { int _bufferOffset = 0; List _buffer = []; StreamSubscription? _bucketSubscription; + final _log = Logger('TimelineService'); int _totalAssets = 0; int get totalAssets => _totalAssets; @@ -128,10 +130,10 @@ class TimelineService { Stream> Function() get watchBuckets => _bucketSource; - Future> loadAssets(int index, int count) => + Future?> loadAssets(int index, int count) => _mutex.run(() => _loadAssets(index, count)); - Future> _loadAssets(int index, int count) async { + Future?> _loadAssets(int index, int count) async { if (hasRange(index, count)) { return getAssets(index, count); } @@ -169,9 +171,10 @@ class TimelineService { index + count <= _bufferOffset + _buffer.length && index + count <= _totalAssets; - List getAssets(int index, int count) { + List? getAssets(int index, int count) { if (!hasRange(index, count)) { - throw RangeError('TimelineService::getAssets Index out of range'); + _log.warning('TimelineService::getAssets Index out of range'); + return null; } int start = index - _bufferOffset; return _buffer.slice(start, start + count); diff --git a/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart index 7fff6d7d2d..aa6e47d0ca 100644 --- a/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart +++ b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart @@ -107,19 +107,22 @@ class _FixedSegmentRow extends ConsumerWidget { } if (timelineService.hasRange(assetIndex, assetCount)) { - return _buildAssetRow( - context, - timelineService.getAssets(assetIndex, assetCount), - ); + final assets = timelineService.getAssets(assetIndex, assetCount); + if (assets == null) { + return _buildPlaceholder(context); + } + + return _buildAssetRow(context, assets); } - return FutureBuilder>( + return FutureBuilder?>( future: timelineService.loadAssets(assetIndex, assetCount), builder: (context, snapshot) { - if (snapshot.connectionState != ConnectionState.done) { + if (snapshot.connectionState != ConnectionState.done || + snapshot.data == null) { return _buildPlaceholder(context); } - return _buildAssetRow(context, snapshot.requireData); + return _buildAssetRow(context, snapshot.data!); }, ); } diff --git a/mobile/lib/presentation/widgets/timeline/header.widget.dart b/mobile/lib/presentation/widgets/timeline/header.widget.dart index c6c10c26c9..13ad1c04fa 100644 --- a/mobile/lib/presentation/widgets/timeline/header.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/header.widget.dart @@ -112,8 +112,9 @@ class _BulkSelectIconButton extends ConsumerWidget { List bucketAssets; try { bucketAssets = ref - .watch(timelineServiceProvider) - .getAssets(assetOffset, bucket.assetCount); + .watch(timelineServiceProvider) + .getAssets(assetOffset, bucket.assetCount) ?? + []; } catch (e) { bucketAssets = []; } diff --git a/mobile/lib/providers/timeline/multiselect.provider.dart b/mobile/lib/providers/timeline/multiselect.provider.dart index 9e0f690e7d..04357c3ba7 100644 --- a/mobile/lib/providers/timeline/multiselect.provider.dart +++ b/mobile/lib/providers/timeline/multiselect.provider.dart @@ -1,6 +1,5 @@ import 'package:collection/collection.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; - import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/services/timeline.service.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; @@ -130,7 +129,7 @@ class MultiSelectNotifier extends Notifier { final assets = await _timelineService.loadAssets(offset, bucketCount); final selectedAssets = state.selectedAssets.toSet(); - selectedAssets.addAll(assets); + selectedAssets.addAll(assets ?? []); state = state.copyWith( selectedAssets: selectedAssets, @@ -141,14 +140,14 @@ class MultiSelectNotifier extends Notifier { final assets = await _timelineService.loadAssets(offset, bucketCount); final selectedAssets = state.selectedAssets.toSet(); - selectedAssets.removeAll(assets); + selectedAssets.removeAll(assets ?? []); state = state.copyWith(selectedAssets: selectedAssets); } void toggleBucketSelection(int offset, int bucketCount) async { final assets = await _timelineService.loadAssets(offset, bucketCount); - toggleBucketSelectionByAssets(assets); + toggleBucketSelectionByAssets(assets ?? []); } void toggleBucketSelectionByAssets(List bucketAssets) {