Compare commits

...

2 Commits

28 changed files with 1526 additions and 5 deletions

View File

@@ -230,6 +230,11 @@ void configureAppRouter() {
name: 'videocall',
pageBuilder: const VideocallBuilder().buildPage,
),
GoRoute(
path: 'friends',
name: 'friends',
pageBuilder: const FriendsBuilder().buildPage,
),
],
),
],

View File

@@ -15,3 +15,4 @@ export 'src/features/background_image/background_image_builder.dart';
export 'src/features/installed_apps/installed_apps_builder.dart';
export 'src/features/app_usage_schedules/app_usage_schedules_builder.dart';
export 'src/features/videocall/videocall_builder.dart';
export 'src/features/friends/friends_builder.dart';

View File

@@ -145,6 +145,15 @@ class DeviceManagementScreen extends ConsumerWidget {
iconSize: SizeUtils.getByScreen(small: 42, big: 40),
text: context.translate(I18n.callHistory),
),
gap,
AppMenuButton(
color: primaryColor,
onPressed: () =>
navigationContract.pushTo(AppRoutes.friends),
icon: Icons.people_outline,
iconSize: SizeUtils.getByScreen(small: 42, big: 40),
text: context.translate(I18n.makeFriends),
),
if (capabilities?.appUsageSchedules != null) ...[
gap,
AppMenuButton(

View File

@@ -0,0 +1,15 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'friend_entity.freezed.dart';
@freezed
abstract class FriendEntity with _$FriendEntity {
const factory FriendEntity({
required String id,
required String deviceId,
required String friendId,
String? phone,
required String name,
required int createdAt,
}) = _FriendEntity;
}

View File

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

View File

@@ -0,0 +1,61 @@
import 'package:dio/dio.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
import 'friend_entity.dart';
import 'friends_response_dto.dart';
class FriendsDatasource {
FriendsDatasource(this._repository);
final SaveFamilyRepository _repository;
Future<List<FriendEntity>> getFriends({
required String deviceIdentificator,
String? search,
}) async {
try {
final queryParams = <String, dynamic>{'pageSize': 1000};
if (search != null && search.isNotEmpty) {
queryParams['search'] = search;
}
final response = await _repository.get<Map<String, dynamic>>(
'/devices/identificator/$deviceIdentificator/friends',
queryParameters: queryParams,
);
final data = response.data;
if (data == null || data.isEmpty) return [];
final model = FriendsResponseDto.fromJson(data);
return model.items
.map(
(item) => FriendEntity(
id: item.id,
deviceId: item.deviceId,
friendId: item.friendId,
phone: item.phone,
name: item.name,
createdAt: item.createdAt,
),
)
.toList();
} on DioException catch (error) {
if (error.response?.statusCode == 404) return [];
throw mapDioError(error, defaultMessage: 'Error getting friends');
}
}
Future<void> deleteFriend({
required String deviceIdentificator,
required String friendId,
}) async {
try {
await _repository.delete<dynamic>(
'/devices/identificator/$deviceIdentificator/friends/$friendId',
);
} on DioException catch (error) {
throw mapDioError(error, defaultMessage: 'Error deleting friend');
}
}
}

View File

@@ -0,0 +1,9 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:get_it/get_it.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
import 'friends_datasource.dart';
final friendsDatasourceProvider = Provider<FriendsDatasource>((ref) {
return FriendsDatasource(GetIt.I<SaveFamilyRepository>());
});

View File

@@ -0,0 +1,30 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'friends_response_dto.freezed.dart';
part 'friends_response_dto.g.dart';
@freezed
abstract class FriendsResponseDto with _$FriendsResponseDto {
const factory FriendsResponseDto({
required int total,
required List<FriendItemDto> items,
}) = _FriendsResponseDto;
factory FriendsResponseDto.fromJson(Map<String, dynamic> json) =>
_$FriendsResponseDtoFromJson(json);
}
@freezed
abstract class FriendItemDto with _$FriendItemDto {
const factory FriendItemDto({
required String id,
required String deviceId,
required String friendId,
String? phone,
required String name,
required int createdAt,
}) = _FriendItemDto;
factory FriendItemDto.fromJson(Map<String, dynamic> json) =>
_$FriendItemDtoFromJson(json);
}

View File

@@ -0,0 +1,564 @@
// 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 'friends_response_dto.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$FriendsResponseDto {
int get total; List<FriendItemDto> get items;
/// Create a copy of FriendsResponseDto
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$FriendsResponseDtoCopyWith<FriendsResponseDto> get copyWith => _$FriendsResponseDtoCopyWithImpl<FriendsResponseDto>(this as FriendsResponseDto, _$identity);
/// Serializes this FriendsResponseDto to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is FriendsResponseDto&&(identical(other.total, total) || other.total == total)&&const DeepCollectionEquality().equals(other.items, items));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,total,const DeepCollectionEquality().hash(items));
@override
String toString() {
return 'FriendsResponseDto(total: $total, items: $items)';
}
}
/// @nodoc
abstract mixin class $FriendsResponseDtoCopyWith<$Res> {
factory $FriendsResponseDtoCopyWith(FriendsResponseDto value, $Res Function(FriendsResponseDto) _then) = _$FriendsResponseDtoCopyWithImpl;
@useResult
$Res call({
int total, List<FriendItemDto> items
});
}
/// @nodoc
class _$FriendsResponseDtoCopyWithImpl<$Res>
implements $FriendsResponseDtoCopyWith<$Res> {
_$FriendsResponseDtoCopyWithImpl(this._self, this._then);
final FriendsResponseDto _self;
final $Res Function(FriendsResponseDto) _then;
/// Create a copy of FriendsResponseDto
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? total = null,Object? items = null,}) {
return _then(_self.copyWith(
total: null == total ? _self.total : total // ignore: cast_nullable_to_non_nullable
as int,items: null == items ? _self.items : items // ignore: cast_nullable_to_non_nullable
as List<FriendItemDto>,
));
}
}
/// Adds pattern-matching-related methods to [FriendsResponseDto].
extension FriendsResponseDtoPatterns on FriendsResponseDto {
/// 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( _FriendsResponseDto value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _FriendsResponseDto() 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( _FriendsResponseDto value) $default,){
final _that = this;
switch (_that) {
case _FriendsResponseDto():
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( _FriendsResponseDto value)? $default,){
final _that = this;
switch (_that) {
case _FriendsResponseDto() 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( int total, List<FriendItemDto> items)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _FriendsResponseDto() when $default != null:
return $default(_that.total,_that.items);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( int total, List<FriendItemDto> items) $default,) {final _that = this;
switch (_that) {
case _FriendsResponseDto():
return $default(_that.total,_that.items);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( int total, List<FriendItemDto> items)? $default,) {final _that = this;
switch (_that) {
case _FriendsResponseDto() when $default != null:
return $default(_that.total,_that.items);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _FriendsResponseDto implements FriendsResponseDto {
const _FriendsResponseDto({required this.total, required final List<FriendItemDto> items}): _items = items;
factory _FriendsResponseDto.fromJson(Map<String, dynamic> json) => _$FriendsResponseDtoFromJson(json);
@override final int total;
final List<FriendItemDto> _items;
@override List<FriendItemDto> get items {
if (_items is EqualUnmodifiableListView) return _items;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_items);
}
/// Create a copy of FriendsResponseDto
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$FriendsResponseDtoCopyWith<_FriendsResponseDto> get copyWith => __$FriendsResponseDtoCopyWithImpl<_FriendsResponseDto>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$FriendsResponseDtoToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _FriendsResponseDto&&(identical(other.total, total) || other.total == total)&&const DeepCollectionEquality().equals(other._items, _items));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,total,const DeepCollectionEquality().hash(_items));
@override
String toString() {
return 'FriendsResponseDto(total: $total, items: $items)';
}
}
/// @nodoc
abstract mixin class _$FriendsResponseDtoCopyWith<$Res> implements $FriendsResponseDtoCopyWith<$Res> {
factory _$FriendsResponseDtoCopyWith(_FriendsResponseDto value, $Res Function(_FriendsResponseDto) _then) = __$FriendsResponseDtoCopyWithImpl;
@override @useResult
$Res call({
int total, List<FriendItemDto> items
});
}
/// @nodoc
class __$FriendsResponseDtoCopyWithImpl<$Res>
implements _$FriendsResponseDtoCopyWith<$Res> {
__$FriendsResponseDtoCopyWithImpl(this._self, this._then);
final _FriendsResponseDto _self;
final $Res Function(_FriendsResponseDto) _then;
/// Create a copy of FriendsResponseDto
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? total = null,Object? items = null,}) {
return _then(_FriendsResponseDto(
total: null == total ? _self.total : total // ignore: cast_nullable_to_non_nullable
as int,items: null == items ? _self._items : items // ignore: cast_nullable_to_non_nullable
as List<FriendItemDto>,
));
}
}
/// @nodoc
mixin _$FriendItemDto {
String get id; String get deviceId; String get friendId; String? get phone; String get name; int get createdAt;
/// Create a copy of FriendItemDto
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$FriendItemDtoCopyWith<FriendItemDto> get copyWith => _$FriendItemDtoCopyWithImpl<FriendItemDto>(this as FriendItemDto, _$identity);
/// Serializes this FriendItemDto to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is FriendItemDto&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.friendId, friendId) || other.friendId == friendId)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.name, name) || other.name == name)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,deviceId,friendId,phone,name,createdAt);
@override
String toString() {
return 'FriendItemDto(id: $id, deviceId: $deviceId, friendId: $friendId, phone: $phone, name: $name, createdAt: $createdAt)';
}
}
/// @nodoc
abstract mixin class $FriendItemDtoCopyWith<$Res> {
factory $FriendItemDtoCopyWith(FriendItemDto value, $Res Function(FriendItemDto) _then) = _$FriendItemDtoCopyWithImpl;
@useResult
$Res call({
String id, String deviceId, String friendId, String? phone, String name, int createdAt
});
}
/// @nodoc
class _$FriendItemDtoCopyWithImpl<$Res>
implements $FriendItemDtoCopyWith<$Res> {
_$FriendItemDtoCopyWithImpl(this._self, this._then);
final FriendItemDto _self;
final $Res Function(FriendItemDto) _then;
/// Create a copy of FriendItemDto
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? deviceId = null,Object? friendId = null,Object? phone = freezed,Object? name = null,Object? createdAt = null,}) {
return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
as String,friendId: null == friendId ? _self.friendId : friendId // ignore: cast_nullable_to_non_nullable
as String,phone: freezed == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
as String?,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// Adds pattern-matching-related methods to [FriendItemDto].
extension FriendItemDtoPatterns on FriendItemDto {
/// 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( _FriendItemDto value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _FriendItemDto() 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( _FriendItemDto value) $default,){
final _that = this;
switch (_that) {
case _FriendItemDto():
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( _FriendItemDto value)? $default,){
final _that = this;
switch (_that) {
case _FriendItemDto() 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 id, String deviceId, String friendId, String? phone, String name, int createdAt)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _FriendItemDto() when $default != null:
return $default(_that.id,_that.deviceId,_that.friendId,_that.phone,_that.name,_that.createdAt);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 id, String deviceId, String friendId, String? phone, String name, int createdAt) $default,) {final _that = this;
switch (_that) {
case _FriendItemDto():
return $default(_that.id,_that.deviceId,_that.friendId,_that.phone,_that.name,_that.createdAt);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 id, String deviceId, String friendId, String? phone, String name, int createdAt)? $default,) {final _that = this;
switch (_that) {
case _FriendItemDto() when $default != null:
return $default(_that.id,_that.deviceId,_that.friendId,_that.phone,_that.name,_that.createdAt);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _FriendItemDto implements FriendItemDto {
const _FriendItemDto({required this.id, required this.deviceId, required this.friendId, this.phone, required this.name, required this.createdAt});
factory _FriendItemDto.fromJson(Map<String, dynamic> json) => _$FriendItemDtoFromJson(json);
@override final String id;
@override final String deviceId;
@override final String friendId;
@override final String? phone;
@override final String name;
@override final int createdAt;
/// Create a copy of FriendItemDto
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$FriendItemDtoCopyWith<_FriendItemDto> get copyWith => __$FriendItemDtoCopyWithImpl<_FriendItemDto>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$FriendItemDtoToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _FriendItemDto&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.friendId, friendId) || other.friendId == friendId)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.name, name) || other.name == name)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,deviceId,friendId,phone,name,createdAt);
@override
String toString() {
return 'FriendItemDto(id: $id, deviceId: $deviceId, friendId: $friendId, phone: $phone, name: $name, createdAt: $createdAt)';
}
}
/// @nodoc
abstract mixin class _$FriendItemDtoCopyWith<$Res> implements $FriendItemDtoCopyWith<$Res> {
factory _$FriendItemDtoCopyWith(_FriendItemDto value, $Res Function(_FriendItemDto) _then) = __$FriendItemDtoCopyWithImpl;
@override @useResult
$Res call({
String id, String deviceId, String friendId, String? phone, String name, int createdAt
});
}
/// @nodoc
class __$FriendItemDtoCopyWithImpl<$Res>
implements _$FriendItemDtoCopyWith<$Res> {
__$FriendItemDtoCopyWithImpl(this._self, this._then);
final _FriendItemDto _self;
final $Res Function(_FriendItemDto) _then;
/// Create a copy of FriendItemDto
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? deviceId = null,Object? friendId = null,Object? phone = freezed,Object? name = null,Object? createdAt = null,}) {
return _then(_FriendItemDto(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
as String,friendId: null == friendId ? _self.friendId : friendId // ignore: cast_nullable_to_non_nullable
as String,phone: freezed == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
as String?,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
// dart format on

View File

@@ -0,0 +1,38 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'friends_response_dto.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_FriendsResponseDto _$FriendsResponseDtoFromJson(Map<String, dynamic> json) =>
_FriendsResponseDto(
total: (json['total'] as num).toInt(),
items: (json['items'] as List<dynamic>)
.map((e) => FriendItemDto.fromJson(e as Map<String, dynamic>))
.toList(),
);
Map<String, dynamic> _$FriendsResponseDtoToJson(_FriendsResponseDto instance) =>
<String, dynamic>{'total': instance.total, 'items': instance.items};
_FriendItemDto _$FriendItemDtoFromJson(Map<String, dynamic> json) =>
_FriendItemDto(
id: json['id'] as String,
deviceId: json['deviceId'] as String,
friendId: json['friendId'] as String,
phone: json['phone'] as String?,
name: json['name'] as String,
createdAt: (json['createdAt'] as num).toInt(),
);
Map<String, dynamic> _$FriendItemDtoToJson(_FriendItemDto instance) =>
<String, dynamic>{
'id': instance.id,
'deviceId': instance.deviceId,
'friendId': instance.friendId,
'phone': instance.phone,
'name': instance.name,
'createdAt': instance.createdAt,
};

View File

@@ -0,0 +1,15 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'presentation/friends_screen.dart';
class FriendsBuilder {
const FriendsBuilder();
Page<void> buildPage(BuildContext context, GoRouterState state) {
return MaterialPage<void>(
key: state.pageKey,
child: const FriendsScreen(),
);
}
}

View File

@@ -0,0 +1,227 @@
import 'package:design_system/design_system.dart';
import 'package:device_management/src/features/friends/data/friend_entity.dart';
import 'package:device_management/src/features/friends/presentation/providers/friends_controller.dart';
import 'package:device_management/src/features/friends/presentation/providers/friends_provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:legacy_ui/legacy_ui.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:sf_shared/sf_shared.dart';
class FriendsScreen extends ConsumerStatefulWidget {
const FriendsScreen({super.key});
@override
ConsumerState<FriendsScreen> createState() => _FriendsScreenState();
}
class _FriendsScreenState extends ConsumerState<FriendsScreen> {
bool _editMode = false;
@override
Widget build(BuildContext context) {
final device = ref.watch(selectedDeviceProvider).value;
if (device == null) {
return LegacyPageLayout(
title: context.translate(I18n.makeFriends),
body: const SizedBox.shrink(),
);
}
final friendsAsync = ref.watch(friendsProvider(device.identificator));
ref.listen(friendsControllerProvider, (previous, next) {
if (next.hasError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(context.translate(I18n.friendDeleteError)),
backgroundColor: Theme.of(context).colorScheme.error,
),
);
}
if (previous?.isLoading == true && next.hasValue && !next.hasError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(context.translate(I18n.friendDeleted))),
);
}
});
return LegacyPageLayout(
title: context.translate(I18n.makeFriends),
showEdit: true,
onEditChange: () => setState(() => _editMode = !_editMode),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
child: Text(
context.translate(I18n.friendsMaxInfo),
style: TextStyle(
fontSize: 13,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
),
Expanded(
child: friendsAsync.when(
loading: () => const LegacyLoadingIndicator(),
error: (error, _) => Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.error_outline,
size: 64,
color: Theme.of(context).colorScheme.outline,
),
const SizedBox(height: 12),
Text(
error.toString(),
style: TextStyle(
color: Theme.of(context).colorScheme.onSurfaceVariant,
fontSize: 14,
),
textAlign: TextAlign.center,
),
],
),
),
data: (friends) {
if (friends.isEmpty) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.people_outline,
size: 64,
color: Theme.of(context).colorScheme.outline,
),
const SizedBox(height: 12),
Text(
context.translate(I18n.friendsEmpty),
style: TextStyle(
color:
Theme.of(context).colorScheme.onSurfaceVariant,
fontSize: 14,
),
),
],
),
);
}
return ListView.builder(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 4,
),
itemCount: friends.length,
itemBuilder: (context, index) {
final friend = friends[index];
return _FriendTile(
friend: friend,
showDelete: _editMode,
onDelete: () => _confirmDelete(
context,
deviceIdentificator: device.identificator,
friend: friend,
),
);
},
);
},
),
),
],
),
);
}
Future<void> _confirmDelete(
BuildContext context, {
required String deviceIdentificator,
required FriendEntity friend,
}) async {
final confirmed = await showLegacyDialog<bool>(
context: context,
builder: (dialogContext) => AlertDialog(
title: Text(context.translate(I18n.friendDeleteConfirm)),
content: Text(friend.name),
actions: [
TextButton(
onPressed: () => Navigator.pop(dialogContext, false),
child: Text(context.translate(I18n.cancel)),
),
FilledButton(
onPressed: () => Navigator.pop(dialogContext, true),
style: FilledButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.error,
),
child: Text(context.translate(I18n.delete)),
),
],
),
);
if (confirmed != true) return;
ref.read(friendsControllerProvider.notifier).deleteFriend(
deviceIdentificator: deviceIdentificator,
friendId: friend.friendId,
);
}
}
class _FriendTile extends StatelessWidget {
final FriendEntity friend;
final bool showDelete;
final VoidCallback onDelete;
const _FriendTile({
required this.friend,
required this.showDelete,
required this.onDelete,
});
@override
Widget build(BuildContext context) {
final date = DateTime.fromMillisecondsSinceEpoch(friend.createdAt);
final dateStr =
'${date.day.toString().padLeft(2, '0')}/${date.month.toString().padLeft(2, '0')}/${date.year}';
return Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 4),
leading: Image.asset(
'assets/shared/images/iso_sf.png',
width: 44,
height: 44,
),
title: Text(
friend.name,
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
subtitle: Text(
friend.phone ?? dateStr,
style: TextStyle(
fontSize: 12,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
trailing: showDelete
? IconButton(
icon: Icon(
Icons.delete_outline,
color: Theme.of(context).colorScheme.error,
),
onPressed: onDelete,
)
: null,
),
);
}
}

View File

@@ -0,0 +1,30 @@
import 'package:device_management/src/features/friends/data/friends_datasource_provider.dart';
import 'package:device_management/src/features/friends/presentation/providers/friends_provider.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'friends_controller.g.dart';
@riverpod
class FriendsController extends _$FriendsController {
@override
FutureOr<void> build() {}
Future<bool> deleteFriend({
required String deviceIdentificator,
required String friendId,
}) async {
state = const AsyncLoading();
state = await AsyncValue.guard(() async {
await ref
.read(friendsDatasourceProvider)
.deleteFriend(
deviceIdentificator: deviceIdentificator,
friendId: friendId,
);
});
if (!state.hasError) {
ref.invalidate(friendsProvider);
}
return !state.hasError;
}
}

View File

@@ -0,0 +1,55 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'friends_controller.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
@ProviderFor(FriendsController)
const friendsControllerProvider = FriendsControllerProvider._();
final class FriendsControllerProvider
extends $AsyncNotifierProvider<FriendsController, void> {
const FriendsControllerProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'friendsControllerProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$friendsControllerHash();
@$internal
@override
FriendsController create() => FriendsController();
}
String _$friendsControllerHash() => r'7b4faf55a771ebb1386cfe43c045f2dbf986be77';
abstract class _$FriendsController extends $AsyncNotifier<void> {
FutureOr<void> build();
@$mustCallSuper
@override
void runBuild() {
build();
final ref = this.ref as $Ref<AsyncValue<void>, void>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<AsyncValue<void>, void>,
AsyncValue<void>,
Object?,
Object?
>;
element.handleValue(ref, null);
}
}

View File

@@ -0,0 +1,18 @@
import 'package:device_management/src/features/friends/data/friends_datasource_provider.dart';
import 'package:device_management/src/features/friends/data/friend_entity.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'friends_provider.g.dart';
@riverpod
Future<List<FriendEntity>> friends(
Ref ref,
String deviceIdentificator, {
String? search,
}) async {
final friends = await ref
.read(friendsDatasourceProvider)
.getFriends(deviceIdentificator: deviceIdentificator, search: search);
friends.sort((a, b) => b.createdAt.compareTo(a.createdAt));
return friends;
}

View File

@@ -0,0 +1,94 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'friends_provider.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
@ProviderFor(friends)
const friendsProvider = FriendsFamily._();
final class FriendsProvider
extends
$FunctionalProvider<
AsyncValue<List<FriendEntity>>,
List<FriendEntity>,
FutureOr<List<FriendEntity>>
>
with
$FutureModifier<List<FriendEntity>>,
$FutureProvider<List<FriendEntity>> {
const FriendsProvider._({
required FriendsFamily super.from,
required (String, {String? search}) super.argument,
}) : super(
retry: null,
name: r'friendsProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$friendsHash();
@override
String toString() {
return r'friendsProvider'
''
'$argument';
}
@$internal
@override
$FutureProviderElement<List<FriendEntity>> $createElement(
$ProviderPointer pointer,
) => $FutureProviderElement(pointer);
@override
FutureOr<List<FriendEntity>> create(Ref ref) {
final argument = this.argument as (String, {String? search});
return friends(ref, argument.$1, search: argument.search);
}
@override
bool operator ==(Object other) {
return other is FriendsProvider && other.argument == argument;
}
@override
int get hashCode {
return argument.hashCode;
}
}
String _$friendsHash() => r'd02a13d813bcc31fade65a9d7a1ed9670fce1b89';
final class FriendsFamily extends $Family
with
$FunctionalFamilyOverride<
FutureOr<List<FriendEntity>>,
(String, {String? search})
> {
const FriendsFamily._()
: super(
retry: null,
name: r'friendsProvider',
dependencies: null,
$allTransitiveDependencies: null,
isAutoDispose: true,
);
FriendsProvider call(String deviceIdentificator, {String? search}) =>
FriendsProvider._(
argument: (deviceIdentificator, search: search),
from: this,
);
@override
String toString() => r'friendsProvider';
}

View File

@@ -42,7 +42,7 @@ final class VideocallControllerProvider
}
String _$videocallControllerHash() =>
r'72a9b2eac6a4fedbb7b8234c10ce5084d178dee4';
r'e94071625d05927701912826102a5b526b72140b';
abstract class _$VideocallController extends $Notifier<VideocallState> {
VideocallState build();

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 516 KiB

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:sf_localizations/sf_localizations.dart';
class LegacyLoadingIndicator extends StatelessWidget {
final double size;
@@ -8,10 +9,23 @@ class LegacyLoadingIndicator extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Image.asset(
'packages/design_system/assets/animations/loading_legacy.gif',
width: size,
height: size,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
'packages/design_system/assets/animations/birds_loading.gif',
width: size,
height: size,
),
const SizedBox(height: 8),
Text(
context.translate(I18n.loadingText),
style: TextStyle(
fontSize: 14,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
],
),
);
}

View File

@@ -77,6 +77,7 @@ class AppRoutes {
static const callHistory = '$deviceManagement/call_history';
static const backgroundImage = '$deviceManagement/background_image';
static const videocall = '$deviceManagement/videocall';
static const friends = '$deviceManagement/friends';
static const legacyLogin = '$legacy/login';
static const legacySignup = '$legacy/signup';

View File

@@ -998,6 +998,13 @@
"logOutConfirm": "Möchten Sie sich wirklich abmelden?",
"loginEmail": "(E-Mail)",
"makeFriends": "Freunde finden",
"friendsEmpty": "Noch keine Freunde",
"friendDeleted": "Freund gelöscht",
"friendDeleteError": "Fehler beim Löschen des Freundes",
"friendDeleteConfirm": "Diesen Freund löschen?",
"friendSearchHint": "Nach Name suchen",
"friendsMaxInfo": "*Das Gerät kann sich mit bis zu vier Freunden verbinden",
"loadingText": "Laden...",
"male": "Männlich",
"maximum": "Maximum",
"minimum": "Minimum",

View File

@@ -642,6 +642,13 @@
"topApps": "Top apps",
"appsSurveillance": "Apps surveillance",
"makeFriends": "Make friends",
"friendsEmpty": "No friends yet",
"friendDeleted": "Friend deleted",
"friendDeleteError": "Error deleting friend",
"friendDeleteConfirm": "Delete this friend?",
"friendSearchHint": "Search by name",
"friendsMaxInfo": "*The device can connect with up to four friends",
"loadingText": "Loading...",
"locateDevicePlaySoundButton": "Play sound on device",
"locateDeviceDescription": "Locate your device with a sound.",
"locateDeviceInstructions": "1. Press the Play Sound button to activate the sound on the device.\n2. Press OK.\n3. The device will play a sound.\n4. The device will start sounding after receiving the instructions.",

View File

@@ -643,6 +643,13 @@
"topApps": "Apps más usadas",
"appsSurveillance": "Supervisión de las aplicaciones",
"makeFriends": "Hacer amigos",
"friendsEmpty": "No hay amigos todavía",
"friendDeleted": "Amigo eliminado",
"friendDeleteError": "Error al eliminar amigo",
"friendDeleteConfirm": "¿Eliminar este amigo?",
"friendSearchHint": "Buscar por nombre",
"friendsMaxInfo": "*El dispositivo podrá conectarse hasta con cuatro amigos",
"loadingText": "Cargando...",
"locateDevicePlaySoundButton": "Reproducir sonido en dispositivo",
"locateDeviceDescription": "Localiza tu dispositivo con un sonido.",
"locateDeviceInstructions": "1. Pulsa el botón Reproducir Sonido para activar el sonido en el dispositivo.\n2. Pulsa OK\n3. El dispositivo reproducirá un sonido\n4. El dispositivo comenzará a sonar después de recibir las instrucciones",

View File

@@ -998,6 +998,13 @@
"logOutConfirm": "Êtes-vous sûr de vouloir vous déconnecter ?",
"loginEmail": "(E-mail)",
"makeFriends": "Se faire des amis",
"friendsEmpty": "Pas encore d'amis",
"friendDeleted": "Ami supprimé",
"friendDeleteError": "Erreur lors de la suppression de l'ami",
"friendDeleteConfirm": "Supprimer cet ami ?",
"friendSearchHint": "Rechercher par nom",
"friendsMaxInfo": "*L'appareil peut se connecter avec jusqu'à quatre amis",
"loadingText": "Chargement...",
"male": "Homme",
"maximum": "Maximum",
"minimum": "Minimum",

View File

@@ -998,6 +998,13 @@
"logOutConfirm": "Sei sicuro di voler disconnetterti?",
"loginEmail": "(Email)",
"makeFriends": "Fare amicizia",
"friendsEmpty": "Nessun amico ancora",
"friendDeleted": "Amico eliminato",
"friendDeleteError": "Errore durante l'eliminazione dell'amico",
"friendDeleteConfirm": "Eliminare questo amico?",
"friendSearchHint": "Cerca per nome",
"friendsMaxInfo": "*Il dispositivo può connettersi con un massimo di quattro amici",
"loadingText": "Caricamento...",
"male": "Uomo",
"maximum": "Massimo",
"minimum": "Minimo",

View File

@@ -998,6 +998,13 @@
"logOutConfirm": "Tem certeza de que deseja sair?",
"loginEmail": "(E-mail)",
"makeFriends": "Fazer amigos",
"friendsEmpty": "Ainda sem amigos",
"friendDeleted": "Amigo eliminado",
"friendDeleteError": "Erro ao eliminar amigo",
"friendDeleteConfirm": "Eliminar este amigo?",
"friendSearchHint": "Pesquisar por nome",
"friendsMaxInfo": "*O dispositivo pode conectar-se com até quatro amigos",
"loadingText": "Carregando...",
"male": "Homem",
"maximum": "Máximo",
"minimum": "Mínimo",

View File

@@ -665,6 +665,13 @@ class I18n {
static const String loginSuccess = 'loginSuccess';
static const String mainContactPhoneNumber = 'mainContactPhoneNumber';
static const String makeFriends = 'makeFriends';
static const String friendsEmpty = 'friendsEmpty';
static const String friendDeleted = 'friendDeleted';
static const String friendDeleteError = 'friendDeleteError';
static const String friendDeleteConfirm = 'friendDeleteConfirm';
static const String friendSearchHint = 'friendSearchHint';
static const String friendsMaxInfo = 'friendsMaxInfo';
static const String loadingText = 'loadingText';
static const String male = 'male';
static const String mapTitle = 'mapTitle';
static const String maximum = 'maximum';