feat: sync device settings after updates and improve remote connection features
- Add DeviceSettingsSync extension on Ref to centralize device provider
updates after settings changes (sound, volume, language, timezone,
battery, disable functions, alerts, pedometer, heart rate freq,
location freq, background image)
- Add photo capture countdown with Lottie animation in remote camera
- Replace Image.network with Image.memory for photo display
- Fix commands datasource to handle text/html responses (post<dynamic>)
- Add typed error/success events to RemoteConnectionViewModel
- Add background image active indicator and backgroundImageId to device settings
- Change photos endpoint from /devices/identificator/:id/photos/files to /photos/files
- Remove dead deviceId param from getBackgroundImage chain
- Relax PictureEntity required fields to @Default for API compatibility
- Fix LocationViewModel rebuild crash (ref.watch → ref.read)
- Use .select() in RemoteCameraScreen for optimized rebuilds
- Increase health measure countdown to 60s
This commit is contained in:
@@ -1,9 +1,7 @@
|
||||
import '../models/get_background_image_response_model.dart';
|
||||
|
||||
abstract class BackgroundImageRemoteDatasource {
|
||||
Future<GetBackgroundImageResponseModel> getBackgroundImage({
|
||||
required String deviceId,
|
||||
});
|
||||
Future<GetBackgroundImageResponseModel> getBackgroundImage();
|
||||
|
||||
Future<String> uploadImage({
|
||||
required String path,
|
||||
|
||||
@@ -6,17 +6,16 @@ import 'package:sf_infrastructure/configure_dependencies.dart';
|
||||
import '../models/get_background_image_response_model.dart';
|
||||
import 'background_image_remote_datasource.dart';
|
||||
|
||||
class BackgroundImageRemoteDatasourceImpl implements BackgroundImageRemoteDatasource {
|
||||
class BackgroundImageRemoteDatasourceImpl
|
||||
implements BackgroundImageRemoteDatasource {
|
||||
BackgroundImageRemoteDatasourceImpl(this._repository);
|
||||
|
||||
final QuestiaRepository _repository;
|
||||
|
||||
@override
|
||||
Future<GetBackgroundImageResponseModel> getBackgroundImage({required String deviceId}) async {
|
||||
Future<GetBackgroundImageResponseModel> getBackgroundImage() async {
|
||||
final response = await safeCall(
|
||||
() => _repository.get<dynamic>(
|
||||
'/devices/identificator/$deviceId/photos/files',
|
||||
),
|
||||
() => _repository.get<dynamic>('/photos/files'),
|
||||
'Error getting background image',
|
||||
);
|
||||
|
||||
@@ -39,10 +38,7 @@ class BackgroundImageRemoteDatasourceImpl implements BackgroundImageRemoteDataso
|
||||
final dio = GetIt.I<Dio>();
|
||||
dio.options.headers.remove('content-type');
|
||||
try {
|
||||
final response = await dio.post<dynamic>(
|
||||
'/photos',
|
||||
data: formData,
|
||||
);
|
||||
final response = await dio.post<dynamic>('/photos', data: formData);
|
||||
|
||||
final data = response.data;
|
||||
if (data == null) {
|
||||
@@ -74,4 +70,4 @@ class BackgroundImageRemoteDatasourceImpl implements BackgroundImageRemoteDataso
|
||||
'Error setting background image',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,13 +22,13 @@ abstract class GetPicturesItemResponseModel
|
||||
with _$GetPicturesItemResponseModel {
|
||||
const factory GetPicturesItemResponseModel({
|
||||
required String id,
|
||||
required String deviceIdentificator,
|
||||
@Default('') String deviceIdentificator,
|
||||
String? imgType,
|
||||
String? timestamp,
|
||||
required String fileId,
|
||||
@Default('') String fileId,
|
||||
String? fileName,
|
||||
String? contentType,
|
||||
required int createdAt,
|
||||
@Default(0) int createdAt,
|
||||
Map<String, dynamic>? file,
|
||||
}) = _GetPicturesItemResponseModel;
|
||||
|
||||
|
||||
@@ -486,17 +486,17 @@ return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp
|
||||
@JsonSerializable()
|
||||
|
||||
class _GetPicturesItemResponseModel implements GetPicturesItemResponseModel {
|
||||
const _GetPicturesItemResponseModel({required this.id, required this.deviceIdentificator, this.imgType, this.timestamp, required this.fileId, this.fileName, this.contentType, required this.createdAt, final Map<String, dynamic>? file}): _file = file;
|
||||
const _GetPicturesItemResponseModel({required this.id, this.deviceIdentificator = '', this.imgType, this.timestamp, this.fileId = '', this.fileName, this.contentType, this.createdAt = 0, final Map<String, dynamic>? file}): _file = file;
|
||||
factory _GetPicturesItemResponseModel.fromJson(Map<String, dynamic> json) => _$GetPicturesItemResponseModelFromJson(json);
|
||||
|
||||
@override final String id;
|
||||
@override final String deviceIdentificator;
|
||||
@override@JsonKey() final String deviceIdentificator;
|
||||
@override final String? imgType;
|
||||
@override final String? timestamp;
|
||||
@override final String fileId;
|
||||
@override@JsonKey() final String fileId;
|
||||
@override final String? fileName;
|
||||
@override final String? contentType;
|
||||
@override final int createdAt;
|
||||
@override@JsonKey() final int createdAt;
|
||||
final Map<String, dynamic>? _file;
|
||||
@override Map<String, dynamic>? get file {
|
||||
final value = _file;
|
||||
|
||||
@@ -24,13 +24,13 @@ _GetPicturesItemResponseModel _$GetPicturesItemResponseModelFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _GetPicturesItemResponseModel(
|
||||
id: json['id'] as String,
|
||||
deviceIdentificator: json['deviceIdentificator'] as String,
|
||||
deviceIdentificator: json['deviceIdentificator'] as String? ?? '',
|
||||
imgType: json['imgType'] as String?,
|
||||
timestamp: json['timestamp'] as String?,
|
||||
fileId: json['fileId'] as String,
|
||||
fileId: json['fileId'] as String? ?? '',
|
||||
fileName: json['fileName'] as String?,
|
||||
contentType: json['contentType'] as String?,
|
||||
createdAt: (json['createdAt'] as num).toInt(),
|
||||
createdAt: (json['createdAt'] as num?)?.toInt() ?? 0,
|
||||
file: json['file'] as Map<String, dynamic>?,
|
||||
);
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ class BackgroundImageRepositoryImpl implements BackgroundImageRepository {
|
||||
final BackgroundImageRemoteDatasource _remote;
|
||||
|
||||
@override
|
||||
Future<List<PictureEntity>> getPhotos({required String deviceId}) async {
|
||||
final model = await _remote.getBackgroundImage(deviceId: deviceId);
|
||||
Future<List<PictureEntity>> getPhotos() async {
|
||||
final model = await _remote.getBackgroundImage();
|
||||
return model.toEntities();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import 'package:device_management/src/features/remote_connection/domain/entities/picture_entity.dart';
|
||||
|
||||
abstract class BackgroundImageRepository {
|
||||
Future<List<PictureEntity>> getPhotos({required String deviceId});
|
||||
Future<List<PictureEntity>> getPhotos();
|
||||
Future<String> uploadImage({required String path});
|
||||
Future<void> setBackgroundImage({required String deviceId, required String photoId});
|
||||
}
|
||||
|
||||
@@ -255,6 +255,7 @@ class ActivityMeterViewModel extends Notifier<ActivityMeterViewState> {
|
||||
updatedSettings: updatedSettings,
|
||||
);
|
||||
if (!ref.mounted) return false;
|
||||
ref.syncDeviceSettings(device, updatedSettings);
|
||||
return true;
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return false;
|
||||
|
||||
@@ -113,7 +113,7 @@ class BackgroundImageScreen extends ConsumerWidget {
|
||||
)
|
||||
: _PhotoGrid(
|
||||
state: state,
|
||||
vm: vm,
|
||||
onPhotoTap: vm.setAsBackground,
|
||||
primaryColor: primaryColor,
|
||||
),
|
||||
),
|
||||
@@ -160,12 +160,12 @@ class _EmptyState extends StatelessWidget {
|
||||
|
||||
class _PhotoGrid extends StatelessWidget {
|
||||
final BackgroundImageViewState state;
|
||||
final BackgroundImageViewModel vm;
|
||||
final ValueChanged<String> onPhotoTap;
|
||||
final Color primaryColor;
|
||||
|
||||
const _PhotoGrid({
|
||||
required this.state,
|
||||
required this.vm,
|
||||
required this.onPhotoTap,
|
||||
required this.primaryColor,
|
||||
});
|
||||
|
||||
@@ -192,28 +192,53 @@ class _PhotoGrid extends StatelessWidget {
|
||||
itemCount: state.photos.length,
|
||||
itemBuilder: (context, index) {
|
||||
final photo = state.photos[index];
|
||||
final isActive = state.currentBackgroundId == photo.id;
|
||||
return GestureDetector(
|
||||
onTap: () => vm.setAsBackground(photo.id),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: Colors.grey.shade300,
|
||||
),
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: photo.fileBytes != null
|
||||
? Image.memory(
|
||||
photo.fileBytes!,
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: Center(
|
||||
child: Icon(
|
||||
Icons.image_outlined,
|
||||
size: 48,
|
||||
color: Colors.grey.shade400,
|
||||
onTap: () => onPhotoTap(photo.id),
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: isActive ? primaryColor : Colors.grey.shade300,
|
||||
width: isActive ? 3 : 1,
|
||||
),
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: photo.fileBytes != null
|
||||
? Image.memory(
|
||||
photo.fileBytes!,
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: Center(
|
||||
child: Icon(
|
||||
Icons.image_outlined,
|
||||
size: 48,
|
||||
color: Colors.grey.shade400,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isActive)
|
||||
Positioned(
|
||||
top: 8,
|
||||
right: 8,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
color: primaryColor,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.check,
|
||||
color: Colors.white,
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -26,12 +26,14 @@ class BackgroundImageViewModel extends Notifier<BackgroundImageViewState> {
|
||||
final device = ref.read(selectedDeviceProvider);
|
||||
if (device == null) return;
|
||||
|
||||
final photos = await _repository.getPhotos(
|
||||
deviceId: device.identificator,
|
||||
);
|
||||
final photos = await _repository.getPhotos();
|
||||
if (!ref.mounted) return;
|
||||
|
||||
state = state.copyWith(photos: photos, isLoading: false);
|
||||
state = state.copyWith(
|
||||
photos: photos,
|
||||
currentBackgroundId: device.settings.backgroundImageId,
|
||||
isLoading: false,
|
||||
);
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(
|
||||
@@ -57,14 +59,29 @@ class BackgroundImageViewModel extends Notifier<BackgroundImageViewState> {
|
||||
);
|
||||
if (image == null) return;
|
||||
|
||||
final device = ref.read(selectedDeviceProvider);
|
||||
if (device == null) return;
|
||||
|
||||
state = state.copyWith(isSaving: true, errorEvent: null, successEvent: null);
|
||||
|
||||
try {
|
||||
await _repository.uploadImage(path: image.path);
|
||||
final photoId = await _repository.uploadImage(path: image.path);
|
||||
if (!ref.mounted) return;
|
||||
|
||||
await _repository.setBackgroundImage(
|
||||
deviceId: device.id,
|
||||
photoId: photoId,
|
||||
);
|
||||
if (!ref.mounted) return;
|
||||
|
||||
ref.syncDeviceSettings(
|
||||
device,
|
||||
device.settings.copyWith(backgroundImageId: photoId),
|
||||
);
|
||||
|
||||
state = state.copyWith(
|
||||
isSaving: false,
|
||||
currentBackgroundId: photoId,
|
||||
successEvent: BackgroundImageSuccessEvent.uploaded,
|
||||
);
|
||||
|
||||
@@ -91,8 +108,14 @@ class BackgroundImageViewModel extends Notifier<BackgroundImageViewState> {
|
||||
);
|
||||
if (!ref.mounted) return;
|
||||
|
||||
ref.syncDeviceSettings(
|
||||
device,
|
||||
device.settings.copyWith(backgroundImageId: photoId),
|
||||
);
|
||||
|
||||
state = state.copyWith(
|
||||
isSaving: false,
|
||||
currentBackgroundId: photoId,
|
||||
successEvent: BackgroundImageSuccessEvent.backgroundSet,
|
||||
);
|
||||
} catch (e) {
|
||||
|
||||
@@ -12,6 +12,7 @@ abstract class BackgroundImageViewState with _$BackgroundImageViewState {
|
||||
@Default([]) List<PictureEntity> photos,
|
||||
@Default(true) bool isLoading,
|
||||
@Default(false) bool isSaving,
|
||||
String? currentBackgroundId,
|
||||
BackgroundImageSuccessEvent? successEvent,
|
||||
BackgroundImageErrorEvent? errorEvent,
|
||||
}) = _BackgroundImageViewState;
|
||||
|
||||
@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$BackgroundImageViewState {
|
||||
|
||||
List<PictureEntity> get photos; bool get isLoading; bool get isSaving; BackgroundImageSuccessEvent? get successEvent; BackgroundImageErrorEvent? get errorEvent;
|
||||
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)
|
||||
@@ -25,16 +25,16 @@ $BackgroundImageViewStateCopyWith<BackgroundImageViewState> get copyWith => _$Ba
|
||||
|
||||
@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.successEvent, successEvent) || other.successEvent == successEvent)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent));
|
||||
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,successEvent,errorEvent);
|
||||
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, successEvent: $successEvent, errorEvent: $errorEvent)';
|
||||
return 'BackgroundImageViewState(photos: $photos, isLoading: $isLoading, isSaving: $isSaving, currentBackgroundId: $currentBackgroundId, successEvent: $successEvent, errorEvent: $errorEvent)';
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ abstract mixin class $BackgroundImageViewStateCopyWith<$Res> {
|
||||
factory $BackgroundImageViewStateCopyWith(BackgroundImageViewState value, $Res Function(BackgroundImageViewState) _then) = _$BackgroundImageViewStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
List<PictureEntity> photos, bool isLoading, bool isSaving, BackgroundImageSuccessEvent? successEvent, BackgroundImageErrorEvent? errorEvent
|
||||
List<PictureEntity> photos, bool isLoading, bool isSaving, String? currentBackgroundId, BackgroundImageSuccessEvent? successEvent, BackgroundImageErrorEvent? errorEvent
|
||||
});
|
||||
|
||||
|
||||
@@ -62,12 +62,13 @@ class _$BackgroundImageViewStateCopyWithImpl<$Res>
|
||||
|
||||
/// 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? successEvent = freezed,Object? errorEvent = freezed,}) {
|
||||
@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,successEvent: freezed == successEvent ? _self.successEvent : successEvent // 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?,
|
||||
));
|
||||
@@ -154,10 +155,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<PictureEntity> photos, bool isLoading, bool isSaving, BackgroundImageSuccessEvent? successEvent, BackgroundImageErrorEvent? errorEvent)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@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.successEvent,_that.errorEvent);case _:
|
||||
return $default(_that.photos,_that.isLoading,_that.isSaving,_that.currentBackgroundId,_that.successEvent,_that.errorEvent);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@@ -175,10 +176,10 @@ return $default(_that.photos,_that.isLoading,_that.isSaving,_that.successEvent,_
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<PictureEntity> photos, bool isLoading, bool isSaving, BackgroundImageSuccessEvent? successEvent, BackgroundImageErrorEvent? errorEvent) $default,) {final _that = this;
|
||||
@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.successEvent,_that.errorEvent);case _:
|
||||
return $default(_that.photos,_that.isLoading,_that.isSaving,_that.currentBackgroundId,_that.successEvent,_that.errorEvent);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@@ -195,10 +196,10 @@ return $default(_that.photos,_that.isLoading,_that.isSaving,_that.successEvent,_
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<PictureEntity> photos, bool isLoading, bool isSaving, BackgroundImageSuccessEvent? successEvent, BackgroundImageErrorEvent? errorEvent)? $default,) {final _that = this;
|
||||
@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.successEvent,_that.errorEvent);case _:
|
||||
return $default(_that.photos,_that.isLoading,_that.isSaving,_that.currentBackgroundId,_that.successEvent,_that.errorEvent);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@@ -210,7 +211,7 @@ return $default(_that.photos,_that.isLoading,_that.isSaving,_that.successEvent,_
|
||||
|
||||
|
||||
class _BackgroundImageViewState implements BackgroundImageViewState {
|
||||
const _BackgroundImageViewState({final List<PictureEntity> photos = const [], this.isLoading = true, this.isSaving = false, this.successEvent, this.errorEvent}): _photos = photos;
|
||||
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;
|
||||
@@ -222,6 +223,7 @@ class _BackgroundImageViewState implements BackgroundImageViewState {
|
||||
|
||||
@override@JsonKey() final bool isLoading;
|
||||
@override@JsonKey() final bool isSaving;
|
||||
@override final String? currentBackgroundId;
|
||||
@override final BackgroundImageSuccessEvent? successEvent;
|
||||
@override final BackgroundImageErrorEvent? errorEvent;
|
||||
|
||||
@@ -235,16 +237,16 @@ _$BackgroundImageViewStateCopyWith<_BackgroundImageViewState> get copyWith => __
|
||||
|
||||
@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.successEvent, successEvent) || other.successEvent == successEvent)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent));
|
||||
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,successEvent,errorEvent);
|
||||
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, successEvent: $successEvent, errorEvent: $errorEvent)';
|
||||
return 'BackgroundImageViewState(photos: $photos, isLoading: $isLoading, isSaving: $isSaving, currentBackgroundId: $currentBackgroundId, successEvent: $successEvent, errorEvent: $errorEvent)';
|
||||
}
|
||||
|
||||
|
||||
@@ -255,7 +257,7 @@ abstract mixin class _$BackgroundImageViewStateCopyWith<$Res> implements $Backgr
|
||||
factory _$BackgroundImageViewStateCopyWith(_BackgroundImageViewState value, $Res Function(_BackgroundImageViewState) _then) = __$BackgroundImageViewStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
List<PictureEntity> photos, bool isLoading, bool isSaving, BackgroundImageSuccessEvent? successEvent, BackgroundImageErrorEvent? errorEvent
|
||||
List<PictureEntity> photos, bool isLoading, bool isSaving, String? currentBackgroundId, BackgroundImageSuccessEvent? successEvent, BackgroundImageErrorEvent? errorEvent
|
||||
});
|
||||
|
||||
|
||||
@@ -272,12 +274,13 @@ class __$BackgroundImageViewStateCopyWithImpl<$Res>
|
||||
|
||||
/// 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? successEvent = freezed,Object? errorEvent = freezed,}) {
|
||||
@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,successEvent: freezed == successEvent ? _self.successEvent : successEvent // 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?,
|
||||
));
|
||||
|
||||
@@ -32,7 +32,7 @@ class HealthViewModel extends Notifier<HealthViewState> {
|
||||
Timer? _measureTimer;
|
||||
|
||||
static const int _historyPageSize = 20;
|
||||
static const int _measureDurationSeconds = 35;
|
||||
static const int _measureDurationSeconds = 60;
|
||||
|
||||
@override
|
||||
HealthViewState build() {
|
||||
@@ -295,6 +295,7 @@ class HealthViewModel extends Notifier<HealthViewState> {
|
||||
updatedSettings: updatedSettings,
|
||||
);
|
||||
if (!ref.mounted) return false;
|
||||
ref.syncDeviceSettings(device, updatedSettings);
|
||||
return true;
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return false;
|
||||
|
||||
@@ -8,13 +8,13 @@ part 'picture_entity.freezed.dart';
|
||||
abstract class PictureEntity with _$PictureEntity {
|
||||
const factory PictureEntity({
|
||||
required String id,
|
||||
required String deviceIdentificator,
|
||||
@Default('') String deviceIdentificator,
|
||||
String? imgType,
|
||||
String? timestamp,
|
||||
required String fileId,
|
||||
@Default('') String fileId,
|
||||
String? fileName,
|
||||
String? contentType,
|
||||
required int createdAt,
|
||||
@Default(0) int createdAt,
|
||||
Uint8List? fileBytes,
|
||||
}) = _PictureEntity;
|
||||
}
|
||||
|
||||
@@ -214,17 +214,17 @@ return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp
|
||||
|
||||
|
||||
class _PictureEntity implements PictureEntity {
|
||||
const _PictureEntity({required this.id, required this.deviceIdentificator, this.imgType, this.timestamp, required this.fileId, this.fileName, this.contentType, required this.createdAt, this.fileBytes});
|
||||
const _PictureEntity({required this.id, this.deviceIdentificator = '', this.imgType, this.timestamp, this.fileId = '', this.fileName, this.contentType, this.createdAt = 0, this.fileBytes});
|
||||
|
||||
|
||||
@override final String id;
|
||||
@override final String deviceIdentificator;
|
||||
@override@JsonKey() final String deviceIdentificator;
|
||||
@override final String? imgType;
|
||||
@override final String? timestamp;
|
||||
@override final String fileId;
|
||||
@override@JsonKey() final String fileId;
|
||||
@override final String? fileName;
|
||||
@override final String? contentType;
|
||||
@override final int createdAt;
|
||||
@override@JsonKey() final int createdAt;
|
||||
@override final Uint8List? fileBytes;
|
||||
|
||||
/// Create a copy of PictureEntity
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:lottie/lottie.dart';
|
||||
import 'package:device_management/src/features/remote_connection/presentation/state/remote_connection_view_model.dart';
|
||||
import 'package:device_management/src/features/remote_connection/presentation/state/remote_connection_view_state.dart';
|
||||
import 'package:device_management/src/features/remote_connection/presentation/widgets/show_picture_dialog.dart';
|
||||
import 'package:navigation/navigation.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
@@ -17,10 +19,33 @@ class RemoteCameraScreen extends ConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
|
||||
ref.listen(
|
||||
remoteConnectionViewModelProvider.select((s) => s.successMessage),
|
||||
(_, successMessage) {
|
||||
if (successMessage.isNotEmpty) {
|
||||
showTopSnackbar(context, message: context.translate(successMessage), type: MessageType.success);
|
||||
remoteConnectionViewModelProvider.select((s) => s.errorEvent),
|
||||
(_, next) {
|
||||
if (next != null) {
|
||||
final message = switch (next) {
|
||||
RemoteConnectionErrorEvent.takePicture =>
|
||||
context.translate(I18n.errorTakePicture),
|
||||
RemoteConnectionErrorEvent.fetchPhotos =>
|
||||
context.translate(I18n.errorFetchPhotos),
|
||||
RemoteConnectionErrorEvent.call =>
|
||||
context.translate(I18n.errorCall),
|
||||
RemoteConnectionErrorEvent.invalidPhone =>
|
||||
context.translate(I18n.errorMessagePhoneIsInvalid),
|
||||
};
|
||||
showTopSnackbar(context, message: message, type: MessageType.error);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
ref.listen(
|
||||
remoteConnectionViewModelProvider.select((s) => s.successEvent),
|
||||
(_, next) {
|
||||
if (next != null) {
|
||||
final message = switch (next) {
|
||||
RemoteConnectionSuccessEvent.photoTaken =>
|
||||
context.translate(I18n.photoTaken),
|
||||
};
|
||||
showTopSnackbar(context, message: message, type: MessageType.success);
|
||||
ref.read(remoteConnectionViewModelProvider.notifier).clearSuccess();
|
||||
}
|
||||
},
|
||||
@@ -28,20 +53,38 @@ class RemoteCameraScreen extends ConsumerWidget {
|
||||
|
||||
final theme = ref.watch(themePortProvider);
|
||||
|
||||
final isLoadingPictures = ref.watch(
|
||||
remoteConnectionViewModelProvider.select((s)=>s.isLoadingPictures)
|
||||
final isLoading = ref.watch(
|
||||
remoteConnectionViewModelProvider.select((s) => s.isLoadingPictures),
|
||||
);
|
||||
final isTakingPicture = ref.watch(
|
||||
remoteConnectionViewModelProvider.select((s)=>s.isTakingPicture)
|
||||
final isTaking = ref.watch(
|
||||
remoteConnectionViewModelProvider.select((s) => s.isTakingPicture),
|
||||
);
|
||||
final isWaiting = ref.watch(
|
||||
remoteConnectionViewModelProvider.select((s) => s.isWaitingForPhoto),
|
||||
);
|
||||
final countdown = ref.watch(
|
||||
remoteConnectionViewModelProvider.select((s) => s.photoCountdown),
|
||||
);
|
||||
|
||||
Widget body;
|
||||
if (isLoading || isTaking) {
|
||||
body = const Center(child: CircularProgressIndicator());
|
||||
} else if (isWaiting) {
|
||||
body = _WaitingForPhotoOverlay(
|
||||
remainingSeconds: countdown,
|
||||
theme: theme,
|
||||
);
|
||||
} else {
|
||||
body = const _GallerySection();
|
||||
}
|
||||
|
||||
return LegacyPageLayout(
|
||||
theme: theme,
|
||||
title: context.translate(I18n.remoteCamera),
|
||||
body: isLoadingPictures || isTakingPicture
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: const _GallerySection(),
|
||||
footer: _TakePictureSection(),
|
||||
body: body,
|
||||
footer: isWaiting
|
||||
? const SizedBox.shrink()
|
||||
: const _TakePictureSection(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -87,12 +130,12 @@ class _GallerySection extends ConsumerWidget {
|
||||
color: theme.getColorFor(ThemeCode.textTertiary)
|
||||
))
|
||||
),
|
||||
child: Image.network(
|
||||
pictures[index].fileId,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (_, __, ___) =>
|
||||
const Icon(Icons.broken_image, color: Colors.grey),
|
||||
)
|
||||
child: pictures[index].fileBytes != null
|
||||
? Image.memory(
|
||||
pictures[index].fileBytes!,
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: const Icon(Icons.broken_image, color: Colors.grey),
|
||||
)
|
||||
)
|
||||
),
|
||||
@@ -124,5 +167,53 @@ class _TakePictureSection extends ConsumerWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _WaitingForPhotoOverlay extends StatelessWidget {
|
||||
static const String _animationAsset =
|
||||
'assets/shared/animations/shooting_photo.json';
|
||||
|
||||
final int remainingSeconds;
|
||||
final ThemePort theme;
|
||||
|
||||
const _WaitingForPhotoOverlay({
|
||||
required this.remainingSeconds,
|
||||
required this.theme,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final primaryColor = theme.getColorFor(ThemeCode.legacyPrimary);
|
||||
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Lottie.asset(
|
||||
_animationAsset,
|
||||
width: 200,
|
||||
height: 200,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
context.translate(I18n.takingPhoto),
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
'${remainingSeconds}s',
|
||||
style: TextStyle(
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:device_management/src/core/providers/pictures_repository_provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:device_management/src/features/remote_connection/domain/entities/picture_entity.dart';
|
||||
import 'package:device_management/src/features/remote_connection/presentation/state/remote_connection_view_state.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
|
||||
import '../../../../core/domain/repositories/pictures_repository.dart';
|
||||
|
||||
@@ -18,8 +19,10 @@ class RemoteConnectionViewModel extends Notifier<RemoteConnectionViewState> {
|
||||
late final TextEditingController phoneController;
|
||||
late final CommandsRepository _commandsRepository;
|
||||
late final PicturesRepository _picturesRepository;
|
||||
Timer? _photoTimer;
|
||||
|
||||
static final RegExp _phoneRegex = RegExp(r'^\+?\d{6,15}$');
|
||||
static const int _photoWaitSeconds = 5;
|
||||
|
||||
@override
|
||||
RemoteConnectionViewState build() {
|
||||
@@ -60,12 +63,12 @@ class RemoteConnectionViewModel extends Notifier<RemoteConnectionViewState> {
|
||||
final text = phoneController.text;
|
||||
if (text == state.phone) return;
|
||||
|
||||
state = state.copyWith(phone: text, errorMessage: '');
|
||||
state = state.copyWith(phone: text, errorEvent: null);
|
||||
}
|
||||
|
||||
void updateDialCode(String value) {
|
||||
if (value == state.dialCode) return;
|
||||
state = state.copyWith(dialCode: value, errorMessage: '');
|
||||
state = state.copyWith(dialCode: value, errorEvent: null);
|
||||
}
|
||||
|
||||
void prevPicture() {
|
||||
@@ -95,18 +98,17 @@ class RemoteConnectionViewModel extends Notifier<RemoteConnectionViewState> {
|
||||
}
|
||||
|
||||
void clearSuccess() {
|
||||
state = state.copyWith(
|
||||
successMessage: '',
|
||||
);
|
||||
state = state.copyWith(successEvent: null);
|
||||
}
|
||||
|
||||
Future<void> takePicture() async {
|
||||
try {
|
||||
state = state.copyWith(
|
||||
isTakingPicture: true,
|
||||
successMessage: '',
|
||||
successEvent: null,
|
||||
errorEvent: null,
|
||||
);
|
||||
|
||||
|
||||
final request = SendCommandRequestModel(
|
||||
device: state.deviceId,
|
||||
command: DeviceCommand.requestPhoto,
|
||||
@@ -115,21 +117,59 @@ class RemoteConnectionViewModel extends Notifier<RemoteConnectionViewState> {
|
||||
await _commandsRepository.send(request: request);
|
||||
if (!ref.mounted) return;
|
||||
|
||||
state = state.copyWith(
|
||||
isTakingPicture: false,
|
||||
isWaitingForPhoto: true,
|
||||
photoCountdown: _photoWaitSeconds,
|
||||
);
|
||||
|
||||
_startPhotoCountdown();
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(
|
||||
isTakingPicture: false,
|
||||
errorEvent: RemoteConnectionErrorEvent.takePicture,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _startPhotoCountdown() {
|
||||
_photoTimer?.cancel();
|
||||
_photoTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
if (!ref.mounted) {
|
||||
timer.cancel();
|
||||
return;
|
||||
}
|
||||
final remaining = state.photoCountdown - 1;
|
||||
if (remaining <= 0) {
|
||||
timer.cancel();
|
||||
_photoTimer = null;
|
||||
_fetchPhotosAfterCapture();
|
||||
} else {
|
||||
state = state.copyWith(photoCountdown: remaining);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _fetchPhotosAfterCapture() async {
|
||||
try {
|
||||
final pictures = await _picturesRepository.getPictures(
|
||||
deviceId: state.deviceId,
|
||||
);
|
||||
if (!ref.mounted) return;
|
||||
|
||||
state = state.copyWith(
|
||||
isTakingPicture: false,
|
||||
isWaitingForPhoto: false,
|
||||
photoCountdown: 0,
|
||||
pictures: pictures,
|
||||
successMessage: I18n.photoTaken,
|
||||
successEvent: RemoteConnectionSuccessEvent.photoTaken,
|
||||
);
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(
|
||||
isTakingPicture: false,
|
||||
errorMessage: e.toString(),
|
||||
isWaitingForPhoto: false,
|
||||
photoCountdown: 0,
|
||||
errorEvent: RemoteConnectionErrorEvent.fetchPhotos,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -137,12 +177,8 @@ class RemoteConnectionViewModel extends Notifier<RemoteConnectionViewState> {
|
||||
Future<void> call() async {
|
||||
final phone = phoneController.text;
|
||||
final dialCode = state.dialCode;
|
||||
if (phone.isEmpty){
|
||||
state = state.copyWith(errorMessage: I18n.errorMessagePhoneIsInvalid);
|
||||
return;
|
||||
}
|
||||
if (!_phoneRegex.hasMatch(phone)) {
|
||||
state = state.copyWith(errorMessage: I18n.errorMessagePhoneIsInvalid);
|
||||
if (phone.isEmpty || !_phoneRegex.hasMatch(phone)) {
|
||||
state = state.copyWith(errorEvent: RemoteConnectionErrorEvent.invalidPhone);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -163,7 +199,7 @@ class RemoteConnectionViewModel extends Notifier<RemoteConnectionViewState> {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(
|
||||
isCalling: false,
|
||||
errorMessage: e.toString(),
|
||||
errorEvent: RemoteConnectionErrorEvent.call,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@ import 'package:device_management/src/features/remote_connection/domain/entities
|
||||
|
||||
part 'remote_connection_view_state.freezed.dart';
|
||||
|
||||
enum RemoteConnectionErrorEvent { takePicture, fetchPhotos, call, invalidPhone }
|
||||
enum RemoteConnectionSuccessEvent { photoTaken }
|
||||
|
||||
@freezed
|
||||
abstract class RemoteConnectionViewState with _$RemoteConnectionViewState {
|
||||
const factory RemoteConnectionViewState({
|
||||
@@ -13,8 +16,10 @@ abstract class RemoteConnectionViewState with _$RemoteConnectionViewState {
|
||||
@Default(0) int pictureIndex,
|
||||
@Default(true) bool isLoadingPictures,
|
||||
@Default(false) bool isTakingPicture,
|
||||
@Default(false) bool isWaitingForPhoto,
|
||||
@Default(0) int photoCountdown,
|
||||
@Default(false) bool isCalling,
|
||||
@Default('') String errorMessage,
|
||||
@Default('') String successMessage
|
||||
RemoteConnectionErrorEvent? errorEvent,
|
||||
RemoteConnectionSuccessEvent? successEvent,
|
||||
}) = _RemoteConnectionViewState;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$RemoteConnectionViewState {
|
||||
|
||||
String get deviceId; String get dialCode; String get phone; List<PictureEntity> get pictures; int get pictureIndex; bool get isLoadingPictures; bool get isTakingPicture; bool get isCalling; String get errorMessage; String get successMessage;
|
||||
String get deviceId; String get dialCode; String get phone; List<PictureEntity> get pictures; int get pictureIndex; bool get isLoadingPictures; bool get isTakingPicture; bool get isWaitingForPhoto; int get photoCountdown; bool get isCalling; RemoteConnectionErrorEvent? get errorEvent; RemoteConnectionSuccessEvent? get successEvent;
|
||||
/// Create a copy of RemoteConnectionViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -25,16 +25,16 @@ $RemoteConnectionViewStateCopyWith<RemoteConnectionViewState> get copyWith => _$
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is RemoteConnectionViewState&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.phone, phone) || other.phone == phone)&&const DeepCollectionEquality().equals(other.pictures, pictures)&&(identical(other.pictureIndex, pictureIndex) || other.pictureIndex == pictureIndex)&&(identical(other.isLoadingPictures, isLoadingPictures) || other.isLoadingPictures == isLoadingPictures)&&(identical(other.isTakingPicture, isTakingPicture) || other.isTakingPicture == isTakingPicture)&&(identical(other.isCalling, isCalling) || other.isCalling == isCalling)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.successMessage, successMessage) || other.successMessage == successMessage));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is RemoteConnectionViewState&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.phone, phone) || other.phone == phone)&&const DeepCollectionEquality().equals(other.pictures, pictures)&&(identical(other.pictureIndex, pictureIndex) || other.pictureIndex == pictureIndex)&&(identical(other.isLoadingPictures, isLoadingPictures) || other.isLoadingPictures == isLoadingPictures)&&(identical(other.isTakingPicture, isTakingPicture) || other.isTakingPicture == isTakingPicture)&&(identical(other.isWaitingForPhoto, isWaitingForPhoto) || other.isWaitingForPhoto == isWaitingForPhoto)&&(identical(other.photoCountdown, photoCountdown) || other.photoCountdown == photoCountdown)&&(identical(other.isCalling, isCalling) || other.isCalling == isCalling)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent)&&(identical(other.successEvent, successEvent) || other.successEvent == successEvent));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,deviceId,dialCode,phone,const DeepCollectionEquality().hash(pictures),pictureIndex,isLoadingPictures,isTakingPicture,isCalling,errorMessage,successMessage);
|
||||
int get hashCode => Object.hash(runtimeType,deviceId,dialCode,phone,const DeepCollectionEquality().hash(pictures),pictureIndex,isLoadingPictures,isTakingPicture,isWaitingForPhoto,photoCountdown,isCalling,errorEvent,successEvent);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'RemoteConnectionViewState(deviceId: $deviceId, dialCode: $dialCode, phone: $phone, pictures: $pictures, pictureIndex: $pictureIndex, isLoadingPictures: $isLoadingPictures, isTakingPicture: $isTakingPicture, isCalling: $isCalling, errorMessage: $errorMessage, successMessage: $successMessage)';
|
||||
return 'RemoteConnectionViewState(deviceId: $deviceId, dialCode: $dialCode, phone: $phone, pictures: $pictures, pictureIndex: $pictureIndex, isLoadingPictures: $isLoadingPictures, isTakingPicture: $isTakingPicture, isWaitingForPhoto: $isWaitingForPhoto, photoCountdown: $photoCountdown, isCalling: $isCalling, errorEvent: $errorEvent, successEvent: $successEvent)';
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ abstract mixin class $RemoteConnectionViewStateCopyWith<$Res> {
|
||||
factory $RemoteConnectionViewStateCopyWith(RemoteConnectionViewState value, $Res Function(RemoteConnectionViewState) _then) = _$RemoteConnectionViewStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String deviceId, String dialCode, String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isCalling, String errorMessage, String successMessage
|
||||
String deviceId, String dialCode, String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isWaitingForPhoto, int photoCountdown, bool isCalling, RemoteConnectionErrorEvent? errorEvent, RemoteConnectionSuccessEvent? successEvent
|
||||
});
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ class _$RemoteConnectionViewStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of RemoteConnectionViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? deviceId = null,Object? dialCode = null,Object? phone = null,Object? pictures = null,Object? pictureIndex = null,Object? isLoadingPictures = null,Object? isTakingPicture = null,Object? isCalling = null,Object? errorMessage = null,Object? successMessage = null,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? deviceId = null,Object? dialCode = null,Object? phone = null,Object? pictures = null,Object? pictureIndex = null,Object? isLoadingPictures = null,Object? isTakingPicture = null,Object? isWaitingForPhoto = null,Object? photoCountdown = null,Object? isCalling = null,Object? errorEvent = freezed,Object? successEvent = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
||||
as String,dialCode: null == dialCode ? _self.dialCode : dialCode // ignore: cast_nullable_to_non_nullable
|
||||
@@ -71,10 +71,12 @@ as String,pictures: null == pictures ? _self.pictures : pictures // ignore: cast
|
||||
as List<PictureEntity>,pictureIndex: null == pictureIndex ? _self.pictureIndex : pictureIndex // ignore: cast_nullable_to_non_nullable
|
||||
as int,isLoadingPictures: null == isLoadingPictures ? _self.isLoadingPictures : isLoadingPictures // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isTakingPicture: null == isTakingPicture ? _self.isTakingPicture : isTakingPicture // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isCalling: null == isCalling ? _self.isCalling : isCalling // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,successMessage: null == successMessage ? _self.successMessage : successMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
as bool,isWaitingForPhoto: null == isWaitingForPhoto ? _self.isWaitingForPhoto : isWaitingForPhoto // ignore: cast_nullable_to_non_nullable
|
||||
as bool,photoCountdown: null == photoCountdown ? _self.photoCountdown : photoCountdown // ignore: cast_nullable_to_non_nullable
|
||||
as int,isCalling: null == isCalling ? _self.isCalling : isCalling // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorEvent: freezed == errorEvent ? _self.errorEvent : errorEvent // ignore: cast_nullable_to_non_nullable
|
||||
as RemoteConnectionErrorEvent?,successEvent: freezed == successEvent ? _self.successEvent : successEvent // ignore: cast_nullable_to_non_nullable
|
||||
as RemoteConnectionSuccessEvent?,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -159,10 +161,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String deviceId, String dialCode, String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isCalling, String errorMessage, String successMessage)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String deviceId, String dialCode, String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isWaitingForPhoto, int photoCountdown, bool isCalling, RemoteConnectionErrorEvent? errorEvent, RemoteConnectionSuccessEvent? successEvent)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _RemoteConnectionViewState() when $default != null:
|
||||
return $default(_that.deviceId,_that.dialCode,_that.phone,_that.pictures,_that.pictureIndex,_that.isLoadingPictures,_that.isTakingPicture,_that.isCalling,_that.errorMessage,_that.successMessage);case _:
|
||||
return $default(_that.deviceId,_that.dialCode,_that.phone,_that.pictures,_that.pictureIndex,_that.isLoadingPictures,_that.isTakingPicture,_that.isWaitingForPhoto,_that.photoCountdown,_that.isCalling,_that.errorEvent,_that.successEvent);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@@ -180,10 +182,10 @@ return $default(_that.deviceId,_that.dialCode,_that.phone,_that.pictures,_that.p
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String deviceId, String dialCode, String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isCalling, String errorMessage, String successMessage) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String deviceId, String dialCode, String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isWaitingForPhoto, int photoCountdown, bool isCalling, RemoteConnectionErrorEvent? errorEvent, RemoteConnectionSuccessEvent? successEvent) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _RemoteConnectionViewState():
|
||||
return $default(_that.deviceId,_that.dialCode,_that.phone,_that.pictures,_that.pictureIndex,_that.isLoadingPictures,_that.isTakingPicture,_that.isCalling,_that.errorMessage,_that.successMessage);case _:
|
||||
return $default(_that.deviceId,_that.dialCode,_that.phone,_that.pictures,_that.pictureIndex,_that.isLoadingPictures,_that.isTakingPicture,_that.isWaitingForPhoto,_that.photoCountdown,_that.isCalling,_that.errorEvent,_that.successEvent);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@@ -200,10 +202,10 @@ return $default(_that.deviceId,_that.dialCode,_that.phone,_that.pictures,_that.p
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String deviceId, String dialCode, String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isCalling, String errorMessage, String successMessage)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String deviceId, String dialCode, String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isWaitingForPhoto, int photoCountdown, bool isCalling, RemoteConnectionErrorEvent? errorEvent, RemoteConnectionSuccessEvent? successEvent)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _RemoteConnectionViewState() when $default != null:
|
||||
return $default(_that.deviceId,_that.dialCode,_that.phone,_that.pictures,_that.pictureIndex,_that.isLoadingPictures,_that.isTakingPicture,_that.isCalling,_that.errorMessage,_that.successMessage);case _:
|
||||
return $default(_that.deviceId,_that.dialCode,_that.phone,_that.pictures,_that.pictureIndex,_that.isLoadingPictures,_that.isTakingPicture,_that.isWaitingForPhoto,_that.photoCountdown,_that.isCalling,_that.errorEvent,_that.successEvent);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@@ -215,7 +217,7 @@ return $default(_that.deviceId,_that.dialCode,_that.phone,_that.pictures,_that.p
|
||||
|
||||
|
||||
class _RemoteConnectionViewState implements RemoteConnectionViewState {
|
||||
const _RemoteConnectionViewState({this.deviceId = '', this.dialCode = '+34', this.phone = '', final List<PictureEntity> pictures = const [], this.pictureIndex = 0, this.isLoadingPictures = true, this.isTakingPicture = false, this.isCalling = false, this.errorMessage = '', this.successMessage = ''}): _pictures = pictures;
|
||||
const _RemoteConnectionViewState({this.deviceId = '', this.dialCode = '+34', this.phone = '', final List<PictureEntity> pictures = const [], this.pictureIndex = 0, this.isLoadingPictures = true, this.isTakingPicture = false, this.isWaitingForPhoto = false, this.photoCountdown = 0, this.isCalling = false, this.errorEvent, this.successEvent}): _pictures = pictures;
|
||||
|
||||
|
||||
@override@JsonKey() final String deviceId;
|
||||
@@ -231,9 +233,11 @@ class _RemoteConnectionViewState implements RemoteConnectionViewState {
|
||||
@override@JsonKey() final int pictureIndex;
|
||||
@override@JsonKey() final bool isLoadingPictures;
|
||||
@override@JsonKey() final bool isTakingPicture;
|
||||
@override@JsonKey() final bool isWaitingForPhoto;
|
||||
@override@JsonKey() final int photoCountdown;
|
||||
@override@JsonKey() final bool isCalling;
|
||||
@override@JsonKey() final String errorMessage;
|
||||
@override@JsonKey() final String successMessage;
|
||||
@override final RemoteConnectionErrorEvent? errorEvent;
|
||||
@override final RemoteConnectionSuccessEvent? successEvent;
|
||||
|
||||
/// Create a copy of RemoteConnectionViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -245,16 +249,16 @@ _$RemoteConnectionViewStateCopyWith<_RemoteConnectionViewState> get copyWith =>
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _RemoteConnectionViewState&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.phone, phone) || other.phone == phone)&&const DeepCollectionEquality().equals(other._pictures, _pictures)&&(identical(other.pictureIndex, pictureIndex) || other.pictureIndex == pictureIndex)&&(identical(other.isLoadingPictures, isLoadingPictures) || other.isLoadingPictures == isLoadingPictures)&&(identical(other.isTakingPicture, isTakingPicture) || other.isTakingPicture == isTakingPicture)&&(identical(other.isCalling, isCalling) || other.isCalling == isCalling)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.successMessage, successMessage) || other.successMessage == successMessage));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _RemoteConnectionViewState&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.phone, phone) || other.phone == phone)&&const DeepCollectionEquality().equals(other._pictures, _pictures)&&(identical(other.pictureIndex, pictureIndex) || other.pictureIndex == pictureIndex)&&(identical(other.isLoadingPictures, isLoadingPictures) || other.isLoadingPictures == isLoadingPictures)&&(identical(other.isTakingPicture, isTakingPicture) || other.isTakingPicture == isTakingPicture)&&(identical(other.isWaitingForPhoto, isWaitingForPhoto) || other.isWaitingForPhoto == isWaitingForPhoto)&&(identical(other.photoCountdown, photoCountdown) || other.photoCountdown == photoCountdown)&&(identical(other.isCalling, isCalling) || other.isCalling == isCalling)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent)&&(identical(other.successEvent, successEvent) || other.successEvent == successEvent));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,deviceId,dialCode,phone,const DeepCollectionEquality().hash(_pictures),pictureIndex,isLoadingPictures,isTakingPicture,isCalling,errorMessage,successMessage);
|
||||
int get hashCode => Object.hash(runtimeType,deviceId,dialCode,phone,const DeepCollectionEquality().hash(_pictures),pictureIndex,isLoadingPictures,isTakingPicture,isWaitingForPhoto,photoCountdown,isCalling,errorEvent,successEvent);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'RemoteConnectionViewState(deviceId: $deviceId, dialCode: $dialCode, phone: $phone, pictures: $pictures, pictureIndex: $pictureIndex, isLoadingPictures: $isLoadingPictures, isTakingPicture: $isTakingPicture, isCalling: $isCalling, errorMessage: $errorMessage, successMessage: $successMessage)';
|
||||
return 'RemoteConnectionViewState(deviceId: $deviceId, dialCode: $dialCode, phone: $phone, pictures: $pictures, pictureIndex: $pictureIndex, isLoadingPictures: $isLoadingPictures, isTakingPicture: $isTakingPicture, isWaitingForPhoto: $isWaitingForPhoto, photoCountdown: $photoCountdown, isCalling: $isCalling, errorEvent: $errorEvent, successEvent: $successEvent)';
|
||||
}
|
||||
|
||||
|
||||
@@ -265,7 +269,7 @@ abstract mixin class _$RemoteConnectionViewStateCopyWith<$Res> implements $Remot
|
||||
factory _$RemoteConnectionViewStateCopyWith(_RemoteConnectionViewState value, $Res Function(_RemoteConnectionViewState) _then) = __$RemoteConnectionViewStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String deviceId, String dialCode, String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isCalling, String errorMessage, String successMessage
|
||||
String deviceId, String dialCode, String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isWaitingForPhoto, int photoCountdown, bool isCalling, RemoteConnectionErrorEvent? errorEvent, RemoteConnectionSuccessEvent? successEvent
|
||||
});
|
||||
|
||||
|
||||
@@ -282,7 +286,7 @@ class __$RemoteConnectionViewStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of RemoteConnectionViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? deviceId = null,Object? dialCode = null,Object? phone = null,Object? pictures = null,Object? pictureIndex = null,Object? isLoadingPictures = null,Object? isTakingPicture = null,Object? isCalling = null,Object? errorMessage = null,Object? successMessage = null,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? deviceId = null,Object? dialCode = null,Object? phone = null,Object? pictures = null,Object? pictureIndex = null,Object? isLoadingPictures = null,Object? isTakingPicture = null,Object? isWaitingForPhoto = null,Object? photoCountdown = null,Object? isCalling = null,Object? errorEvent = freezed,Object? successEvent = freezed,}) {
|
||||
return _then(_RemoteConnectionViewState(
|
||||
deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
||||
as String,dialCode: null == dialCode ? _self.dialCode : dialCode // ignore: cast_nullable_to_non_nullable
|
||||
@@ -291,10 +295,12 @@ as String,pictures: null == pictures ? _self._pictures : pictures // ignore: cas
|
||||
as List<PictureEntity>,pictureIndex: null == pictureIndex ? _self.pictureIndex : pictureIndex // ignore: cast_nullable_to_non_nullable
|
||||
as int,isLoadingPictures: null == isLoadingPictures ? _self.isLoadingPictures : isLoadingPictures // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isTakingPicture: null == isTakingPicture ? _self.isTakingPicture : isTakingPicture // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isCalling: null == isCalling ? _self.isCalling : isCalling // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,successMessage: null == successMessage ? _self.successMessage : successMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
as bool,isWaitingForPhoto: null == isWaitingForPhoto ? _self.isWaitingForPhoto : isWaitingForPhoto // ignore: cast_nullable_to_non_nullable
|
||||
as bool,photoCountdown: null == photoCountdown ? _self.photoCountdown : photoCountdown // ignore: cast_nullable_to_non_nullable
|
||||
as int,isCalling: null == isCalling ? _self.isCalling : isCalling // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorEvent: freezed == errorEvent ? _self.errorEvent : errorEvent // ignore: cast_nullable_to_non_nullable
|
||||
as RemoteConnectionErrorEvent?,successEvent: freezed == successEvent ? _self.successEvent : successEvent // ignore: cast_nullable_to_non_nullable
|
||||
as RemoteConnectionSuccessEvent?,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ class ShowPictureDialog extends ConsumerWidget {
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _PictureSection(fileId: picture.fileId),
|
||||
child: _PictureSection(picture: picture),
|
||||
),
|
||||
_MetadataSection(picture: picture),
|
||||
_ControlsSection(
|
||||
@@ -59,24 +59,22 @@ class ShowPictureDialog extends ConsumerWidget {
|
||||
}
|
||||
|
||||
class _PictureSection extends StatelessWidget {
|
||||
final String fileId;
|
||||
final PictureEntity picture;
|
||||
|
||||
const _PictureSection({required this.fileId});
|
||||
const _PictureSection({required this.picture});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Image.network(
|
||||
fileId,
|
||||
fit: BoxFit.contain,
|
||||
loadingBuilder: (context, child, loadingProgress) {
|
||||
if (loadingProgress == null) return child;
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
},
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return const Icon(Icons.broken_image, size: 64, color: Colors.grey);
|
||||
},
|
||||
),
|
||||
if (picture.fileBytes != null) {
|
||||
return Center(
|
||||
child: Image.memory(
|
||||
picture.fileBytes!,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
);
|
||||
}
|
||||
return const Center(
|
||||
child: Icon(Icons.broken_image, size: 64, color: Colors.grey),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:device_management/src/features/remote_connection/presentation/state/remote_connection_view_model.dart';
|
||||
import 'package:device_management/src/features/remote_connection/presentation/state/remote_connection_view_state.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
|
||||
@@ -13,26 +14,39 @@ class SpyCallDialog extends ConsumerWidget {
|
||||
final theme = ref.watch(themePortProvider);
|
||||
final vm = ref.read(remoteConnectionViewModelProvider.notifier);
|
||||
|
||||
ref.listen(remoteConnectionViewModelProvider.select((s) => s.errorMessage),
|
||||
(_, msg) {
|
||||
if (msg.isNotEmpty) {
|
||||
showTopSnackbar(context,
|
||||
message: context.translate(msg), type: MessageType.error);
|
||||
}
|
||||
});
|
||||
|
||||
ref.listen(remoteConnectionViewModelProvider.select((s) => s.isCalling),
|
||||
(prev, isCalling) {
|
||||
if (prev == true && !isCalling) {
|
||||
final error = ref.read(remoteConnectionViewModelProvider).errorMessage;
|
||||
if (error.isEmpty && context.mounted) {
|
||||
Navigator.pop(context);
|
||||
showTopSnackbar(context,
|
||||
message: context.translate(I18n.remoteListening),
|
||||
type: MessageType.success);
|
||||
ref.listen(
|
||||
remoteConnectionViewModelProvider.select((s) => s.errorEvent),
|
||||
(_, next) {
|
||||
if (next != null) {
|
||||
final message = switch (next) {
|
||||
RemoteConnectionErrorEvent.invalidPhone =>
|
||||
context.translate(I18n.errorMessagePhoneIsInvalid),
|
||||
RemoteConnectionErrorEvent.call =>
|
||||
context.translate(I18n.errorCall),
|
||||
RemoteConnectionErrorEvent.takePicture =>
|
||||
context.translate(I18n.errorTakePicture),
|
||||
RemoteConnectionErrorEvent.fetchPhotos =>
|
||||
context.translate(I18n.errorFetchPhotos),
|
||||
};
|
||||
showTopSnackbar(context, message: message, type: MessageType.error);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
ref.listen(
|
||||
remoteConnectionViewModelProvider.select((s) => s.isCalling),
|
||||
(prev, isCalling) {
|
||||
if (prev == true && !isCalling) {
|
||||
final error = ref.read(remoteConnectionViewModelProvider).errorEvent;
|
||||
if (error == null && context.mounted) {
|
||||
Navigator.pop(context);
|
||||
showTopSnackbar(context,
|
||||
message: context.translate(I18n.remoteListening),
|
||||
type: MessageType.success);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return Container(
|
||||
padding: SizeUtils.getByScreen(
|
||||
|
||||
@@ -64,6 +64,7 @@ class VolumeControlViewModel extends Notifier<VolumeControlViewState> {
|
||||
);
|
||||
|
||||
if (!ref.mounted) return;
|
||||
ref.syncDeviceSettings(device, updatedSettings);
|
||||
state = state.copyWith(isLoading: false, isComplete: true);
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
|
||||
@@ -24,7 +24,7 @@ class LocationViewModel extends Notifier<LocationViewState> {
|
||||
@override
|
||||
LocationViewState build() {
|
||||
_locationRepository = ref.read(locationRepositoryProvider);
|
||||
final device = ref.watch(selectedDeviceProvider);
|
||||
final device = ref.read(selectedDeviceProvider);
|
||||
if (device != null) {
|
||||
_fetchData(device.id);
|
||||
}
|
||||
@@ -282,6 +282,7 @@ class LocationViewModel extends Notifier<LocationViewState> {
|
||||
);
|
||||
|
||||
if (!ref.mounted) return false;
|
||||
ref.syncDeviceSettings(device, updatedSettings);
|
||||
state = state.copyWith(isSubmitting: false);
|
||||
return true;
|
||||
} catch (e) {
|
||||
|
||||
@@ -566,7 +566,7 @@ class _LocationMapState extends ConsumerState<LocationMap>
|
||||
const SizedBox(height: 8),
|
||||
FrequencySelector(
|
||||
currentFrequency:
|
||||
widget.selectedDevice!.settings.frequency,
|
||||
ref.watch(selectedDeviceProvider)?.settings.frequency ?? 60,
|
||||
options:
|
||||
widget.selectedDevice!.capabilities!.location!.options,
|
||||
onChanged: _updateFrequency,
|
||||
|
||||
@@ -57,6 +57,7 @@ class AlertsViewModel extends Notifier<AlertsViewState> {
|
||||
updatedSettings: updatedSettings,
|
||||
);
|
||||
if (!ref.mounted) return;
|
||||
ref.syncDeviceSettings(device, updatedSettings);
|
||||
state = state.copyWith(isSaving: false, saveSuccess: true);
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
|
||||
@@ -41,6 +41,7 @@ class BatteryViewModel extends Notifier<BatteryViewState> {
|
||||
updatedSettings: updatedSettings,
|
||||
);
|
||||
if (!ref.mounted) return;
|
||||
ref.syncDeviceSettings(device, updatedSettings);
|
||||
state = state.copyWith(
|
||||
nightMode: value,
|
||||
isSaving: false,
|
||||
|
||||
@@ -53,6 +53,7 @@ class DisableFunctionsViewModel extends Notifier<DisableFunctionsViewState> {
|
||||
updatedSettings: updatedSettings,
|
||||
);
|
||||
if (!ref.mounted) return;
|
||||
ref.syncDeviceSettings(device, updatedSettings);
|
||||
state = state.copyWith(isSaving: false, saveSuccess: true);
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
|
||||
@@ -57,6 +57,10 @@ class LanguageViewModel extends Notifier<LanguageViewState> {
|
||||
);
|
||||
|
||||
if (!ref.mounted) return;
|
||||
ref.syncDeviceSettings(
|
||||
device,
|
||||
device.settings.copyWith(language: state.language),
|
||||
);
|
||||
state = state.copyWith(isLoading: false, isComplete: true);
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
|
||||
@@ -60,6 +60,7 @@ class SoundViewModel extends Notifier<SoundViewState> {
|
||||
);
|
||||
|
||||
if (!ref.mounted) return;
|
||||
ref.syncDeviceSettings(device, updatedSettings);
|
||||
state = state.copyWith(isLoading: false, isComplete: true);
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
|
||||
@@ -52,6 +52,7 @@ class TimezoneViewModel extends Notifier<TimezoneViewState> {
|
||||
updatedSettings: updatedSettings,
|
||||
);
|
||||
if (!ref.mounted) return;
|
||||
ref.syncDeviceSettings(device, updatedSettings);
|
||||
state = state.copyWith(isSaving: false, saveSuccess: true);
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
|
||||
@@ -14,7 +14,7 @@ class CommandsRemoteDatasourceImpl implements CommandsRemoteDatasource {
|
||||
required SendCommandRequestModel request,
|
||||
}) async {
|
||||
await safeCall(
|
||||
() => _repository.post<Map<String, dynamic>>(
|
||||
() => _repository.post<dynamic>(
|
||||
'/commands',
|
||||
body: request.toJson(),
|
||||
),
|
||||
|
||||
@@ -16,3 +16,11 @@ class SelectedDeviceNotifier extends Notifier<DeviceEntity?> {
|
||||
state = device;
|
||||
}
|
||||
}
|
||||
|
||||
extension DeviceSettingsSync on Ref {
|
||||
void syncDeviceSettings(DeviceEntity device, DeviceSettingsEntity settings) {
|
||||
read(selectedDeviceProvider.notifier).setSelectedDevice(
|
||||
device.copyWith(settings: settings),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user