Added locate device screen, state and endpoints

This commit is contained in:
2026-02-12 15:48:11 +01:00
parent f8ff70c35d
commit a53cfb24ea
25 changed files with 1295 additions and 32 deletions

View File

@@ -1,3 +1,4 @@
import 'package:functions/src/core/data/models/entities/locate_device_request_entity.dart';
import 'package:functions/src/features/contacts/domain/entities/contact_entity.dart';
import 'package:functions/src/features/remote_connection/domain/entities/picture_entity.dart';
@@ -7,4 +8,6 @@ abstract class FunctionsRemoteDatasource {
Future<List<PictureEntity>> getPictures({required String userId});
Future<PictureEntity> takePicture({required String userId});
Future<void> locateDevice({required LocateDeviceRequestEntity request});
}

View File

@@ -3,6 +3,8 @@ import 'dart:convert';
import 'package:dio/dio.dart';
// import 'package:flutter/material.dart';
import 'package:functions/src/core/data/datasources/functions_remote_datasource.dart';
import 'package:functions/src/core/data/models/entities/locate_device_request_entity.dart';
import 'package:functions/src/core/data/models/send_command_request_model.dart';
import 'package:functions/src/features/contacts/domain/entities/contact_entity.dart';
import 'package:functions/src/features/remote_connection/domain/entities/picture_entity.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
@@ -92,6 +94,22 @@ class FunctionsRemoteDatasourceImpl implements FunctionsRemoteDatasource {
}*/
return PictureEntity(id: '1', userId: '1111', createdAt: 1111);
}
@override
Future<void> locateDevice({required LocateDeviceRequestEntity request}) async {
try {
final body = request.toModel().toJson();
await _repository.post<void>(
'/commands',
body: body,
);
} on DioException catch (error) {
throw _mapDioError(
error,
defaultMessage: error.response?.data ?? 'Error to locate device',
);
}
}
}
Exception _mapDioError(DioException error, {required String defaultMessage}) {

View File

@@ -0,0 +1,11 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'locate_device_request_entity.freezed.dart';
@freezed
abstract class LocateDeviceRequestEntity with _$LocateDeviceRequestEntity {
const factory LocateDeviceRequestEntity({
required String deviceName,
}) = _LocateDeviceRequestEntity;
}

View File

@@ -0,0 +1,274 @@
// 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 'locate_device_request_entity.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$LocateDeviceRequestEntity {
String get deviceName; dynamic get command;
/// Create a copy of LocateDeviceRequestEntity
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$LocateDeviceRequestEntityCopyWith<LocateDeviceRequestEntity> get copyWith => _$LocateDeviceRequestEntityCopyWithImpl<LocateDeviceRequestEntity>(this as LocateDeviceRequestEntity, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is LocateDeviceRequestEntity&&(identical(other.deviceName, deviceName) || other.deviceName == deviceName)&&const DeepCollectionEquality().equals(other.command, command));
}
@override
int get hashCode => Object.hash(runtimeType,deviceName,const DeepCollectionEquality().hash(command));
@override
String toString() {
return 'LocateDeviceRequestEntity(deviceName: $deviceName, command: $command)';
}
}
/// @nodoc
abstract mixin class $LocateDeviceRequestEntityCopyWith<$Res> {
factory $LocateDeviceRequestEntityCopyWith(LocateDeviceRequestEntity value, $Res Function(LocateDeviceRequestEntity) _then) = _$LocateDeviceRequestEntityCopyWithImpl;
@useResult
$Res call({
String deviceName, dynamic command
});
}
/// @nodoc
class _$LocateDeviceRequestEntityCopyWithImpl<$Res>
implements $LocateDeviceRequestEntityCopyWith<$Res> {
_$LocateDeviceRequestEntityCopyWithImpl(this._self, this._then);
final LocateDeviceRequestEntity _self;
final $Res Function(LocateDeviceRequestEntity) _then;
/// Create a copy of LocateDeviceRequestEntity
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? deviceName = null,Object? command = freezed,}) {
return _then(_self.copyWith(
deviceName: null == deviceName ? _self.deviceName : deviceName // ignore: cast_nullable_to_non_nullable
as String,command: freezed == command ? _self.command : command // ignore: cast_nullable_to_non_nullable
as dynamic,
));
}
}
/// Adds pattern-matching-related methods to [LocateDeviceRequestEntity].
extension LocateDeviceRequestEntityPatterns on LocateDeviceRequestEntity {
/// 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( _LocateDeviceRequestEntity value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _LocateDeviceRequestEntity() 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( _LocateDeviceRequestEntity value) $default,){
final _that = this;
switch (_that) {
case _LocateDeviceRequestEntity():
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( _LocateDeviceRequestEntity value)? $default,){
final _that = this;
switch (_that) {
case _LocateDeviceRequestEntity() 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( String deviceName, dynamic command)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _LocateDeviceRequestEntity() when $default != null:
return $default(_that.deviceName,_that.command);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( String deviceName, dynamic command) $default,) {final _that = this;
switch (_that) {
case _LocateDeviceRequestEntity():
return $default(_that.deviceName,_that.command);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( String deviceName, dynamic command)? $default,) {final _that = this;
switch (_that) {
case _LocateDeviceRequestEntity() when $default != null:
return $default(_that.deviceName,_that.command);case _:
return null;
}
}
}
/// @nodoc
class _LocateDeviceRequestEntity implements LocateDeviceRequestEntity {
const _LocateDeviceRequestEntity({required this.deviceName, this.command});
@override final String deviceName;
@override final dynamic command;
/// Create a copy of LocateDeviceRequestEntity
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$LocateDeviceRequestEntityCopyWith<_LocateDeviceRequestEntity> get copyWith => __$LocateDeviceRequestEntityCopyWithImpl<_LocateDeviceRequestEntity>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _LocateDeviceRequestEntity&&(identical(other.deviceName, deviceName) || other.deviceName == deviceName)&&const DeepCollectionEquality().equals(other.command, command));
}
@override
int get hashCode => Object.hash(runtimeType,deviceName,const DeepCollectionEquality().hash(command));
@override
String toString() {
return 'LocateDeviceRequestEntity(deviceName: $deviceName, command: $command)';
}
}
/// @nodoc
abstract mixin class _$LocateDeviceRequestEntityCopyWith<$Res> implements $LocateDeviceRequestEntityCopyWith<$Res> {
factory _$LocateDeviceRequestEntityCopyWith(_LocateDeviceRequestEntity value, $Res Function(_LocateDeviceRequestEntity) _then) = __$LocateDeviceRequestEntityCopyWithImpl;
@override @useResult
$Res call({
String deviceName, dynamic command
});
}
/// @nodoc
class __$LocateDeviceRequestEntityCopyWithImpl<$Res>
implements _$LocateDeviceRequestEntityCopyWith<$Res> {
__$LocateDeviceRequestEntityCopyWithImpl(this._self, this._then);
final _LocateDeviceRequestEntity _self;
final $Res Function(_LocateDeviceRequestEntity) _then;
/// Create a copy of LocateDeviceRequestEntity
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? deviceName = null,Object? command = freezed,}) {
return _then(_LocateDeviceRequestEntity(
deviceName: null == deviceName ? _self.deviceName : deviceName // ignore: cast_nullable_to_non_nullable
as String,command: freezed == command ? _self.command : command // ignore: cast_nullable_to_non_nullable
as dynamic,
));
}
}
// dart format on

View File

@@ -0,0 +1,24 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:functions/src/core/data/models/entities/locate_device_request_entity.dart';
part 'send_command_request_model.freezed.dart';
part 'send_command_request_model.g.dart';
@freezed
abstract class SendCommandRequestModel with _$SendCommandRequestModel {
const factory SendCommandRequestModel({
required String deviceName,
required String command,
}) = _SendCommandRequestModel;
factory SendCommandRequestModel.fromJson(Map<String, dynamic> json) =>
_$SendCommandRequestModelFromJson(json);
}
extension SendCommandRequestModelMapper on LocateDeviceRequestEntity {
SendCommandRequestModel toModel() => SendCommandRequestModel(
deviceName: deviceName,
command: 'FIND_DEVICE',
);
}

View File

@@ -0,0 +1,280 @@
// 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 'send_command_request_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$SendCommandRequestModel {
String get deviceName; String get command;
/// Create a copy of SendCommandRequestModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$SendCommandRequestModelCopyWith<SendCommandRequestModel> get copyWith => _$SendCommandRequestModelCopyWithImpl<SendCommandRequestModel>(this as SendCommandRequestModel, _$identity);
/// Serializes this SendCommandRequestModel to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SendCommandRequestModel&&(identical(other.deviceName, deviceName) || other.deviceName == deviceName)&&(identical(other.command, command) || other.command == command));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,deviceName,command);
@override
String toString() {
return 'SendCommandRequestModel(deviceName: $deviceName, command: $command)';
}
}
/// @nodoc
abstract mixin class $SendCommandRequestModelCopyWith<$Res> {
factory $SendCommandRequestModelCopyWith(SendCommandRequestModel value, $Res Function(SendCommandRequestModel) _then) = _$SendCommandRequestModelCopyWithImpl;
@useResult
$Res call({
String deviceName, String command
});
}
/// @nodoc
class _$SendCommandRequestModelCopyWithImpl<$Res>
implements $SendCommandRequestModelCopyWith<$Res> {
_$SendCommandRequestModelCopyWithImpl(this._self, this._then);
final SendCommandRequestModel _self;
final $Res Function(SendCommandRequestModel) _then;
/// Create a copy of SendCommandRequestModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? deviceName = null,Object? command = null,}) {
return _then(_self.copyWith(
deviceName: null == deviceName ? _self.deviceName : deviceName // ignore: cast_nullable_to_non_nullable
as String,command: null == command ? _self.command : command // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// Adds pattern-matching-related methods to [SendCommandRequestModel].
extension SendCommandRequestModelPatterns on SendCommandRequestModel {
/// 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( _SendCommandRequestModel value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _SendCommandRequestModel() 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( _SendCommandRequestModel value) $default,){
final _that = this;
switch (_that) {
case _SendCommandRequestModel():
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( _SendCommandRequestModel value)? $default,){
final _that = this;
switch (_that) {
case _SendCommandRequestModel() 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( String deviceName, String command)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _SendCommandRequestModel() when $default != null:
return $default(_that.deviceName,_that.command);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( String deviceName, String command) $default,) {final _that = this;
switch (_that) {
case _SendCommandRequestModel():
return $default(_that.deviceName,_that.command);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( String deviceName, String command)? $default,) {final _that = this;
switch (_that) {
case _SendCommandRequestModel() when $default != null:
return $default(_that.deviceName,_that.command);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _SendCommandRequestModel implements SendCommandRequestModel {
const _SendCommandRequestModel({required this.deviceName, required this.command});
factory _SendCommandRequestModel.fromJson(Map<String, dynamic> json) => _$SendCommandRequestModelFromJson(json);
@override final String deviceName;
@override final String command;
/// Create a copy of SendCommandRequestModel
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$SendCommandRequestModelCopyWith<_SendCommandRequestModel> get copyWith => __$SendCommandRequestModelCopyWithImpl<_SendCommandRequestModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$SendCommandRequestModelToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SendCommandRequestModel&&(identical(other.deviceName, deviceName) || other.deviceName == deviceName)&&(identical(other.command, command) || other.command == command));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,deviceName,command);
@override
String toString() {
return 'SendCommandRequestModel(deviceName: $deviceName, command: $command)';
}
}
/// @nodoc
abstract mixin class _$SendCommandRequestModelCopyWith<$Res> implements $SendCommandRequestModelCopyWith<$Res> {
factory _$SendCommandRequestModelCopyWith(_SendCommandRequestModel value, $Res Function(_SendCommandRequestModel) _then) = __$SendCommandRequestModelCopyWithImpl;
@override @useResult
$Res call({
String deviceName, String command
});
}
/// @nodoc
class __$SendCommandRequestModelCopyWithImpl<$Res>
implements _$SendCommandRequestModelCopyWith<$Res> {
__$SendCommandRequestModelCopyWithImpl(this._self, this._then);
final _SendCommandRequestModel _self;
final $Res Function(_SendCommandRequestModel) _then;
/// Create a copy of SendCommandRequestModel
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? deviceName = null,Object? command = null,}) {
return _then(_SendCommandRequestModel(
deviceName: null == deviceName ? _self.deviceName : deviceName // ignore: cast_nullable_to_non_nullable
as String,command: null == command ? _self.command : command // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
// dart format on

View File

@@ -0,0 +1,21 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'send_command_request_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_SendCommandRequestModel _$SendCommandRequestModelFromJson(
Map<String, dynamic> json,
) => _SendCommandRequestModel(
deviceName: json['deviceName'] as String,
command: json['command'] as String,
);
Map<String, dynamic> _$SendCommandRequestModelToJson(
_SendCommandRequestModel instance,
) => <String, dynamic>{
'deviceName': instance.deviceName,
'command': instance.command,
};

View File

@@ -1,4 +1,5 @@
import 'package:functions/src/core/data/datasources/functions_remote_datasource.dart';
import 'package:functions/src/core/data/models/entities/locate_device_request_entity.dart';
import 'package:functions/src/core/domain/repositories/functions_repository.dart';
import 'package:functions/src/features/contacts/domain/entities/contact_entity.dart';
import 'package:functions/src/features/remote_connection/domain/entities/picture_entity.dart';
@@ -24,4 +25,10 @@ class FunctionsRepositoryImpl implements FunctionsRepository {
await Future<void>.delayed(const Duration(milliseconds: 2000));
return _remote.takePicture(userId: userId);
}
@override
Future<void> locateDevice({required LocateDeviceRequestEntity request}) async {
await Future<void>.delayed(const Duration(milliseconds: 2000));
return _remote.locateDevice(request: request);
}
}

View File

@@ -1,3 +1,4 @@
import 'package:functions/src/core/data/models/entities/locate_device_request_entity.dart';
import 'package:functions/src/features/contacts/domain/entities/contact_entity.dart';
import 'package:functions/src/features/remote_connection/domain/entities/picture_entity.dart';
@@ -7,4 +8,6 @@ abstract class FunctionsRepository {
Future<List<PictureEntity>> getPictures({required String userId});
Future<PictureEntity> takePicture({required String userId});
Future<void> locateDevice({required LocateDeviceRequestEntity request});
}

View File

@@ -70,12 +70,6 @@ class FunctionsScreen extends ConsumerWidget {
text: 'Do not disturb'
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){},
icon: SFIcons.locationAreaCircle,
text: 'Safety zone'
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){},
icon: SFIcons.videoCallCircle,
@@ -84,19 +78,13 @@ class FunctionsScreen extends ConsumerWidget {
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){},
icon: SFIcons.fallCircle,
text: 'Fall notice'
icon: SFIcons.healthCircle,
text: 'Health'
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){},
icon: SFIcons.medicationCircle,
text: 'Medication reminder'
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){},
icon: SFIcons.activityCircle,
icon: SFIcons.healthCircle,
text: 'Activity meter'
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
@@ -108,6 +96,30 @@ class FunctionsScreen extends ConsumerWidget {
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){},
icon: Icons.call_outlined,
text: 'Call watch'
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){},
icon: SFIcons.screenTime,
text: 'Apps use'
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){},
icon: Icons.app_registration_sharp,
text: 'Apps supervision'
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){},
icon: SFIcons.friendsCircle,
text: 'Make friends'
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){navigationContract.pushTo(AppRoutes.locateDevice);},
icon: SFIcons.locateSfCircle,
text: 'Locate your SaveFamily'
),

View File

@@ -0,0 +1,5 @@
import 'package:functions/src/core/data/models/entities/locate_device_request_entity.dart';
abstract class LocateDeviceUseCase {
Future<void> locateDevice({required LocateDeviceRequestEntity request});
}

View File

@@ -0,0 +1,14 @@
import 'package:functions/src/core/data/models/entities/locate_device_request_entity.dart';
import 'package:functions/src/core/domain/repositories/functions_repository.dart';
import 'package:functions/src/features/locate_device/domain/locate_device_use_case.dart';
class LocateDeviceUseCaseImpl implements LocateDeviceUseCase {
LocateDeviceUseCaseImpl(this._repository);
final FunctionsRepository _repository;
@override
Future<void> locateDevice({required LocateDeviceRequestEntity request}) async {
return _repository.locateDevice(request: request);
}
}

View File

@@ -0,0 +1,18 @@
import 'package:flutter/material.dart';
import 'package:functions/src/features/locate_device/presentation/locate_device_screen.dart';
import 'package:go_router/go_router.dart';
import 'package:get_it/get_it.dart';
import 'package:navigation/navigation.dart';
class LocateDeviceBuilder {
const LocateDeviceBuilder();
Page<void> buildPage(BuildContext context, GoRouterState state) {
final NavigationContract navigationContract = GetIt.I<NavigationContract>();
return MaterialPage<void>(
key: state.pageKey,
child: LocateDeviceScreen(navigationContract: navigationContract),
);
}
}

View File

@@ -0,0 +1,148 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:functions/src/features/locate_device/presentation/state/locate_device_view_model.dart';
import 'package:navigation/navigation.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:utils/utils.dart';
import 'package:legacy_shared/legacy_shared.dart';
class LocateDeviceScreen extends ConsumerWidget {
final NavigationContract navigationContract;
const LocateDeviceScreen({super.key, required this.navigationContract});
@override
Widget build(BuildContext context, WidgetRef ref) {
return PageLayout(
title: context.translate('Find your device'),
body: Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 16),
big: EdgeInsets.symmetric(horizontal: 15)
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(context.translate('Locate your SaveFamily in nearby locations'),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 18, big: 17),
fontWeight: FontWeight.w500,
)
),
SizedBox(height: SizeUtils.getByScreen(small: 10, big: 8)),
Text('This function allows you to find the device indoors or in places close to you. When you press the button, the smartwatch will start beeping and you will be able to hear it.',
style: TextStyle(
fontSize: 16
))
],
),
),
footer: Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(vertical: 12, horizontal: 26),
big: EdgeInsets.symmetric(vertical: 10, horizontal: 25)
),
child: PrimaryButton(
onPressed: () {
showDialog(context: context, builder: (context)=>Dialog(
child: LocateDialog(),
));
},
text: context.translate('Locate my SaveFamily'),
color: Color(0xFF588EA5),
height: SizeUtils.getByScreen(small: 36, big: 35),
),
)
);
}
}
class LocateDialog extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final viewModel = ref.read(locateDeviceViewModelProvider.notifier);
final viewState = ref.watch(locateDeviceViewModelProvider);
return Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 32, vertical: 30),
big: EdgeInsets.symmetric(horizontal: 30, vertical: 28)
),
width: SizeUtils.getByScreen(small: 360, big: 350),
height: SizeUtils.getByScreen(small: 210, big: 205),
child: Column(
children: [
if (viewState.isLoading)
Expanded(child: Center(child: Text(context.translate('Sending...'),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 25, big: 24),
),
))),
if (viewState.isComplete)
Expanded(child: Center(child: Text(context.translate('Sent successfully'),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 25, big: 24),
),
))),
if (viewState.errorMessage.isNotEmpty) ...[
Expanded(child: Center(child: Text(context.translate('The device is not connected to the Internet'),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 20, big: 19),
),
))),
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 23)),
Expanded(child: PrimaryButton(
onPressed: (){
Navigator.pop(context);
viewModel.endLocation();
},
text: context.translate('OK'),
color: Color(0xFF588EA5),
height: SizeUtils.getByScreen(small: 38, big: 36),
radius: SizeUtils.getByScreen(small: 32, big: 34),
)),
],
if (!viewState.isComplete && !viewState.isLoading && viewState.errorMessage.isEmpty) ...[
Text(context.translate('You are going to activate the remote location of your device. It will start ringing.'),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 19, big: 18),
),
),
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 23)),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(child: PrimaryButton(
onPressed: (){Navigator.pop(context);},
text: context.translate(I18n.legacyCancel),
color: Color(0xFF588EA5),
height: SizeUtils.getByScreen(small: 38, big: 36),
radius: SizeUtils.getByScreen(small: 32, big: 34),
)),
SizedBox(width: SizeUtils.getByScreen(small: 4, big: 16)),
Expanded(child: PrimaryButton(
onPressed: () {
viewModel.locateDevice();
},
text: context.translate(I18n.accept),
color: Color(0xFF588EA5),
height: SizeUtils.getByScreen(small: 38, big: 36),
radius: SizeUtils.getByScreen(small: 32, big: 34),
))
],
)
]
],
),
);
}
}

View File

@@ -0,0 +1,9 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:functions/src/core/providers/functions_repository_provider.dart';
import 'package:functions/src/features/locate_device/domain/locate_device_use_case.dart';
import 'package:functions/src/features/locate_device/domain/locate_device_use_case_impl.dart';
final locateDeviceUseCaseProvider = Provider.autoDispose<LocateDeviceUseCase>((ref) {
final functionsRepository = ref.read(functionsRepositoryProvider);
return LocateDeviceUseCaseImpl(functionsRepository);
});

View File

@@ -0,0 +1,62 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:functions/src/core/data/models/entities/locate_device_request_entity.dart';
import 'package:functions/src/features/locate_device/domain/locate_device_use_case.dart';
import 'package:functions/src/features/locate_device/presentation/providers/locate_device_use_case_provider.dart';
import 'package:functions/src/features/locate_device/presentation/state/locate_device_view_state.dart';
// import 'package:legacy_shared/src/providers/logged_user_provider.dart';
// import 'package:sf_localizations/sf_localizations.dart';
final locateDeviceViewModelProvider =
NotifierProvider.autoDispose<LocateDeviceViewModel, LocateDeviceViewState>(
LocateDeviceViewModel.new,
);
class LocateDeviceViewModel extends Notifier<LocateDeviceViewState> {
late final LocateDeviceUseCase _locateDeviceUseCase;
late final TextEditingController nameController;
late final TextEditingController phoneController;
// late final UserEntity loggedUser;
@override
LocateDeviceViewState build() {
_locateDeviceUseCase = ref.read(locateDeviceUseCaseProvider);
// loggedUser = ref.read(loggedUserProvider);
return const LocateDeviceViewState();
}
Future<void> locateDevice() async {
try {
state = state.copyWith(
isLoading: true,
isComplete: false,
errorMessage: '',
);
LocateDeviceRequestEntity request = LocateDeviceRequestEntity(deviceName: '');
await _locateDeviceUseCase.locateDevice(request: request);
state = state.copyWith(
isLoading: false,
isComplete: true,
);
} catch (e) {
state = state.copyWith(
isLoading: false,
isComplete: false,
errorMessage: e.toString(),
);
}
}
void endLocation() {
state = state.copyWith(
errorMessage: '',
);
}
}

View File

@@ -0,0 +1,11 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'locate_device_view_state.freezed.dart';
@freezed
abstract class LocateDeviceViewState with _$LocateDeviceViewState {
const factory LocateDeviceViewState({
@Default(false) bool isLoading,
@Default(false) bool isComplete,
@Default('') String errorMessage,
}) = _LocateDeviceViewState;
}

View File

@@ -0,0 +1,277 @@
// 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 'locate_device_view_state.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$LocateDeviceViewState {
bool get isLoading; bool get isComplete; String get errorMessage;
/// Create a copy of LocateDeviceViewState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$LocateDeviceViewStateCopyWith<LocateDeviceViewState> get copyWith => _$LocateDeviceViewStateCopyWithImpl<LocateDeviceViewState>(this as LocateDeviceViewState, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is LocateDeviceViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isComplete, isComplete) || other.isComplete == isComplete)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
}
@override
int get hashCode => Object.hash(runtimeType,isLoading,isComplete,errorMessage);
@override
String toString() {
return 'LocateDeviceViewState(isLoading: $isLoading, isComplete: $isComplete, errorMessage: $errorMessage)';
}
}
/// @nodoc
abstract mixin class $LocateDeviceViewStateCopyWith<$Res> {
factory $LocateDeviceViewStateCopyWith(LocateDeviceViewState value, $Res Function(LocateDeviceViewState) _then) = _$LocateDeviceViewStateCopyWithImpl;
@useResult
$Res call({
bool isLoading, bool isComplete, String errorMessage
});
}
/// @nodoc
class _$LocateDeviceViewStateCopyWithImpl<$Res>
implements $LocateDeviceViewStateCopyWith<$Res> {
_$LocateDeviceViewStateCopyWithImpl(this._self, this._then);
final LocateDeviceViewState _self;
final $Res Function(LocateDeviceViewState) _then;
/// Create a copy of LocateDeviceViewState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? isLoading = null,Object? isComplete = null,Object? errorMessage = null,}) {
return _then(_self.copyWith(
isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,isComplete: null == isComplete ? _self.isComplete : isComplete // ignore: cast_nullable_to_non_nullable
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// Adds pattern-matching-related methods to [LocateDeviceViewState].
extension LocateDeviceViewStatePatterns on LocateDeviceViewState {
/// 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( _LocateDeviceViewState value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _LocateDeviceViewState() 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( _LocateDeviceViewState value) $default,){
final _that = this;
switch (_that) {
case _LocateDeviceViewState():
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( _LocateDeviceViewState value)? $default,){
final _that = this;
switch (_that) {
case _LocateDeviceViewState() 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( bool isLoading, bool isComplete, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _LocateDeviceViewState() when $default != null:
return $default(_that.isLoading,_that.isComplete,_that.errorMessage);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( bool isLoading, bool isComplete, String errorMessage) $default,) {final _that = this;
switch (_that) {
case _LocateDeviceViewState():
return $default(_that.isLoading,_that.isComplete,_that.errorMessage);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( bool isLoading, bool isComplete, String errorMessage)? $default,) {final _that = this;
switch (_that) {
case _LocateDeviceViewState() when $default != null:
return $default(_that.isLoading,_that.isComplete,_that.errorMessage);case _:
return null;
}
}
}
/// @nodoc
class _LocateDeviceViewState implements LocateDeviceViewState {
const _LocateDeviceViewState({this.isLoading = false, this.isComplete = false, this.errorMessage = ''});
@override@JsonKey() final bool isLoading;
@override@JsonKey() final bool isComplete;
@override@JsonKey() final String errorMessage;
/// Create a copy of LocateDeviceViewState
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$LocateDeviceViewStateCopyWith<_LocateDeviceViewState> get copyWith => __$LocateDeviceViewStateCopyWithImpl<_LocateDeviceViewState>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _LocateDeviceViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isComplete, isComplete) || other.isComplete == isComplete)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
}
@override
int get hashCode => Object.hash(runtimeType,isLoading,isComplete,errorMessage);
@override
String toString() {
return 'LocateDeviceViewState(isLoading: $isLoading, isComplete: $isComplete, errorMessage: $errorMessage)';
}
}
/// @nodoc
abstract mixin class _$LocateDeviceViewStateCopyWith<$Res> implements $LocateDeviceViewStateCopyWith<$Res> {
factory _$LocateDeviceViewStateCopyWith(_LocateDeviceViewState value, $Res Function(_LocateDeviceViewState) _then) = __$LocateDeviceViewStateCopyWithImpl;
@override @useResult
$Res call({
bool isLoading, bool isComplete, String errorMessage
});
}
/// @nodoc
class __$LocateDeviceViewStateCopyWithImpl<$Res>
implements _$LocateDeviceViewStateCopyWith<$Res> {
__$LocateDeviceViewStateCopyWithImpl(this._self, this._then);
final _LocateDeviceViewState _self;
final $Res Function(_LocateDeviceViewState) _then;
/// Create a copy of LocateDeviceViewState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? isLoading = null,Object? isComplete = null,Object? errorMessage = null,}) {
return _then(_LocateDeviceViewState(
isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,isComplete: null == isComplete ? _self.isComplete : isComplete // ignore: cast_nullable_to_non_nullable
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
// dart format on

View File

@@ -47,7 +47,7 @@ class LegacyDashboardScreen extends ConsumerWidget {
label: context.translate(I18n.functions),
),
NavigationDestination(
icon: Icon(Icons.location_on_outlined),
icon: Icon(SFIcons.locationOutlined),
label: context.translate(I18n.location),
),
NavigationDestination(

View File

@@ -41,7 +41,7 @@ class PageLayout extends StatelessWidget{
),
),
SizedBox(height: SizeUtils.getByScreen(small: 30, big: 28)),
body,
Expanded(child: body),
?footer
],
)