Compare commits

...

4 Commits

Author SHA1 Message Date
shenlong-tanwen a8d1c6cec6 rebase changes 2026-05-04 21:33:45 +07:00
shenlong-tanwen ea53d6ac39 fix timeline not updated on orientation change 2026-05-04 21:28:56 +07:00
shenlong-tanwen 4eaac35aa6 enable retries 2026-05-04 21:28:56 +07:00
shenlong-tanwen 7a923659d1 chore: riverpod v2 to v3
# Conflicts:
#	mobile/lib/presentation/widgets/asset_viewer/asset_page.widget.dart
2026-05-04 21:28:55 +07:00
55 changed files with 447 additions and 170 deletions
+3 -3
View File
@@ -45,12 +45,12 @@ analyzer:
- lib/**/*.g.dart - lib/**/*.g.dart
- lib/**/*.drift.dart - lib/**/*.drift.dart
# TODO: Re-enable after upgrading custom_lint
# plugins:
# - custom_lint
errors: errors:
unawaited_futures: warning unawaited_futures: warning
plugins:
riverpod_lint: ^3.1.3
custom_lint: custom_lint:
rules: rules:
- avoid_build_context_in_providers: false - avoid_build_context_in_providers: false
+5 -1
View File
@@ -57,7 +57,11 @@ void main() async {
runApp(ProviderScope(overrides: [driftProvider.overrideWith(driftOverride(drift))], child: const MainWidget())); runApp(ProviderScope(overrides: [driftProvider.overrideWith(driftOverride(drift))], child: const MainWidget()));
} catch (error, stack) { } catch (error, stack) {
runApp(BootstrapErrorWidget(error: error.toString(), stack: stack.toString())); runApp(
ProviderScope(
child: BootstrapErrorWidget(error: error.toString(), stack: stack.toString()),
),
);
} }
} }
@@ -7,12 +7,12 @@ import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/presentation/widgets/images/local_album_thumbnail.widget.dart'; import 'package:immich_mobile/presentation/widgets/images/local_album_thumbnail.widget.dart';
import 'package:immich_mobile/presentation/widgets/images/remote_image_provider.dart';
import 'package:immich_mobile/presentation/widgets/people/partner_user_avatar.widget.dart'; import 'package:immich_mobile/presentation/widgets/people/partner_user_avatar.widget.dart';
import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
import 'package:immich_mobile/providers/infrastructure/partner.provider.dart'; import 'package:immich_mobile/providers/infrastructure/partner.provider.dart';
import 'package:immich_mobile/providers/infrastructure/people.provider.dart'; import 'package:immich_mobile/providers/infrastructure/people.provider.dart';
import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart';
import 'package:immich_mobile/presentation/widgets/images/remote_image_provider.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/utils/image_url_builder.dart'; import 'package:immich_mobile/utils/image_url_builder.dart';
import 'package:immich_mobile/widgets/common/immich_sliver_app_bar.dart'; import 'package:immich_mobile/widgets/common/immich_sliver_app_bar.dart';
@@ -333,7 +333,7 @@ class _QuickAccessButtonList extends ConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final partnerSharedWithAsync = ref.watch(driftSharedWithPartnerProvider); final partnerSharedWithAsync = ref.watch(driftSharedWithPartnerProvider);
final partners = partnerSharedWithAsync.valueOrNull ?? []; final partners = partnerSharedWithAsync.value ?? [];
return SliverPadding( return SliverPadding(
padding: const EdgeInsets.only(left: 16, top: 12, right: 16, bottom: 32), padding: const EdgeInsets.only(left: 16, top: 12, right: 16, bottom: 32),
@@ -639,7 +639,7 @@ class DriftSearchPage extends HookConsumerWidget {
label: 'search_filter_location'.t(context: context), label: 'search_filter_location'.t(context: context),
currentFilter: locationCurrentFilterWidget.value, currentFilter: locationCurrentFilterWidget.value,
), ),
if (userPreferences.valueOrNull?.tagsEnabled ?? false) if (userPreferences.value?.tagsEnabled ?? false)
SearchFilterChip( SearchFilterChip(
icon: Icons.sell_outlined, icon: Icons.sell_outlined,
onTap: showTagPicker, onTap: showTagPicker,
@@ -665,7 +665,7 @@ class DriftSearchPage extends HookConsumerWidget {
label: 'search_filter_media_type'.t(context: context), label: 'search_filter_media_type'.t(context: context),
currentFilter: mediaTypeCurrentFilterWidget.value, currentFilter: mediaTypeCurrentFilterWidget.value,
), ),
if (userPreferences.valueOrNull?.ratingsEnabled ?? false) if (userPreferences.value?.ratingsEnabled ?? false)
SearchFilterChip( SearchFilterChip(
icon: Icons.star_outline_rounded, icon: Icons.star_outline_rounded,
onTap: showStarRatingPicker, onTap: showStarRatingPicker,
@@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:hooks_riverpod/legacy.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/services/search.service.dart'; import 'package:immich_mobile/domain/services/search.service.dart';
import 'package:immich_mobile/models/search/search_filter.model.dart'; import 'package:immich_mobile/models/search/search_filter.model.dart';
@@ -57,6 +57,12 @@ class _AlbumSelectorState extends ConsumerState<AlbumSelector> {
void initState() { void initState() {
super.initState(); super.initState();
ref.listenManual(
remoteAlbumProvider.select((state) => state.albums),
(_, _) => sortAlbums(),
fireImmediately: true,
);
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
final appSettings = ref.read(appSettingsServiceProvider); final appSettings = ref.read(appSettingsServiceProvider);
final savedSortMode = appSettings.getSetting(AppSettingsEnum.selectedAlbumSortOrder); final savedSortMode = appSettings.getSetting(AppSettingsEnum.selectedAlbumSortOrder);
@@ -19,7 +19,7 @@ class AssetDetails extends ConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final exifInfo = ref.watch(assetExifProvider(asset)).valueOrNull; final exifInfo = ref.watch(assetExifProvider(asset)).value;
return Container( return Container(
constraints: BoxConstraints(minHeight: minHeight), constraints: BoxConstraints(minHeight: minHeight),
@@ -14,14 +14,14 @@ import 'package:immich_mobile/extensions/scroll_extensions.dart';
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_details.widget.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_details.widget.dart';
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_stack.provider.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_stack.provider.dart';
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_stack.widget.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_stack.widget.dart';
import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart';
import 'package:immich_mobile/presentation/widgets/asset_viewer/video_viewer.widget.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/video_viewer.widget.dart';
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart'; import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart';
import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart';
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
import 'package:immich_mobile/services/app_settings.service.dart';
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
import 'package:immich_mobile/services/app_settings.service.dart';
import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart'; import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart';
import 'package:immich_mobile/widgets/photo_view/photo_view.dart'; import 'package:immich_mobile/widgets/photo_view/photo_view.dart';
@@ -365,7 +365,7 @@ class _AssetPageState extends ConsumerState<AssetPage> {
BaseAsset displayAsset = asset; BaseAsset displayAsset = asset;
final showAssetStack = ref.watch(timelineServiceProvider.select((s) => s.origin != TimelineOrigin.trash)); final showAssetStack = ref.watch(timelineServiceProvider.select((s) => s.origin != TimelineOrigin.trash));
final stackChildren = showAssetStack ? ref.watch(stackChildrenNotifier(asset)).valueOrNull : null; final stackChildren = showAssetStack ? ref.watch(stackChildrenNotifier(asset)).value : null;
if (stackChildren != null && stackChildren.isNotEmpty) { if (stackChildren != null && stackChildren.isNotEmpty) {
displayAsset = stackChildren.elementAt(stackIndex); displayAsset = stackChildren.elementAt(stackIndex);
} }
@@ -2,9 +2,13 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
class StackChildrenNotifier extends AutoDisposeFamilyAsyncNotifier<List<RemoteAsset>, BaseAsset> { class StackChildrenNotifier extends AsyncNotifier<List<RemoteAsset>> {
final BaseAsset asset;
StackChildrenNotifier(this.asset);
@override @override
Future<List<RemoteAsset>> build(BaseAsset asset) { Future<List<RemoteAsset>> build() {
final asset = this.asset;
if (asset is! RemoteAsset || asset.stackId == null) { if (asset is! RemoteAsset || asset.stackId == null) {
return Future.value(const []); return Future.value(const []);
} }
@@ -5,6 +5,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:hooks_riverpod/misc.dart';
import 'package:immich_mobile/domain/models/album/album.model.dart'; import 'package:immich_mobile/domain/models/album/album.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/events.model.dart'; import 'package:immich_mobile/domain/models/events.model.dart';
@@ -17,9 +18,9 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/download_statu
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_page.widget.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_page.widget.dart';
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_preloader.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_preloader.dart';
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_stack.provider.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_stack.provider.dart';
import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart';
import 'package:immich_mobile/presentation/widgets/asset_viewer/viewer_top_app_bar.widget.dart';
import 'package:immich_mobile/presentation/widgets/asset_viewer/viewer_bottom_app_bar.widget.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/viewer_bottom_app_bar.widget.dart';
import 'package:immich_mobile/presentation/widgets/asset_viewer/viewer_top_app_bar.widget.dart';
import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart';
import 'package:immich_mobile/providers/cast.provider.dart'; import 'package:immich_mobile/providers/cast.provider.dart';
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
@@ -105,6 +106,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
final asset = ref.read(assetViewerProvider).currentAsset; final asset = ref.read(assetViewerProvider).currentAsset;
assert(asset != null, "Current asset should not be null when opening the AssetViewer"); assert(asset != null, "Current asset should not be null when opening the AssetViewer");
// ignore: invalid_use_of_protected_member
if (asset != null) _stackChildrenKeepAlive = ref.read(stackChildrenNotifier(asset).notifier).ref.keepAlive(); if (asset != null) _stackChildrenKeepAlive = ref.read(stackChildrenNotifier(asset).notifier).ref.keepAlive();
_reloadSubscription = EventStream.shared.listen(_onEvent); _reloadSubscription = EventStream.shared.listen(_onEvent);
@@ -161,6 +163,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
_preloader.preload(index, context.sizeData); _preloader.preload(index, context.sizeData);
_handleCasting(); _handleCasting();
_stackChildrenKeepAlive?.close(); _stackChildrenKeepAlive?.close();
// ignore: invalid_use_of_protected_member
_stackChildrenKeepAlive = ref.read(stackChildrenNotifier(asset).notifier).ref.keepAlive(); _stackChildrenKeepAlive = ref.read(stackChildrenNotifier(asset).notifier).ref.keepAlive();
} }
@@ -43,7 +43,7 @@ class _ScopedMapTimeline extends StatelessWidget {
} }
final users = ref.watch(mapStateProvider).withPartners final users = ref.watch(mapStateProvider).withPartners
? ref.watch(timelineUsersProvider).valueOrNull ?? [user.id] ? ref.watch(timelineUsersProvider).value ?? [user.id]
: [user.id]; : [user.id];
final timelineService = ref final timelineService = ref
@@ -1,5 +1,6 @@
import 'dart:math' as math; import 'dart:math' as math;
import 'package:collection/collection.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/setting.model.dart'; import 'package:immich_mobile/domain/models/setting.model.dart';
import 'package:immich_mobile/domain/models/timeline.model.dart'; import 'package:immich_mobile/domain/models/timeline.model.dart';
@@ -48,6 +49,26 @@ class TimelineArgs {
showStorageIndicator.hashCode ^ showStorageIndicator.hashCode ^
withStack.hashCode ^ withStack.hashCode ^
groupBy.hashCode; groupBy.hashCode;
TimelineArgs copyWith({
double? maxWidth,
double? maxHeight,
double? spacing,
int? columnCount,
bool? showStorageIndicator,
bool? withStack,
GroupAssetsBy? groupBy,
}) {
return TimelineArgs(
maxWidth: maxWidth ?? this.maxWidth,
maxHeight: maxHeight ?? this.maxHeight,
spacing: spacing ?? this.spacing,
columnCount: columnCount ?? this.columnCount,
showStorageIndicator: showStorageIndicator ?? this.showStorageIndicator,
withStack: withStack ?? this.withStack,
groupBy: groupBy ?? this.groupBy,
);
}
} }
class TimelineState { class TimelineState {
@@ -86,25 +107,37 @@ class TimelineStateNotifier extends Notifier<TimelineState> {
// This provider watches the buckets from the timeline service & args and serves the segments. // This provider watches the buckets from the timeline service & args and serves the segments.
// It should be used only after the timeline service and timeline args provider is overridden // It should be used only after the timeline service and timeline args provider is overridden
final timelineSegmentProvider = StreamProvider.autoDispose<List<Segment>>((ref) async* { final timelineSegmentProvider = StreamNotifierProvider<_TimelineSegmentNotifier, List<Segment>>(
final args = ref.watch(timelineArgsProvider); _TimelineSegmentNotifier.new,
final columnCount = args.columnCount; dependencies: [timelineServiceProvider, timelineArgsProvider],
final spacing = args.spacing; );
final availableTileWidth = args.maxWidth - (spacing * (columnCount - 1));
final tileExtent = math.max(0, availableTileWidth) / columnCount;
final groupBy = args.groupBy ?? GroupAssetsBy.values[ref.watch(settingsProvider).get(Setting.groupAssetsBy)]; class _TimelineSegmentNotifier extends StreamNotifier<List<Segment>> {
@override
Stream<List<Segment>> build() async* {
final args = ref.watch(timelineArgsProvider);
final columnCount = args.columnCount;
final spacing = args.spacing;
final availableTileWidth = args.maxWidth - (spacing * (columnCount - 1));
final tileExtent = math.max(0, availableTileWidth) / columnCount;
final groupBy = args.groupBy ?? GroupAssetsBy.values[ref.watch(settingsProvider).get(Setting.groupAssetsBy)];
final timelineService = ref.watch(timelineServiceProvider);
yield* timelineService.watchBuckets().map((buckets) {
return FixedSegmentBuilder(
buckets: buckets,
tileHeight: tileExtent,
columnCount: columnCount,
spacing: spacing,
groupBy: groupBy,
).generate();
});
}
final timelineService = ref.watch(timelineServiceProvider); @override
yield* timelineService.watchBuckets().map((buckets) { bool updateShouldNotify(AsyncValue<List<Segment>> previous, AsyncValue<List<Segment>> next) {
return FixedSegmentBuilder( final listEquals = const DeepCollectionEquality().equals;
buckets: buckets, return !listEquals(previous.value, next.value);
tileHeight: tileExtent, }
columnCount: columnCount, }
spacing: spacing,
groupBy: groupBy,
).generate();
});
}, dependencies: [timelineServiceProvider, timelineArgsProvider]);
final timelineStateProvider = NotifierProvider<TimelineStateNotifier, TimelineState>(TimelineStateNotifier.new); final timelineStateProvider = NotifierProvider<TimelineStateNotifier, TimelineState>(TimelineStateNotifier.new);
@@ -71,10 +71,9 @@ class Timeline extends StatelessWidget {
builder: (_, constraints) => ProviderScope( builder: (_, constraints) => ProviderScope(
overrides: [ overrides: [
timelineArgsProvider.overrideWith( timelineArgsProvider.overrideWith(
(ref) => TimelineArgs( () => TimelineArgsNotifier(
maxWidth: constraints.maxWidth, initialMaxWidth: constraints.maxWidth,
maxHeight: constraints.maxHeight, initialMaxHeight: constraints.maxHeight,
columnCount: ref.watch(settingsProvider.select((s) => s.get(Setting.tilesPerRow))),
showStorageIndicator: showStorageIndicator, showStorageIndicator: showStorageIndicator,
withStack: withStack, withStack: withStack,
groupBy: groupBy, groupBy: groupBy,
@@ -92,6 +91,7 @@ class Timeline extends StatelessWidget {
persistentBottomBar: persistentBottomBar, persistentBottomBar: persistentBottomBar,
snapToMonth: snapToMonth, snapToMonth: snapToMonth,
maxWidth: constraints.maxWidth, maxWidth: constraints.maxWidth,
maxHeight: constraints.maxHeight,
loadingWidget: loadingWidget, loadingWidget: loadingWidget,
), ),
), ),
@@ -122,6 +122,7 @@ class _SliverTimeline extends ConsumerStatefulWidget {
this.persistentBottomBar = false, this.persistentBottomBar = false,
this.snapToMonth = true, this.snapToMonth = true,
this.maxWidth, this.maxWidth,
this.maxHeight,
this.loadingWidget, this.loadingWidget,
}); });
@@ -134,6 +135,7 @@ class _SliverTimeline extends ConsumerStatefulWidget {
final bool persistentBottomBar; final bool persistentBottomBar;
final bool snapToMonth; final bool snapToMonth;
final double? maxWidth; final double? maxWidth;
final double? maxHeight;
final Widget? loadingWidget; final Widget? loadingWidget;
@override @override
@@ -172,13 +174,21 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
@override @override
void didUpdateWidget(covariant _SliverTimeline oldWidget) { void didUpdateWidget(covariant _SliverTimeline oldWidget) {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
if (widget.maxWidth != oldWidget.maxWidth) { if (widget.maxWidth != oldWidget.maxWidth || widget.maxHeight != oldWidget.maxHeight) {
final asyncSegments = ref.read(timelineSegmentProvider); final segments = ref.read(timelineSegmentProvider).value;
asyncSegments.whenData((segments) { int? restoreAssetIndex;
final index = _getCurrentAssetIndex(segments); if (segments != null) {
// Refresh to wait for new segments to be generated with the updated width before restoring the scroll position restoreAssetIndex = _getCurrentAssetIndex(segments);
final _ = ref.refresh(timelineArgsProvider); }
_restoreAssetIndex = index; WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
ref
.read(timelineArgsProvider.notifier)
.updateConstraints(maxWidth: widget.maxWidth!, maxHeight: widget.maxHeight!);
final _ = ref.refresh(timelineSegmentProvider);
_restoreAssetIndex = restoreAssetIndex;
_restoreAssetPosition(null);
}
}); });
} }
} }
@@ -200,26 +210,40 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
} }
} }
void _restorePosition(List<Segment> segments) {
final targetSegment = segments.lastWhereOrNull((segment) => segment.firstAssetIndex <= _restoreAssetIndex!);
if (targetSegment == null) {
_restoreAssetIndex = null;
return;
}
final assetIndexInSegment = _restoreAssetIndex! - targetSegment.firstAssetIndex;
final newColumnCount = ref.read(timelineArgsProvider).columnCount;
final rowIndexInSegment = (assetIndexInSegment / newColumnCount).floor();
final targetRowIndex = targetSegment.firstIndex + 1 + rowIndexInSegment;
final targetOffset = targetSegment.indexToLayoutOffset(targetRowIndex);
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted && _scrollController.hasClients) {
_scrollController.jumpTo(targetOffset.clamp(0.0, _scrollController.position.maxScrollExtent));
}
});
_restoreAssetIndex = null;
}
void _restoreAssetPosition(_) { void _restoreAssetPosition(_) {
if (_restoreAssetIndex == null) return; if (_restoreAssetIndex == null) return;
final asyncSegments = ref.read(timelineSegmentProvider); final asyncSegments = ref.read(timelineSegmentProvider);
asyncSegments.whenData((segments) { if (asyncSegments is AsyncData<List<Segment>>) {
final targetSegment = segments.lastWhereOrNull((segment) => segment.firstAssetIndex <= _restoreAssetIndex!); _restorePosition(asyncSegments.value);
if (targetSegment != null) { return;
final assetIndexInSegment = _restoreAssetIndex! - targetSegment.firstAssetIndex; }
final newColumnCount = ref.read(timelineArgsProvider).columnCount; late ProviderSubscription<AsyncValue<List<Segment>>> sub;
final rowIndexInSegment = (assetIndexInSegment / newColumnCount).floor(); sub = ref.listenManual<AsyncValue<List<Segment>>>(timelineSegmentProvider, (_, next) {
final targetRowIndex = targetSegment.firstIndex + 1 + rowIndexInSegment; if (next is AsyncData<List<Segment>>) {
final targetOffset = targetSegment.indexToLayoutOffset(targetRowIndex); sub.close();
WidgetsBinding.instance.addPostFrameCallback((_) { _restorePosition(next.value);
if (mounted) {
_scrollController.jumpTo(targetOffset.clamp(0.0, _scrollController.position.maxScrollExtent));
}
});
} }
}); });
_restoreAssetIndex = null;
} }
void _onMultiSelectionToggled(_, bool isEnabled) { void _onMultiSelectionToggled(_, bool isEnabled) {
+10 -11
View File
@@ -3,20 +3,19 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/models/activities/activity.model.dart'; import 'package:immich_mobile/models/activities/activity.model.dart';
import 'package:immich_mobile/providers/activity_service.provider.dart'; import 'package:immich_mobile/providers/activity_service.provider.dart';
// ignore: unintended_html_in_doc_comment /// Maintains the current list of all activities for [share-album-id, asset]
/// Maintains the current list of all activities for <share-album-id, asset>
final albumActivityProvider = AsyncNotifierProvider.autoDispose final albumActivityProvider = AsyncNotifierProvider.autoDispose
.family<AlbumActivity, List<Activity>, (String albumId, String? assetId)>(AlbumActivity.new); .family<AlbumActivity, List<Activity>, (String, String?)>(AlbumActivity.new);
class AlbumActivity extends AutoDisposeFamilyAsyncNotifier<List<Activity>, (String albumId, String? assetId)> { class AlbumActivity extends AsyncNotifier<List<Activity>> {
late String albumId; final String albumId;
late String? assetId; final String? assetId;
AlbumActivity((String albumId, String? assetId) args) : albumId = args.$1, assetId = args.$2;
@override @override
Future<List<Activity>> build((String albumId, String? assetId) args) async { Future<List<Activity>> build() async {
albumId = args.$1;
assetId = args.$2;
return ref.watch(activityServiceProvider).getAllActivities(albumId, assetId: assetId); return ref.watch(activityServiceProvider).getAllActivities(albumId, assetId: assetId);
} }
@@ -57,7 +56,7 @@ class AlbumActivity extends AutoDisposeFamilyAsyncNotifier<List<Activity>, (Stri
} }
void _addToState(Activity activity) { void _addToState(Activity activity) {
final activities = state.valueOrNull ?? []; final activities = state.value ?? [];
if (activities.any((a) => a.id == activity.id)) { if (activities.any((a) => a.id == activity.id)) {
return; return;
} }
@@ -65,7 +64,7 @@ class AlbumActivity extends AutoDisposeFamilyAsyncNotifier<List<Activity>, (Stri
} }
Activity? _removeFromState(String id) { Activity? _removeFromState(String id) {
final activities = state.valueOrNull; final activities = state.value;
if (activities == null) { if (activities == null) {
return null; return null;
} }
@@ -1,4 +1,4 @@
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/legacy.dart';
class AlbumTitleNotifier extends StateNotifier<String> { class AlbumTitleNotifier extends StateNotifier<String> {
AlbumTitleNotifier() : super(""); AlbumTitleNotifier() : super("");
-26
View File
@@ -1,26 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'current_album.provider.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$currentAlbumHash() => r'61f00273d6b69da45add1532cc3d3a076ee55110';
/// See also [CurrentAlbum].
@ProviderFor(CurrentAlbum)
final currentAlbumProvider =
AutoDisposeNotifierProvider<CurrentAlbum, Album?>.internal(
CurrentAlbum.new,
name: r'currentAlbumProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$currentAlbumHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$CurrentAlbum = AutoDisposeNotifier<Album?>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
@@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:hooks_riverpod/legacy.dart';
import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/services/log.service.dart'; import 'package:immich_mobile/domain/services/log.service.dart';
import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart';
@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/providers/asset_viewer/video_player_provider.dart'; import 'package:immich_mobile/providers/asset_viewer/video_player_provider.dart';
@@ -67,24 +69,41 @@ class AssetViewerState {
} }
class AssetViewerStateNotifier extends Notifier<AssetViewerState> { class AssetViewerStateNotifier extends Notifier<AssetViewerState> {
StreamSubscription<BaseAsset?>? _assetSub;
String? _watchedHeroTag;
@override @override
AssetViewerState build() { AssetViewerState build() {
ref.listen(_watchedCurrentAssetProvider, (_, next) { ref.onDispose(() {
final updated = next.valueOrNull; _assetSub?.cancel();
if (updated != null) { _assetSub = null;
state = state.copyWith(currentAsset: updated); _watchedHeroTag = null;
}
}); });
return const AssetViewerState(); return const AssetViewerState();
} }
void _syncAssetSubscription(BaseAsset? asset) {
final heroTag = asset?.heroTag;
if (heroTag == _watchedHeroTag) return;
_watchedHeroTag = heroTag;
_assetSub?.cancel();
_assetSub = null;
if (asset == null) return;
_assetSub = ref.read(assetServiceProvider).watchAsset(asset).listen((updated) {
if (updated == null || updated.heroTag != _watchedHeroTag) return;
state = state.copyWith(currentAsset: updated);
});
}
void reset() { void reset() {
state = const AssetViewerState(); state = const AssetViewerState();
_syncAssetSubscription(null);
} }
void setAsset(BaseAsset asset) { void setAsset(BaseAsset asset) {
if (asset == state.currentAsset) return; if (asset == state.currentAsset) return;
state = state.copyWith(currentAsset: asset, stackIndex: 0); state = state.copyWith(currentAsset: asset, stackIndex: 0);
_syncAssetSubscription(asset);
} }
void setOpacity(double opacity) { void setOpacity(double opacity) {
@@ -134,10 +153,3 @@ class AssetViewerStateNotifier extends Notifier<AssetViewerState> {
} }
final assetViewerProvider = NotifierProvider<AssetViewerStateNotifier, AssetViewerState>(AssetViewerStateNotifier.new); final assetViewerProvider = NotifierProvider<AssetViewerStateNotifier, AssetViewerState>(AssetViewerStateNotifier.new);
final _watchedCurrentAssetProvider = StreamProvider<BaseAsset?>((ref) {
ref.watch(assetViewerProvider.select((s) => s.currentAsset?.heroTag));
final asset = ref.read(assetViewerProvider).currentAsset;
if (asset == null) return const Stream.empty();
return ref.read(assetServiceProvider).watchAsset(asset);
});
@@ -1,7 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:background_downloader/background_downloader.dart'; import 'package:background_downloader/background_downloader.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/legacy.dart';
import 'package:immich_mobile/models/download/download_state.model.dart'; import 'package:immich_mobile/models/download/download_state.model.dart';
import 'package:immich_mobile/models/download/livephotos_medatada.model.dart'; import 'package:immich_mobile/models/download/livephotos_medatada.model.dart';
import 'package:immich_mobile/services/download.service.dart'; import 'package:immich_mobile/services/download.service.dart';
@@ -1,4 +1,5 @@
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:hooks_riverpod/legacy.dart';
/// Whether to display the video part of a motion photo /// Whether to display the video part of a motion photo
final isPlayingMotionVideoProvider = StateNotifierProvider<IsPlayingMotionVideo, bool>((ref) { final isPlayingMotionVideoProvider = StateNotifierProvider<IsPlayingMotionVideo, bool>((ref) {
@@ -1,10 +1,10 @@
import 'dart:io'; import 'dart:io';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/legacy.dart';
import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart'; import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/services/share_intent_service.dart';
import 'package:immich_mobile/services/foreground_upload.service.dart'; import 'package:immich_mobile/services/foreground_upload.service.dart';
import 'package:immich_mobile/services/share_intent_service.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:path/path.dart' as p; import 'package:path/path.dart' as p;
@@ -1,4 +1,5 @@
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:hooks_riverpod/legacy.dart';
final showControlsProvider = StateNotifierProvider<ShowControls, bool>((ref) { final showControlsProvider = StateNotifierProvider<ShowControls, bool>((ref) {
return ShowControls(ref); return ShowControls(ref);
@@ -1,6 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/legacy.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:native_video_player/native_video_player.dart'; import 'package:native_video_player/native_video_player.dart';
import 'package:wakelock_plus/wakelock_plus.dart'; import 'package:wakelock_plus/wakelock_plus.dart';
+2 -1
View File
@@ -1,5 +1,6 @@
import 'package:flutter_udid/flutter_udid.dart'; import 'package:flutter_udid/flutter_udid.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:hooks_riverpod/legacy.dart';
import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart';
@@ -11,9 +12,9 @@ import 'package:immich_mobile/providers/api.provider.dart';
import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/services/api.service.dart';
import 'package:immich_mobile/services/auth.service.dart'; import 'package:immich_mobile/services/auth.service.dart';
import 'package:immich_mobile/services/background_upload.service.dart';
import 'package:immich_mobile/services/foreground_upload.service.dart'; import 'package:immich_mobile/services/foreground_upload.service.dart';
import 'package:immich_mobile/services/secure_storage.service.dart'; import 'package:immich_mobile/services/secure_storage.service.dart';
import 'package:immich_mobile/services/background_upload.service.dart';
import 'package:immich_mobile/services/widget.service.dart'; import 'package:immich_mobile/services/widget.service.dart';
import 'package:immich_mobile/utils/debug_print.dart'; import 'package:immich_mobile/utils/debug_print.dart';
import 'package:immich_mobile/utils/hash.dart'; import 'package:immich_mobile/utils/hash.dart';
@@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:hooks_riverpod/legacy.dart';
/// Tracks per-asset upload progress. /// Tracks per-asset upload progress.
/// Key: local asset ID, Value: upload progress 0.0 to 1.0, or -1.0 for error /// Key: local asset ID, Value: upload progress 0.0 to 1.0, or -1.0 for error
@@ -1,6 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/legacy.dart';
import 'package:immich_mobile/models/server_info/server_disk_info.model.dart'; import 'package:immich_mobile/models/server_info/server_disk_info.model.dart';
import 'package:immich_mobile/services/server_info.service.dart'; import 'package:immich_mobile/services/server_info.service.dart';
@@ -1,4 +1,4 @@
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/legacy.dart';
import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart';
import 'package:immich_mobile/domain/services/local_album.service.dart'; import 'package:immich_mobile/domain/services/local_album.service.dart';
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
@@ -2,16 +2,16 @@ import 'dart:async';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:logging/logging.dart'; import 'package:hooks_riverpod/legacy.dart';
import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/utils/upload_speed_calculator.dart';
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/services/foreground_upload.service.dart';
import 'package:immich_mobile/services/background_upload.service.dart'; import 'package:immich_mobile/services/background_upload.service.dart';
import 'package:immich_mobile/services/foreground_upload.service.dart';
import 'package:immich_mobile/utils/upload_speed_calculator.dart';
import 'package:logging/logging.dart';
class EnqueueStatus { class EnqueueStatus {
final int enqueueCount; final int enqueueCount;
+1 -1
View File
@@ -1,4 +1,4 @@
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/legacy.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/models/cast/cast_manager_state.dart'; import 'package:immich_mobile/models/cast/cast_manager_state.dart';
import 'package:immich_mobile/services/gcast.service.dart'; import 'package:immich_mobile/services/gcast.service.dart';
+1 -1
View File
@@ -1,4 +1,4 @@
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/legacy.dart';
import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart';
@@ -1,4 +1,5 @@
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:hooks_riverpod/legacy.dart';
import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/models/folder/root_folder.model.dart'; import 'package:immich_mobile/models/folder/root_folder.model.dart';
@@ -1,7 +1,7 @@
import 'dart:io'; import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/legacy.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
class GalleryPermissionNotifier extends StateNotifier<PermissionStatus> { class GalleryPermissionNotifier extends StateNotifier<PermissionStatus> {
@@ -1,5 +1,6 @@
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:hooks_riverpod/legacy.dart';
import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart';
import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/services/app_settings.service.dart';
@@ -16,7 +16,7 @@ final mapServiceProvider = Provider<MapService>(
} }
final users = ref.watch(mapStateProvider).withPartners final users = ref.watch(mapStateProvider).withPartners
? ref.watch(timelineUsersProvider).valueOrNull ?? [user.id] ? ref.watch(timelineUsersProvider).value ?? [user.id]
: [user.id]; : [user.id];
final mapService = ref.watch(mapFactoryProvider).remote(users, ref.watch(mapStateProvider).toOptions()); final mapService = ref.watch(mapFactoryProvider).remote(users, ref.watch(mapStateProvider).toOptions());
@@ -1,4 +1,7 @@
import 'package:collection/collection.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/setting.model.dart';
import 'package:immich_mobile/domain/models/timeline.model.dart';
import 'package:immich_mobile/domain/services/timeline.service.dart'; import 'package:immich_mobile/domain/services/timeline.service.dart';
import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart';
import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart';
@@ -10,13 +13,51 @@ final timelineRepositoryProvider = Provider<DriftTimelineRepository>(
(ref) => DriftTimelineRepository(ref.watch(driftProvider)), (ref) => DriftTimelineRepository(ref.watch(driftProvider)),
); );
final timelineArgsProvider = Provider.autoDispose<TimelineArgs>( final timelineArgsProvider = NotifierProvider.autoDispose<TimelineArgsNotifier, TimelineArgs>(
(ref) => throw UnimplementedError('Will be overridden through a ProviderScope.'), TimelineArgsNotifier.new,
dependencies: const [],
); );
class TimelineArgsNotifier extends Notifier<TimelineArgs> {
TimelineArgsNotifier({
double initialMaxWidth = 0,
double initialMaxHeight = 0,
this.showStorageIndicator = false,
this.withStack = false,
this.groupBy,
}) : _maxWidth = initialMaxWidth,
_maxHeight = initialMaxHeight;
double _maxWidth;
double _maxHeight;
final bool showStorageIndicator;
final bool withStack;
final GroupAssetsBy? groupBy;
@override
TimelineArgs build() {
final columnCount = ref.watch(settingsProvider.select((s) => s.get(Setting.tilesPerRow)));
return TimelineArgs(
maxWidth: _maxWidth,
maxHeight: _maxHeight,
columnCount: columnCount,
showStorageIndicator: showStorageIndicator,
withStack: withStack,
groupBy: groupBy,
);
}
void updateConstraints({required double maxWidth, required double maxHeight}) {
if (_maxWidth == maxWidth && _maxHeight == maxHeight) return;
_maxWidth = maxWidth;
_maxHeight = maxHeight;
state = state.copyWith(maxWidth: maxWidth, maxHeight: maxHeight);
}
}
final timelineServiceProvider = Provider<TimelineService>( final timelineServiceProvider = Provider<TimelineService>(
(ref) { (ref) {
final timelineUsers = ref.watch(timelineUsersProvider).valueOrNull ?? []; final timelineUsers = ref.watch(timelineUsersProvider).value ?? [];
final timelineService = ref.watch(timelineFactoryProvider).main(timelineUsers); final timelineService = ref.watch(timelineFactoryProvider).main(timelineUsers);
ref.onDispose(timelineService.dispose); ref.onDispose(timelineService.dispose);
return timelineService; return timelineService;
@@ -33,11 +74,22 @@ final timelineFactoryProvider = Provider<TimelineFactory>(
), ),
); );
final timelineUsersProvider = StreamProvider<List<String>>((ref) { final timelineUsersProvider = StreamNotifierProvider<_TimelineUsersNotifier, List<String>>(_TimelineUsersNotifier.new);
final currentUserId = ref.watch(currentUserProvider.select((u) => u?.id));
if (currentUserId == null) { class _TimelineUsersNotifier extends StreamNotifier<List<String>> {
return Stream.value([]); @override
Stream<List<String>> build() {
final currentUserId = ref.watch(currentUserProvider.select((u) => u?.id));
if (currentUserId == null) {
return Stream.value([]);
}
return ref.watch(timelineRepositoryProvider).watchTimelineUserIds(currentUserId);
} }
return ref.watch(timelineRepositoryProvider).watchTimelineUserIds(currentUserId); @override
}); bool updateShouldNotify(AsyncValue<List<String>> previous, AsyncValue<List<String>> next) {
final listEquals = const DeepCollectionEquality().equals;
return !listEquals(previous.value, next.value);
}
}
@@ -1,7 +1,7 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/legacy.dart';
import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/models/auth/biometric_status.model.dart'; import 'package:immich_mobile/models/auth/biometric_status.model.dart';
@@ -1,4 +1,4 @@
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/legacy.dart';
final multiselectProvider = StateProvider((ref) { final multiselectProvider = StateProvider((ref) {
return false; return false;
+1 -1
View File
@@ -1,4 +1,4 @@
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/legacy.dart';
import 'package:immich_mobile/services/network.service.dart'; import 'package:immich_mobile/services/network.service.dart';
final networkProvider = StateNotifierProvider<NetworkNotifier, String>((ref) { final networkProvider = StateNotifierProvider<NetworkNotifier, String>((ref) {
@@ -1,6 +1,6 @@
import 'dart:io'; import 'dart:io';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/legacy.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
class NotificationPermissionNotifier extends StateNotifier<PermissionStatus> { class NotificationPermissionNotifier extends StateNotifier<PermissionStatus> {
+1 -1
View File
@@ -1,5 +1,5 @@
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/legacy.dart';
final inLockedViewProvider = StateProvider<bool>((ref) => false); final inLockedViewProvider = StateProvider<bool>((ref) => false);
final currentRouteNameProvider = StateProvider<String?>((ref) => null); final currentRouteNameProvider = StateProvider<String?>((ref) => null);
@@ -1,4 +1,4 @@
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/legacy.dart';
final secureStorageProvider = StateNotifierProvider<SecureStorageProvider, void>((ref) { final secureStorageProvider = StateNotifierProvider<SecureStorageProvider, void>((ref) {
return SecureStorageProvider(); return SecureStorageProvider();
@@ -1,4 +1,5 @@
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:hooks_riverpod/legacy.dart';
import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/models/server_info/server_config.model.dart'; import 'package:immich_mobile/models/server_info/server_config.model.dart';
import 'package:immich_mobile/models/server_info/server_disk_info.model.dart'; import 'package:immich_mobile/models/server_info/server_disk_info.model.dart';
@@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:hooks_riverpod/legacy.dart';
import 'package:immich_mobile/models/shared_link/shared_link.model.dart'; import 'package:immich_mobile/models/shared_link/shared_link.model.dart';
import 'package:immich_mobile/services/shared_link.service.dart'; import 'package:immich_mobile/services/shared_link.service.dart';
+1 -1
View File
@@ -1,4 +1,4 @@
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/legacy.dart';
enum TabEnum { home, search, albums, library } enum TabEnum { home, search, albums, library }
+4 -5
View File
@@ -1,12 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/legacy.dart';
import 'package:immich_mobile/constants/colors.dart'; import 'package:immich_mobile/constants/colors.dart';
import 'package:immich_mobile/theme/color_scheme.dart';
import 'package:immich_mobile/theme/theme_data.dart';
import 'package:immich_mobile/theme/dynamic_theme.dart';
import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart';
import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/services/app_settings.service.dart';
import 'package:immich_mobile/theme/color_scheme.dart';
import 'package:immich_mobile/theme/dynamic_theme.dart';
import 'package:immich_mobile/theme/theme_data.dart';
import 'package:immich_mobile/utils/debug_print.dart'; import 'package:immich_mobile/utils/debug_print.dart';
final immichThemeModeProvider = StateProvider<ThemeMode>((ref) { final immichThemeModeProvider = StateProvider<ThemeMode>((ref) {
@@ -1,6 +1,6 @@
import 'dart:convert'; import 'dart:convert';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/legacy.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:immich_mobile/domain/services/user.service.dart'; import 'package:immich_mobile/domain/services/user.service.dart';
import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
+1 -1
View File
@@ -1,6 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/legacy.dart';
import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/domain/services/user.service.dart'; import 'package:immich_mobile/domain/services/user.service.dart';
import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
@@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:hooks_riverpod/legacy.dart';
import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/infrastructure/repositories/network.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/network.repository.dart';
@@ -25,6 +25,7 @@ final deepLinkServiceProvider = Provider(
ref.watch(driftPeopleServiceProvider), ref.watch(driftPeopleServiceProvider),
ref.watch(currentUserProvider), ref.watch(currentUserProvider),
), ),
dependencies: [remoteAlbumServiceProvider],
); );
class DeepLinkService { class DeepLinkService {
@@ -3,12 +3,13 @@ import 'dart:math';
import 'package:async/async.dart'; import 'package:async/async.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:hooks_riverpod/legacy.dart';
import 'package:immich_mobile/constants/colors.dart'; import 'package:immich_mobile/constants/colors.dart';
import 'package:immich_mobile/extensions/duration_extensions.dart';
import 'package:immich_mobile/models/cast/cast_manager_state.dart'; import 'package:immich_mobile/models/cast/cast_manager_state.dart';
import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart';
import 'package:immich_mobile/providers/asset_viewer/video_player_provider.dart'; import 'package:immich_mobile/providers/asset_viewer/video_player_provider.dart';
import 'package:immich_mobile/providers/cast.provider.dart'; import 'package:immich_mobile/providers/cast.provider.dart';
import 'package:immich_mobile/extensions/duration_extensions.dart';
import 'package:immich_mobile/widgets/asset_viewer/animated_play_pause.dart'; import 'package:immich_mobile/widgets/asset_viewer/animated_play_pause.dart';
class VideoControls extends ConsumerStatefulWidget { class VideoControls extends ConsumerStatefulWidget {
@@ -25,7 +26,7 @@ class VideoControls extends ConsumerStatefulWidget {
class _VideoControlsState extends ConsumerState<VideoControls> { class _VideoControlsState extends ConsumerState<VideoControls> {
late final RestartableTimer _hideTimer; late final RestartableTimer _hideTimer;
AutoDisposeStateNotifierProvider<VideoPlayerNotifier, VideoPlayerState> get _provider => StateNotifierProvider<VideoPlayerNotifier, VideoPlayerState> get _provider =>
videoPlayerProvider(widget.videoPlayerName); videoPlayerProvider(widget.videoPlayerName);
@override @override
@@ -5,10 +5,10 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/providers/theme.provider.dart'; import 'package:immich_mobile/providers/theme.provider.dart';
import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/services/app_settings.service.dart';
import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart';
import 'package:immich_mobile/widgets/settings/preference_settings/primary_color_setting.dart'; import 'package:immich_mobile/widgets/settings/preference_settings/primary_color_setting.dart';
import 'package:immich_mobile/widgets/settings/setting_group_title.dart'; import 'package:immich_mobile/widgets/settings/setting_group_title.dart';
import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart';
import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart';
class ThemeSetting extends HookConsumerWidget { class ThemeSetting extends HookConsumerWidget {
const ThemeSetting({super.key}); const ThemeSetting({super.key});
@@ -21,7 +21,8 @@ class ThemeSetting extends HookConsumerWidget {
final isSystemTheme = useValueNotifier(currentTheme.value == ThemeMode.system); final isSystemTheme = useValueNotifier(currentTheme.value == ThemeMode.system);
final applyThemeToBackgroundSetting = useAppSettingsState(AppSettingsEnum.colorfulInterface); final applyThemeToBackgroundSetting = useAppSettingsState(AppSettingsEnum.colorfulInterface);
final applyThemeToBackgroundProvider = useValueNotifier(ref.read(colorfulInterfaceSettingProvider)); final isColorfulInterface = ref.read(colorfulInterfaceSettingProvider);
final applyThemeToBackgroundProvider = useValueNotifier(isColorfulInterface);
useValueChanged( useValueChanged(
currentThemeString.value, currentThemeString.value,
+144 -6
View File
@@ -9,6 +9,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "93.0.0" version: "93.0.0"
analysis_server_plugin:
dependency: transitive
description:
name: analysis_server_plugin
sha256: "5f3920acbd5765764ec9ef6c5bbdd102015424281232ee4fb4f5431c87abb4eb"
url: "https://pub.dev"
source: hosted
version: "0.3.7"
analyzer: analyzer:
dependency: transitive dependency: transitive
description: description:
@@ -17,6 +25,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "10.0.1" version: "10.0.1"
analyzer_buffer:
dependency: transitive
description:
name: analyzer_buffer
sha256: "5fcd06b0715ebeee99f03e3f437b3412249969d8d12b191ea8a1d76e42a4e4a1"
url: "https://pub.dev"
source: hosted
version: "0.3.1"
analyzer_plugin:
dependency: transitive
description:
name: analyzer_plugin
sha256: "7df504f0c9d6891bacc9f73a5a8c5f6fe4fc49c90ec8e3379916372906ba0b32"
url: "https://pub.dev"
source: hosted
version: "0.14.1"
ansicolor: ansicolor:
dependency: transitive dependency: transitive
description: description:
@@ -209,6 +233,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.4" version: "2.0.4"
cli_config:
dependency: transitive
description:
name: cli_config
sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec
url: "https://pub.dev"
source: hosted
version: "0.2.0"
cli_util: cli_util:
dependency: transitive dependency: transitive
description: description:
@@ -273,6 +305,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.2" version: "3.1.2"
coverage:
dependency: transitive
description:
name: coverage
sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d"
url: "https://pub.dev"
source: hosted
version: "1.15.0"
crop_image: crop_image:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -557,10 +597,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: flutter_riverpod name: flutter_riverpod
sha256: "9532ee6db4a943a1ed8383072a2e3eeda041db5657cdf6d2acecf3c21ecbe7e1" sha256: "4e166be88e1dbbaa34a280bdb744aeae73b7ef25fdf8db7a3bb776760a3648e2"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.6.1" version: "3.3.1"
flutter_secure_storage: flutter_secure_storage:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -659,6 +699,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.2.14" version: "8.2.14"
freezed_annotation:
dependency: transitive
description:
name: freezed_annotation
sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
frontend_server_client: frontend_server_client:
dependency: transitive dependency: transitive
description: description:
@@ -780,10 +828,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: hooks_riverpod name: hooks_riverpod
sha256: "70bba33cfc5670c84b796e6929c54b8bc5be7d0fe15bb28c2560500b9ad06966" sha256: "08527ec06aaef75e4b78694e045ef0cd8346594eaf9cc18b0f866398b07b93b1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.6.1" version: "3.3.1"
hotreloader: hotreloader:
dependency: transitive dependency: transitive
description: description:
@@ -1149,6 +1197,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.5.0" version: "0.5.0"
node_preamble:
dependency: transitive
description:
name: node_preamble
sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
objective_c: objective_c:
dependency: transitive dependency: transitive
description: description:
@@ -1433,10 +1489,28 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: riverpod name: riverpod
sha256: "59062512288d3056b2321804332a13ffdd1bf16df70dcc8e506e411280a72959" sha256: "8c22216be8ad3ef2b44af3a329693558c98eca7b8bd4ef495c92db0bba279f83"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.6.1" version: "3.2.1"
riverpod_analyzer_utils:
dependency: "direct overridden"
description:
path: "packages/riverpod_analyzer_utils"
ref: e8b84952e40b395ef47ab1f581eddddf64f0b2fd
resolved-ref: e8b84952e40b395ef47ab1f581eddddf64f0b2fd
url: "https://github.com/rrousselGit/riverpod/"
source: git
version: "1.0.0-dev.9"
riverpod_lint:
dependency: "direct dev"
description:
path: "packages/riverpod_lint"
ref: e8b84952e40b395ef47ab1f581eddddf64f0b2fd
resolved-ref: e8b84952e40b395ef47ab1f581eddddf64f0b2fd
url: "https://github.com/rrousselGit/riverpod/"
source: git
version: "3.1.3"
scroll_date_picker: scroll_date_picker:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -1565,6 +1639,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.2" version: "1.4.2"
shelf_packages_handler:
dependency: transitive
description:
name: shelf_packages_handler
sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
shelf_static:
dependency: transitive
description:
name: shelf_static
sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3
url: "https://pub.dev"
source: hosted
version: "1.1.3"
shelf_web_socket: shelf_web_socket:
dependency: transitive dependency: transitive
description: description:
@@ -1611,6 +1701,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.2.2" version: "4.2.2"
source_map_stack_trace:
dependency: transitive
description:
name: source_map_stack_trace
sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b
url: "https://pub.dev"
source: hosted
version: "2.1.2"
source_maps:
dependency: transitive
description:
name: source_maps
sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812"
url: "https://pub.dev"
source: hosted
version: "0.10.13"
source_span: source_span:
dependency: transitive dependency: transitive
description: description:
@@ -1707,6 +1813,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.2" version: "1.2.2"
test:
dependency: transitive
description:
name: test
sha256: "280d6d890011ca966ad08df7e8a4ddfab0fb3aa49f96ed6de56e3521347a9ae7"
url: "https://pub.dev"
source: hosted
version: "1.30.0"
test_api: test_api:
dependency: transitive dependency: transitive
description: description:
@@ -1715,6 +1829,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.10" version: "0.7.10"
test_core:
dependency: transitive
description:
name: test_core
sha256: "0381bd1585d1a924763c308100f2138205252fb90c9d4eeaf28489ee65ccde51"
url: "https://pub.dev"
source: hosted
version: "0.6.16"
thumbhash: thumbhash:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -1923,6 +2045,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.0" version: "3.1.0"
webkit_inspection_protocol:
dependency: transitive
description:
name: webkit_inspection_protocol
sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572"
url: "https://pub.dev"
source: hosted
version: "1.2.1"
win32: win32:
dependency: transitive dependency: transitive
description: description:
@@ -1987,6 +2117,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.3" version: "3.1.3"
yaml_edit:
dependency: transitive
description:
name: yaml_edit
sha256: "07c9e63ba42519745182b88ca12264a7ba2484d8239958778dfe4d44fe760488"
url: "https://pub.dev"
source: hosted
version: "2.2.4"
sdks: sdks:
dart: ">=3.11.0 <4.0.0" dart: ">=3.11.0 <4.0.0"
flutter: "3.41.9" flutter: "3.41.9"
+19 -6
View File
@@ -35,7 +35,7 @@ dependencies:
fluttertoast: ^8.2.14 fluttertoast: ^8.2.14
geolocator: ^14.0.2 geolocator: ^14.0.2
home_widget: ^0.8.1 home_widget: ^0.8.1
hooks_riverpod: ^2.6.1 hooks_riverpod: ^3.3.1
http: ^1.6.0 http: ^1.6.0
image_picker: ^1.2.1 image_picker: ^1.2.1
immich_ui: immich_ui:
@@ -91,10 +91,9 @@ dependencies:
dev_dependencies: dev_dependencies:
auto_route_generator: ^10.5.0 auto_route_generator: ^10.5.0
build_runner: ^2.13.1 build_runner: ^2.13.1
# Drift generator
drift_dev: ^2.32.1 drift_dev: ^2.32.1
fake_async: ^1.3.3 fake_async: ^1.3.3
file: ^7.0.1 # for MemoryFileSystem file: ^7.0.1
flutter_launcher_icons: ^0.14.4 flutter_launcher_icons: ^0.14.4
flutter_lints: ^5.0.0 flutter_lints: ^5.0.0
flutter_native_splash: ^2.4.7 flutter_native_splash: ^2.4.7
@@ -103,13 +102,27 @@ dev_dependencies:
integration_test: integration_test:
sdk: flutter sdk: flutter
mocktail: ^1.0.5 mocktail: ^1.0.5
# Type safe platform code
pigeon: ^26.3.4 pigeon: ^26.3.4
riverpod_lint: ^3.1.3
# cast 2.1.0 declares a loose bonsoir range but its code targets the 5.x API.
# Pin bonsoir to 5.x until cast releases a version compatible with bonsoir 6.x.
dependency_overrides: dependency_overrides:
# cast 2.1.0 declares a loose bonsoir range but its code targets the 5.x API.
# Pin bonsoir to 5.x until cast releases a version compatible with bonsoir 6.x.
bonsoir: ^5.1.11 bonsoir: ^5.1.11
# the pub version has an outdated analyzer dependency, and the git version is not published to pub.dev
# use the git version until the pub version is updated
riverpod_lint:
git:
url: https://github.com/rrousselGit/riverpod/
ref: 'e8b84952e40b395ef47ab1f581eddddf64f0b2fd'
path: packages/riverpod_lint/
# transitive dependency of riverpod_lint, and the pub version is outdated
# remove the override when the pub version of riverpod_lint is updated
riverpod_analyzer_utils:
git:
url: https://github.com/rrousselGit/riverpod/
ref: 'e8b84952e40b395ef47ab1f581eddddf64f0b2fd'
path: packages/riverpod_analyzer_utils/
flutter: flutter:
uses-material-design: true uses-material-design: true
@@ -7,6 +7,7 @@ import 'package:drift/native.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:hooks_riverpod/misc.dart';
import 'package:immich_mobile/domain/services/store.service.dart'; import 'package:immich_mobile/domain/services/store.service.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
@@ -54,7 +55,7 @@ void main() {
mapStateNotifier.state = mapState.copyWith(darkStyleFetched: const AsyncData("dark")); mapStateNotifier.state = mapState.copyWith(darkStyleFetched: const AsyncData("dark"));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(mapStyle?.valueOrNull, "dark"); expect(mapStyle?.value, "dark");
}); });
testWidgets("Return error when style is not fetched", (tester) async { testWidgets("Return error when style is not fetched", (tester) async {
@@ -88,7 +89,7 @@ void main() {
mapStateNotifier.state = mapState.copyWith(themeMode: ThemeMode.light, lightStyleFetched: const AsyncData("light")); mapStateNotifier.state = mapState.copyWith(themeMode: ThemeMode.light, lightStyleFetched: const AsyncData("light"));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(mapStyle?.valueOrNull, "light"); expect(mapStyle?.value, "light");
}); });
group("System mode", () { group("System mode", () {
@@ -111,7 +112,7 @@ void main() {
); );
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(mapStyle?.valueOrNull, "dark"); expect(mapStyle?.value, "dark");
}); });
testWidgets("Return light theme style when system is light", (tester) async { testWidgets("Return light theme style when system is light", (tester) async {
@@ -133,7 +134,7 @@ void main() {
); );
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(mapStyle?.valueOrNull, "light"); expect(mapStyle?.value, "light");
}); });
testWidgets("Switches style when system brightness changes", (tester) async { testWidgets("Switches style when system brightness changes", (tester) async {
@@ -155,11 +156,11 @@ void main() {
darkStyleFetched: const AsyncData("dark"), darkStyleFetched: const AsyncData("dark"),
); );
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(mapStyle?.valueOrNull, "light"); expect(mapStyle?.value, "light");
tester.binding.platformDispatcher.platformBrightnessTestValue = Brightness.dark; tester.binding.platformDispatcher.platformBrightnessTestValue = Brightness.dark;
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(mapStyle?.valueOrNull, "dark"); expect(mapStyle?.value, "dark");
}); });
}); });
} }
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:hooks_riverpod/misc.dart';
extension PumpConsumerWidget on WidgetTester { extension PumpConsumerWidget on WidgetTester {
/// Wraps the provided [widget] with Material app such that it becomes: /// Wraps the provided [widget] with Material app such that it becomes: