refactor(device_management): migrate background_image to Riverpod
This commit is contained in:
@@ -1,15 +1,16 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:legacy_theme/legacy_theme.dart';
|
||||
import 'package:device_management/src/features/background_image/presentation/providers/background_image_controller.dart';
|
||||
import 'package:device_management/src/features/background_image/presentation/providers/background_image_photos_provider.dart';
|
||||
import 'package:device_management/src/features/remote_connection/domain/entities/picture_entity.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:legacy_device_state/legacy_device_state.dart';
|
||||
import 'package:legacy_theme/legacy_theme.dart';
|
||||
import 'package:navigation/navigation.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:sf_shared/sf_shared.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
|
||||
import 'state/background_image_view_model.dart';
|
||||
import 'state/background_image_view_state.dart';
|
||||
|
||||
class BackgroundImageScreen extends ConsumerWidget {
|
||||
final NavigationContract navigationContract;
|
||||
|
||||
@@ -18,45 +19,32 @@ class BackgroundImageScreen extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final primaryColor = context.sfColors.legacyPrimary;
|
||||
final device = ref.watch(selectedDeviceProvider).value;
|
||||
final photosAsync = ref.watch(backgroundImagePhotosProvider);
|
||||
final isSaving = ref
|
||||
.watch(backgroundImageControllerProvider.select((s) => s.isLoading));
|
||||
|
||||
final state = ref.watch(backgroundImageViewModelProvider);
|
||||
final vm = ref.read(backgroundImageViewModelProvider.notifier);
|
||||
|
||||
ref.listen(backgroundImageViewModelProvider.select((s) => s.errorEvent), (
|
||||
previous,
|
||||
next,
|
||||
) {
|
||||
if (next != null) {
|
||||
final message = switch (next) {
|
||||
BackgroundImageErrorEvent.load => context.translate(
|
||||
I18n.errorBackgroundImageLoad,
|
||||
),
|
||||
BackgroundImageErrorEvent.upload => context.translate(
|
||||
I18n.errorBackgroundImageUpload,
|
||||
),
|
||||
BackgroundImageErrorEvent.set => context.translate(
|
||||
I18n.errorBackgroundImageSet,
|
||||
),
|
||||
ref.listen(backgroundImageControllerProvider, (prev, next) async {
|
||||
if (prev == null || !prev.isLoading || next.isLoading) return;
|
||||
if (next.hasError) {
|
||||
final action =
|
||||
ref.read(backgroundImageControllerProvider.notifier).lastAction;
|
||||
final key = switch (action) {
|
||||
BackgroundImageAction.uploaded => I18n.errorBackgroundImageUpload,
|
||||
BackgroundImageAction.backgroundSet => I18n.errorBackgroundImageSet,
|
||||
null => I18n.errorGeneric,
|
||||
};
|
||||
showTopSnackbar(context, message: message, type: MessageType.error);
|
||||
}
|
||||
});
|
||||
|
||||
ref.listen(backgroundImageViewModelProvider.select((s) => s.successEvent), (
|
||||
previous,
|
||||
next,
|
||||
) {
|
||||
if (next != null) {
|
||||
final message = switch (next) {
|
||||
BackgroundImageSuccessEvent.uploaded => context.translate(
|
||||
I18n.backgroundImageUploaded,
|
||||
),
|
||||
BackgroundImageSuccessEvent.backgroundSet => context.translate(
|
||||
I18n.backgroundImageSet,
|
||||
),
|
||||
};
|
||||
showTopSnackbar(context, message: message, type: MessageType.success);
|
||||
await showErrorDialog(context, key);
|
||||
return;
|
||||
}
|
||||
final action =
|
||||
ref.read(backgroundImageControllerProvider.notifier).lastAction;
|
||||
final key = switch (action) {
|
||||
BackgroundImageAction.uploaded => I18n.backgroundImageUploaded,
|
||||
BackgroundImageAction.backgroundSet => I18n.backgroundImageSet,
|
||||
null => I18n.deviceUpdatedSuccess,
|
||||
};
|
||||
await showSuccessDialog(context, key);
|
||||
});
|
||||
|
||||
return Scaffold(
|
||||
@@ -95,11 +83,14 @@ class BackgroundImageScreen extends ConsumerWidget {
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: IconButton(
|
||||
onPressed: state.isSaving
|
||||
onPressed: isSaving || device == null
|
||||
? null
|
||||
: () async {
|
||||
if (!await guardDeviceCommand(context, ref)) return;
|
||||
vm.uploadPhoto();
|
||||
if (!context.mounted) return;
|
||||
ref
|
||||
.read(backgroundImageControllerProvider.notifier)
|
||||
.uploadPhoto(device: device);
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.add_photo_alternate_outlined,
|
||||
@@ -113,26 +104,43 @@ class BackgroundImageScreen extends ConsumerWidget {
|
||||
),
|
||||
body: SafeArea(
|
||||
top: false,
|
||||
child: state.isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: state.isSaving
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: state.photos.isEmpty
|
||||
? _EmptyState(
|
||||
child: photosAsync.when(
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (_, __) => Center(
|
||||
child: Text(context.translate(I18n.errorBackgroundImageLoad)),
|
||||
),
|
||||
data: (photos) {
|
||||
if (isSaving) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
if (photos.isEmpty) {
|
||||
return _EmptyState(
|
||||
onUpload: () async {
|
||||
if (device == null) return;
|
||||
if (!await guardDeviceCommand(context, ref)) return;
|
||||
vm.uploadPhoto();
|
||||
if (!context.mounted) return;
|
||||
ref
|
||||
.read(backgroundImageControllerProvider.notifier)
|
||||
.uploadPhoto(device: device);
|
||||
},
|
||||
primaryColor: primaryColor,
|
||||
)
|
||||
: _PhotoGrid(
|
||||
state: state,
|
||||
onPhotoTap: (id) async {
|
||||
if (!await guardDeviceCommand(context, ref)) return;
|
||||
vm.setAsBackground(id);
|
||||
},
|
||||
primaryColor: primaryColor,
|
||||
),
|
||||
);
|
||||
}
|
||||
return _PhotoGrid(
|
||||
photos: photos,
|
||||
currentBackgroundId: device?.backgroundImageId,
|
||||
primaryColor: primaryColor,
|
||||
onPhotoTap: (id) async {
|
||||
if (device == null) return;
|
||||
if (!await guardDeviceCommand(context, ref)) return;
|
||||
if (!context.mounted) return;
|
||||
ref
|
||||
.read(backgroundImageControllerProvider.notifier)
|
||||
.setAsBackground(device: device, photoId: id);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -176,14 +184,16 @@ class _EmptyState extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _PhotoGrid extends StatelessWidget {
|
||||
final BackgroundImageViewState state;
|
||||
final ValueChanged<String> onPhotoTap;
|
||||
final List<PictureEntity> photos;
|
||||
final String? currentBackgroundId;
|
||||
final Color primaryColor;
|
||||
final ValueChanged<String> onPhotoTap;
|
||||
|
||||
const _PhotoGrid({
|
||||
required this.state,
|
||||
required this.onPhotoTap,
|
||||
required this.photos,
|
||||
required this.currentBackgroundId,
|
||||
required this.primaryColor,
|
||||
required this.onPhotoTap,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -206,10 +216,10 @@ class _PhotoGrid extends StatelessWidget {
|
||||
crossAxisSpacing: 10,
|
||||
mainAxisSpacing: 10,
|
||||
),
|
||||
itemCount: state.photos.length,
|
||||
itemCount: photos.length,
|
||||
itemBuilder: (context, index) {
|
||||
final photo = state.photos[index];
|
||||
final isActive = state.currentBackgroundId == photo.id;
|
||||
final photo = photos[index];
|
||||
final isActive = currentBackgroundId == photo.id;
|
||||
return GestureDetector(
|
||||
onTap: () => onPhotoTap(photo.id),
|
||||
child: Stack(
|
||||
@@ -232,7 +242,8 @@ class _PhotoGrid extends StatelessWidget {
|
||||
child: Icon(
|
||||
Icons.image_outlined,
|
||||
size: 48,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
color:
|
||||
Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:device_management/src/core/providers/background_image_repository_provider.dart';
|
||||
import 'package:device_management/src/features/background_image/presentation/providers/background_image_photos_provider.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:sf_shared/sf_shared.dart';
|
||||
import 'package:sf_tracking/sf_tracking.dart';
|
||||
|
||||
part 'background_image_controller.g.dart';
|
||||
|
||||
enum BackgroundImageAction { uploaded, backgroundSet }
|
||||
|
||||
class BackgroundImageNoSelectionException implements Exception {
|
||||
const BackgroundImageNoSelectionException();
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class BackgroundImageController extends _$BackgroundImageController {
|
||||
BackgroundImageAction? _lastAction;
|
||||
|
||||
BackgroundImageAction? get lastAction => _lastAction;
|
||||
|
||||
@override
|
||||
FutureOr<void> build() {}
|
||||
|
||||
Future<void> uploadPhoto({required DeviceEntity device}) async {
|
||||
final picker = ImagePicker();
|
||||
final image = await picker.pickImage(
|
||||
source: ImageSource.gallery,
|
||||
maxWidth: 800,
|
||||
imageQuality: 80,
|
||||
);
|
||||
if (image == null) return;
|
||||
|
||||
_lastAction = BackgroundImageAction.uploaded;
|
||||
state = const AsyncLoading();
|
||||
state = await AsyncValue.guard(() async {
|
||||
final photoId = await ref
|
||||
.read(backgroundImageRepositoryProvider)
|
||||
.uploadImage(path: image.path);
|
||||
await ref
|
||||
.read(backgroundImageRepositoryProvider)
|
||||
.setBackgroundImage(deviceId: device.id, photoId: photoId);
|
||||
await _refreshDevice(device);
|
||||
ref.invalidate(backgroundImagePhotosProvider);
|
||||
unawaited(
|
||||
ref.read(sfTrackingProvider).legacyDeviceBackgroundImageUploaded(),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> setAsBackground({
|
||||
required DeviceEntity device,
|
||||
required String photoId,
|
||||
}) async {
|
||||
_lastAction = BackgroundImageAction.backgroundSet;
|
||||
state = const AsyncLoading();
|
||||
state = await AsyncValue.guard(() async {
|
||||
await ref
|
||||
.read(backgroundImageRepositoryProvider)
|
||||
.setBackgroundImage(deviceId: device.id, photoId: photoId);
|
||||
await _refreshDevice(device);
|
||||
unawaited(
|
||||
ref.read(sfTrackingProvider).legacyDeviceBackgroundImageChanged(),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _refreshDevice(DeviceEntity current) async {
|
||||
final devices =
|
||||
await ref.read(sharedDevicesRepositoryProvider).getDevices();
|
||||
final updated = devices.firstWhere(
|
||||
(d) => d.identificator == current.identificator,
|
||||
orElse: () => current,
|
||||
);
|
||||
ref.read(selectedDeviceProvider.notifier).setSelectedDevice(updated);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'background_image_controller.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
@ProviderFor(BackgroundImageController)
|
||||
const backgroundImageControllerProvider = BackgroundImageControllerProvider._();
|
||||
|
||||
final class BackgroundImageControllerProvider
|
||||
extends $AsyncNotifierProvider<BackgroundImageController, void> {
|
||||
const BackgroundImageControllerProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'backgroundImageControllerProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$backgroundImageControllerHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
BackgroundImageController create() => BackgroundImageController();
|
||||
}
|
||||
|
||||
String _$backgroundImageControllerHash() =>
|
||||
r'60faa9520c5534e915321f65d92119220c1ca1e5';
|
||||
|
||||
abstract class _$BackgroundImageController extends $AsyncNotifier<void> {
|
||||
FutureOr<void> build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
build();
|
||||
final ref = this.ref as $Ref<AsyncValue<void>, void>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<AsyncValue<void>, void>,
|
||||
AsyncValue<void>,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import 'package:device_management/src/core/providers/background_image_repository_provider.dart';
|
||||
import 'package:device_management/src/features/remote_connection/domain/entities/picture_entity.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'background_image_photos_provider.g.dart';
|
||||
|
||||
@riverpod
|
||||
Future<List<PictureEntity>> backgroundImagePhotos(Ref ref) async {
|
||||
return ref.read(backgroundImageRepositoryProvider).getPhotos();
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'background_image_photos_provider.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
@ProviderFor(backgroundImagePhotos)
|
||||
const backgroundImagePhotosProvider = BackgroundImagePhotosProvider._();
|
||||
|
||||
final class BackgroundImagePhotosProvider
|
||||
extends
|
||||
$FunctionalProvider<
|
||||
AsyncValue<List<PictureEntity>>,
|
||||
List<PictureEntity>,
|
||||
FutureOr<List<PictureEntity>>
|
||||
>
|
||||
with
|
||||
$FutureModifier<List<PictureEntity>>,
|
||||
$FutureProvider<List<PictureEntity>> {
|
||||
const BackgroundImagePhotosProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'backgroundImagePhotosProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$backgroundImagePhotosHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$FutureProviderElement<List<PictureEntity>> $createElement(
|
||||
$ProviderPointer pointer,
|
||||
) => $FutureProviderElement(pointer);
|
||||
|
||||
@override
|
||||
FutureOr<List<PictureEntity>> create(Ref ref) {
|
||||
return backgroundImagePhotos(ref);
|
||||
}
|
||||
}
|
||||
|
||||
String _$backgroundImagePhotosHash() =>
|
||||
r'25ebb7f6612d1b95435c4b59f66d035f5281ff8b';
|
||||
@@ -1,156 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:sf_shared/sf_shared.dart';
|
||||
import 'package:sf_tracking/sf_tracking.dart';
|
||||
|
||||
import '../../../../core/domain/repositories/background_image_repository.dart';
|
||||
import '../../../../core/providers/background_image_repository_provider.dart';
|
||||
import 'background_image_view_state.dart';
|
||||
|
||||
final backgroundImageViewModelProvider =
|
||||
NotifierProvider.autoDispose<
|
||||
BackgroundImageViewModel,
|
||||
BackgroundImageViewState
|
||||
>(BackgroundImageViewModel.new);
|
||||
|
||||
class BackgroundImageViewModel extends Notifier<BackgroundImageViewState> {
|
||||
late final BackgroundImageRepository _repository;
|
||||
late final SharedDevicesRepository _devicesRepository;
|
||||
late final SfTrackingRepository _tracking;
|
||||
|
||||
@override
|
||||
BackgroundImageViewState build() {
|
||||
_repository = ref.read(backgroundImageRepositoryProvider);
|
||||
_devicesRepository = ref.read(sharedDevicesRepositoryProvider);
|
||||
_tracking = ref.read(sfTrackingProvider);
|
||||
Future.microtask(_load);
|
||||
return const BackgroundImageViewState();
|
||||
}
|
||||
|
||||
Future<void> _load() async {
|
||||
try {
|
||||
final device = ref.read(selectedDeviceProvider).value;
|
||||
if (device == null) return;
|
||||
|
||||
final photos = await _repository.getPhotos();
|
||||
if (!ref.mounted) return;
|
||||
|
||||
state = state.copyWith(
|
||||
photos: photos,
|
||||
currentBackgroundId: device.backgroundImageId,
|
||||
isLoading: false,
|
||||
);
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(
|
||||
isLoading: false,
|
||||
errorEvent: BackgroundImageErrorEvent.load,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> reload() async {
|
||||
state = state.copyWith(isLoading: true, errorEvent: null);
|
||||
await _load();
|
||||
}
|
||||
|
||||
Future<void> uploadPhoto() async {
|
||||
if (state.isSaving) return;
|
||||
|
||||
final picker = ImagePicker();
|
||||
final image = await picker.pickImage(
|
||||
source: ImageSource.gallery,
|
||||
maxWidth: 800,
|
||||
imageQuality: 80,
|
||||
);
|
||||
if (image == null) return;
|
||||
|
||||
final device = ref.read(selectedDeviceProvider).value;
|
||||
if (device == null) return;
|
||||
|
||||
state = state.copyWith(
|
||||
isSaving: true,
|
||||
errorEvent: null,
|
||||
successEvent: null,
|
||||
);
|
||||
|
||||
try {
|
||||
final photoId = await _repository.uploadImage(path: image.path);
|
||||
if (!ref.mounted) return;
|
||||
|
||||
await _repository.setBackgroundImage(
|
||||
deviceId: device.id,
|
||||
photoId: photoId,
|
||||
);
|
||||
if (!ref.mounted) return;
|
||||
|
||||
await _refreshDevice(device);
|
||||
if (!ref.mounted) return;
|
||||
|
||||
unawaited(_tracking.legacyDeviceBackgroundImageUploaded());
|
||||
|
||||
state = state.copyWith(
|
||||
isSaving: false,
|
||||
successEvent: BackgroundImageSuccessEvent.uploaded,
|
||||
);
|
||||
|
||||
await reload();
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(
|
||||
isSaving: false,
|
||||
errorEvent: BackgroundImageErrorEvent.upload,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setAsBackground(String photoId) async {
|
||||
final device = ref.read(selectedDeviceProvider).value;
|
||||
if (device == null) return;
|
||||
|
||||
state = state.copyWith(
|
||||
isSaving: true,
|
||||
errorEvent: null,
|
||||
successEvent: null,
|
||||
);
|
||||
|
||||
try {
|
||||
await _repository.setBackgroundImage(
|
||||
deviceId: device.id,
|
||||
photoId: photoId,
|
||||
);
|
||||
if (!ref.mounted) return;
|
||||
|
||||
await _refreshDevice(device);
|
||||
if (!ref.mounted) return;
|
||||
|
||||
unawaited(_tracking.legacyDeviceBackgroundImageChanged());
|
||||
|
||||
state = state.copyWith(
|
||||
isSaving: false,
|
||||
successEvent: BackgroundImageSuccessEvent.backgroundSet,
|
||||
);
|
||||
|
||||
await reload();
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(
|
||||
isSaving: false,
|
||||
errorEvent: BackgroundImageErrorEvent.set,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _refreshDevice(DeviceEntity currentDevice) async {
|
||||
final devices = await _devicesRepository.getDevices();
|
||||
if (!ref.mounted) return;
|
||||
|
||||
final updated = devices.firstWhere(
|
||||
(d) => d.identificator == currentDevice.identificator,
|
||||
orElse: () => currentDevice,
|
||||
);
|
||||
ref.read(selectedDeviceProvider.notifier).setSelectedDevice(updated);
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import 'package:device_management/src/features/remote_connection/domain/entities/picture_entity.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'background_image_view_state.freezed.dart';
|
||||
|
||||
enum BackgroundImageErrorEvent { load, upload, set }
|
||||
|
||||
enum BackgroundImageSuccessEvent { uploaded, backgroundSet }
|
||||
|
||||
@freezed
|
||||
abstract class BackgroundImageViewState with _$BackgroundImageViewState {
|
||||
const factory BackgroundImageViewState({
|
||||
@Default([]) List<PictureEntity> photos,
|
||||
@Default(true) bool isLoading,
|
||||
@Default(false) bool isSaving,
|
||||
String? currentBackgroundId,
|
||||
BackgroundImageSuccessEvent? successEvent,
|
||||
BackgroundImageErrorEvent? errorEvent,
|
||||
}) = _BackgroundImageViewState;
|
||||
}
|
||||
@@ -1,292 +0,0 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'background_image_view_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$BackgroundImageViewState {
|
||||
|
||||
List<PictureEntity> get photos; bool get isLoading; bool get isSaving; String? get currentBackgroundId; BackgroundImageSuccessEvent? get successEvent; BackgroundImageErrorEvent? get errorEvent;
|
||||
/// Create a copy of BackgroundImageViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$BackgroundImageViewStateCopyWith<BackgroundImageViewState> get copyWith => _$BackgroundImageViewStateCopyWithImpl<BackgroundImageViewState>(this as BackgroundImageViewState, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is BackgroundImageViewState&&const DeepCollectionEquality().equals(other.photos, photos)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isSaving, isSaving) || other.isSaving == isSaving)&&(identical(other.currentBackgroundId, currentBackgroundId) || other.currentBackgroundId == currentBackgroundId)&&(identical(other.successEvent, successEvent) || other.successEvent == successEvent)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(photos),isLoading,isSaving,currentBackgroundId,successEvent,errorEvent);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'BackgroundImageViewState(photos: $photos, isLoading: $isLoading, isSaving: $isSaving, currentBackgroundId: $currentBackgroundId, successEvent: $successEvent, errorEvent: $errorEvent)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $BackgroundImageViewStateCopyWith<$Res> {
|
||||
factory $BackgroundImageViewStateCopyWith(BackgroundImageViewState value, $Res Function(BackgroundImageViewState) _then) = _$BackgroundImageViewStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
List<PictureEntity> photos, bool isLoading, bool isSaving, String? currentBackgroundId, BackgroundImageSuccessEvent? successEvent, BackgroundImageErrorEvent? errorEvent
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$BackgroundImageViewStateCopyWithImpl<$Res>
|
||||
implements $BackgroundImageViewStateCopyWith<$Res> {
|
||||
_$BackgroundImageViewStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final BackgroundImageViewState _self;
|
||||
final $Res Function(BackgroundImageViewState) _then;
|
||||
|
||||
/// Create a copy of BackgroundImageViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? photos = null,Object? isLoading = null,Object? isSaving = null,Object? currentBackgroundId = freezed,Object? successEvent = freezed,Object? errorEvent = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
photos: null == photos ? _self.photos : photos // ignore: cast_nullable_to_non_nullable
|
||||
as List<PictureEntity>,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isSaving: null == isSaving ? _self.isSaving : isSaving // ignore: cast_nullable_to_non_nullable
|
||||
as bool,currentBackgroundId: freezed == currentBackgroundId ? _self.currentBackgroundId : currentBackgroundId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,successEvent: freezed == successEvent ? _self.successEvent : successEvent // ignore: cast_nullable_to_non_nullable
|
||||
as BackgroundImageSuccessEvent?,errorEvent: freezed == errorEvent ? _self.errorEvent : errorEvent // ignore: cast_nullable_to_non_nullable
|
||||
as BackgroundImageErrorEvent?,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [BackgroundImageViewState].
|
||||
extension BackgroundImageViewStatePatterns on BackgroundImageViewState {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _BackgroundImageViewState value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _BackgroundImageViewState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _BackgroundImageViewState value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _BackgroundImageViewState():
|
||||
return $default(_that);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _BackgroundImageViewState value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _BackgroundImageViewState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<PictureEntity> photos, bool isLoading, bool isSaving, String? currentBackgroundId, BackgroundImageSuccessEvent? successEvent, BackgroundImageErrorEvent? errorEvent)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _BackgroundImageViewState() when $default != null:
|
||||
return $default(_that.photos,_that.isLoading,_that.isSaving,_that.currentBackgroundId,_that.successEvent,_that.errorEvent);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<PictureEntity> photos, bool isLoading, bool isSaving, String? currentBackgroundId, BackgroundImageSuccessEvent? successEvent, BackgroundImageErrorEvent? errorEvent) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _BackgroundImageViewState():
|
||||
return $default(_that.photos,_that.isLoading,_that.isSaving,_that.currentBackgroundId,_that.successEvent,_that.errorEvent);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<PictureEntity> photos, bool isLoading, bool isSaving, String? currentBackgroundId, BackgroundImageSuccessEvent? successEvent, BackgroundImageErrorEvent? errorEvent)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _BackgroundImageViewState() when $default != null:
|
||||
return $default(_that.photos,_that.isLoading,_that.isSaving,_that.currentBackgroundId,_that.successEvent,_that.errorEvent);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _BackgroundImageViewState implements BackgroundImageViewState {
|
||||
const _BackgroundImageViewState({final List<PictureEntity> photos = const [], this.isLoading = true, this.isSaving = false, this.currentBackgroundId, this.successEvent, this.errorEvent}): _photos = photos;
|
||||
|
||||
|
||||
final List<PictureEntity> _photos;
|
||||
@override@JsonKey() List<PictureEntity> get photos {
|
||||
if (_photos is EqualUnmodifiableListView) return _photos;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_photos);
|
||||
}
|
||||
|
||||
@override@JsonKey() final bool isLoading;
|
||||
@override@JsonKey() final bool isSaving;
|
||||
@override final String? currentBackgroundId;
|
||||
@override final BackgroundImageSuccessEvent? successEvent;
|
||||
@override final BackgroundImageErrorEvent? errorEvent;
|
||||
|
||||
/// Create a copy of BackgroundImageViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$BackgroundImageViewStateCopyWith<_BackgroundImageViewState> get copyWith => __$BackgroundImageViewStateCopyWithImpl<_BackgroundImageViewState>(this, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _BackgroundImageViewState&&const DeepCollectionEquality().equals(other._photos, _photos)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isSaving, isSaving) || other.isSaving == isSaving)&&(identical(other.currentBackgroundId, currentBackgroundId) || other.currentBackgroundId == currentBackgroundId)&&(identical(other.successEvent, successEvent) || other.successEvent == successEvent)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_photos),isLoading,isSaving,currentBackgroundId,successEvent,errorEvent);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'BackgroundImageViewState(photos: $photos, isLoading: $isLoading, isSaving: $isSaving, currentBackgroundId: $currentBackgroundId, successEvent: $successEvent, errorEvent: $errorEvent)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$BackgroundImageViewStateCopyWith<$Res> implements $BackgroundImageViewStateCopyWith<$Res> {
|
||||
factory _$BackgroundImageViewStateCopyWith(_BackgroundImageViewState value, $Res Function(_BackgroundImageViewState) _then) = __$BackgroundImageViewStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
List<PictureEntity> photos, bool isLoading, bool isSaving, String? currentBackgroundId, BackgroundImageSuccessEvent? successEvent, BackgroundImageErrorEvent? errorEvent
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$BackgroundImageViewStateCopyWithImpl<$Res>
|
||||
implements _$BackgroundImageViewStateCopyWith<$Res> {
|
||||
__$BackgroundImageViewStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _BackgroundImageViewState _self;
|
||||
final $Res Function(_BackgroundImageViewState) _then;
|
||||
|
||||
/// Create a copy of BackgroundImageViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? photos = null,Object? isLoading = null,Object? isSaving = null,Object? currentBackgroundId = freezed,Object? successEvent = freezed,Object? errorEvent = freezed,}) {
|
||||
return _then(_BackgroundImageViewState(
|
||||
photos: null == photos ? _self._photos : photos // ignore: cast_nullable_to_non_nullable
|
||||
as List<PictureEntity>,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isSaving: null == isSaving ? _self.isSaving : isSaving // ignore: cast_nullable_to_non_nullable
|
||||
as bool,currentBackgroundId: freezed == currentBackgroundId ? _self.currentBackgroundId : currentBackgroundId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,successEvent: freezed == successEvent ? _self.successEvent : successEvent // ignore: cast_nullable_to_non_nullable
|
||||
as BackgroundImageSuccessEvent?,errorEvent: freezed == errorEvent ? _self.errorEvent : errorEvent // ignore: cast_nullable_to_non_nullable
|
||||
as BackgroundImageErrorEvent?,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
@@ -0,0 +1,103 @@
|
||||
import 'package:device_management/src/core/domain/repositories/background_image_repository.dart';
|
||||
import 'package:device_management/src/core/providers/background_image_repository_provider.dart';
|
||||
import 'package:device_management/src/features/background_image/presentation/providers/background_image_controller.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:sf_infrastructure/sf_infrastructure.dart';
|
||||
import 'package:sf_shared/sf_shared.dart';
|
||||
import 'package:sf_shared/testing.dart';
|
||||
import 'package:sf_tracking/sf_tracking.dart';
|
||||
|
||||
class MockBackgroundImageRepository extends Mock
|
||||
implements BackgroundImageRepository {}
|
||||
|
||||
class MockSharedDevicesRepository extends Mock
|
||||
implements SharedDevicesRepository {}
|
||||
|
||||
const _device = DeviceEntity(
|
||||
id: 'device-1',
|
||||
identificator: 'imei-1',
|
||||
carrierName: 'Watch',
|
||||
);
|
||||
|
||||
void main() {
|
||||
setUpAll(() {
|
||||
registerFallbackValue(_device);
|
||||
});
|
||||
|
||||
ProviderContainer buildContainer({
|
||||
required BackgroundImageRepository repo,
|
||||
required SharedDevicesRepository devicesRepo,
|
||||
}) {
|
||||
return makeContainer(
|
||||
overrides: [
|
||||
backgroundImageRepositoryProvider.overrideWithValue(repo),
|
||||
sharedDevicesRepositoryProvider.overrideWithValue(devicesRepo),
|
||||
sfTrackingProvider.overrideWithValue(
|
||||
SfTrackingRepository(clients: const []),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
group('BackgroundImageController.setAsBackground', () {
|
||||
test('updates backend and refreshes device on success', () async {
|
||||
final repo = MockBackgroundImageRepository();
|
||||
final devicesRepo = MockSharedDevicesRepository();
|
||||
when(
|
||||
() => repo.setBackgroundImage(
|
||||
deviceId: any(named: 'deviceId'),
|
||||
photoId: any(named: 'photoId'),
|
||||
),
|
||||
).thenAnswer((_) async {});
|
||||
when(() => devicesRepo.getDevices()).thenAnswer((_) async => const []);
|
||||
|
||||
final container =
|
||||
buildContainer(repo: repo, devicesRepo: devicesRepo);
|
||||
addTearDown(container.dispose);
|
||||
|
||||
await container
|
||||
.read(backgroundImageControllerProvider.notifier)
|
||||
.setAsBackground(device: _device, photoId: 'photo-1');
|
||||
|
||||
expect(
|
||||
container.read(backgroundImageControllerProvider),
|
||||
isA<AsyncData<void>>(),
|
||||
);
|
||||
expect(
|
||||
container
|
||||
.read(backgroundImageControllerProvider.notifier)
|
||||
.lastAction,
|
||||
BackgroundImageAction.backgroundSet,
|
||||
);
|
||||
verify(
|
||||
() => repo.setBackgroundImage(deviceId: 'device-1', photoId: 'photo-1'),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
test('exposes AsyncError when repository fails', () async {
|
||||
final repo = MockBackgroundImageRepository();
|
||||
final devicesRepo = MockSharedDevicesRepository();
|
||||
when(
|
||||
() => repo.setBackgroundImage(
|
||||
deviceId: any(named: 'deviceId'),
|
||||
photoId: any(named: 'photoId'),
|
||||
),
|
||||
).thenThrow(const ApiException(message: 'boom', isNetworkError: true));
|
||||
|
||||
final container =
|
||||
buildContainer(repo: repo, devicesRepo: devicesRepo);
|
||||
addTearDown(container.dispose);
|
||||
|
||||
await container
|
||||
.read(backgroundImageControllerProvider.notifier)
|
||||
.setAsBackground(device: _device, photoId: 'photo-1');
|
||||
|
||||
expect(
|
||||
container.read(backgroundImageControllerProvider),
|
||||
isA<AsyncError<void>>(),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user