feat(wifi-settings): redesign with device commands, WebSocket scan, and connect flow

This commit is contained in:
2026-04-17 03:06:23 +02:00
parent e148b9fdfa
commit 973fc2490c
30 changed files with 1297 additions and 338 deletions

View File

@@ -1,13 +1,16 @@
import 'package:settings/src/core/data/models/create_wifi_network_request_model.dart';
import 'package:settings/src/core/data/models/get_wifi_networks_response_model.dart';
import 'package:settings/src/features/wifi_settings/domain/entities/wifi_network_entity.dart';
abstract class WifiRemoteDatasource {
Future<GetWifiNetworksResponseModel> getWifiNetworks({
required String deviceId,
Future<List<WifiNetworkEntity>> getWifiNetworks({
required String deviceIdentificator,
});
Future<void> createWifiNetwork({
required CreateWifiNetworkRequestModel request,
required String id,
required String deviceIdentificator,
required String ssid,
required String bssid,
required String password,
});
Future<void> deleteWifiNetwork({required String networkId});

View File

@@ -2,6 +2,7 @@ import 'package:dio/dio.dart';
import 'package:legacy_shared/legacy_shared.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
import '../../../features/wifi_settings/domain/entities/wifi_network_entity.dart';
import '../models/create_wifi_network_request_model.dart';
import '../models/get_wifi_networks_response_model.dart';
import 'wifi_remote_datasource.dart';
@@ -12,29 +13,39 @@ class WifiRemoteDatasourceImpl implements WifiRemoteDatasource {
final SaveFamilyRepository _repository;
@override
Future<GetWifiNetworksResponseModel> getWifiNetworks({
required String deviceId,
Future<List<WifiNetworkEntity>> getWifiNetworks({
required String deviceIdentificator,
}) async {
try {
final response = await _repository.get<Map<String, dynamic>>(
'/devices/$deviceId/wifi-networks',
'/devices/identificator/$deviceIdentificator/wifi-networks',
);
final data = response.data;
if (data == null || data.isEmpty) {
return const GetWifiNetworksResponseModel(total: 0, items: []);
}
if (data == null || data.isEmpty) return [];
return GetWifiNetworksResponseModel.fromJson(data);
return GetWifiNetworksResponseModel.fromJson(data).toEntities();
} on DioException catch (error) {
if (error.response?.statusCode == 404) return [];
throw mapDioError(error, defaultMessage: 'Error getting WiFi networks');
}
}
@override
Future<void> createWifiNetwork({
required CreateWifiNetworkRequestModel request,
required String id,
required String deviceIdentificator,
required String ssid,
required String bssid,
required String password,
}) async {
final request = CreateWifiNetworkRequestModel(
id: id,
deviceIdentificator: deviceIdentificator,
ssid: ssid,
bssid: bssid,
password: password,
);
await safeCall(
() => _repository.post<dynamic>('/wifi-networks', body: request.toJson()),
'Error creating WiFi network',

View File

@@ -8,9 +8,10 @@ abstract class CreateWifiNetworkRequestModel
with _$CreateWifiNetworkRequestModel {
const factory CreateWifiNetworkRequestModel({
required String id,
required String deviceId,
required String deviceIdentificator,
required String ssid,
required String bssid,
required String password,
}) = _CreateWifiNetworkRequestModel;
factory CreateWifiNetworkRequestModel.fromJson(Map<String, dynamic> json) =>

View File

@@ -15,7 +15,7 @@ T _$identity<T>(T value) => value;
/// @nodoc
mixin _$CreateWifiNetworkRequestModel {
String get id; String get deviceId; String get ssid; String get bssid;
String get id; String get deviceIdentificator; String get ssid; String get bssid; String get password;
/// Create a copy of CreateWifiNetworkRequestModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -28,16 +28,16 @@ $CreateWifiNetworkRequestModelCopyWith<CreateWifiNetworkRequestModel> get copyWi
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is CreateWifiNetworkRequestModel&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.ssid, ssid) || other.ssid == ssid)&&(identical(other.bssid, bssid) || other.bssid == bssid));
return identical(this, other) || (other.runtimeType == runtimeType&&other is CreateWifiNetworkRequestModel&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceIdentificator, deviceIdentificator) || other.deviceIdentificator == deviceIdentificator)&&(identical(other.ssid, ssid) || other.ssid == ssid)&&(identical(other.bssid, bssid) || other.bssid == bssid)&&(identical(other.password, password) || other.password == password));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,deviceId,ssid,bssid);
int get hashCode => Object.hash(runtimeType,id,deviceIdentificator,ssid,bssid,password);
@override
String toString() {
return 'CreateWifiNetworkRequestModel(id: $id, deviceId: $deviceId, ssid: $ssid, bssid: $bssid)';
return 'CreateWifiNetworkRequestModel(id: $id, deviceIdentificator: $deviceIdentificator, ssid: $ssid, bssid: $bssid, password: $password)';
}
@@ -48,7 +48,7 @@ abstract mixin class $CreateWifiNetworkRequestModelCopyWith<$Res> {
factory $CreateWifiNetworkRequestModelCopyWith(CreateWifiNetworkRequestModel value, $Res Function(CreateWifiNetworkRequestModel) _then) = _$CreateWifiNetworkRequestModelCopyWithImpl;
@useResult
$Res call({
String id, String deviceId, String ssid, String bssid
String id, String deviceIdentificator, String ssid, String bssid, String password
});
@@ -65,12 +65,13 @@ class _$CreateWifiNetworkRequestModelCopyWithImpl<$Res>
/// Create a copy of CreateWifiNetworkRequestModel
/// 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? ssid = null,Object? bssid = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? deviceIdentificator = null,Object? ssid = null,Object? bssid = null,Object? password = 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,deviceIdentificator: null == deviceIdentificator ? _self.deviceIdentificator : deviceIdentificator // ignore: cast_nullable_to_non_nullable
as String,ssid: null == ssid ? _self.ssid : ssid // ignore: cast_nullable_to_non_nullable
as String,bssid: null == bssid ? _self.bssid : bssid // ignore: cast_nullable_to_non_nullable
as String,password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable
as String,
));
}
@@ -156,10 +157,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String deviceId, String ssid, String bssid)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String deviceIdentificator, String ssid, String bssid, String password)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _CreateWifiNetworkRequestModel() when $default != null:
return $default(_that.id,_that.deviceId,_that.ssid,_that.bssid);case _:
return $default(_that.id,_that.deviceIdentificator,_that.ssid,_that.bssid,_that.password);case _:
return orElse();
}
@@ -177,10 +178,10 @@ return $default(_that.id,_that.deviceId,_that.ssid,_that.bssid);case _:
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String deviceId, String ssid, String bssid) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String deviceIdentificator, String ssid, String bssid, String password) $default,) {final _that = this;
switch (_that) {
case _CreateWifiNetworkRequestModel():
return $default(_that.id,_that.deviceId,_that.ssid,_that.bssid);case _:
return $default(_that.id,_that.deviceIdentificator,_that.ssid,_that.bssid,_that.password);case _:
throw StateError('Unexpected subclass');
}
@@ -197,10 +198,10 @@ return $default(_that.id,_that.deviceId,_that.ssid,_that.bssid);case _:
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String deviceId, String ssid, String bssid)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String deviceIdentificator, String ssid, String bssid, String password)? $default,) {final _that = this;
switch (_that) {
case _CreateWifiNetworkRequestModel() when $default != null:
return $default(_that.id,_that.deviceId,_that.ssid,_that.bssid);case _:
return $default(_that.id,_that.deviceIdentificator,_that.ssid,_that.bssid,_that.password);case _:
return null;
}
@@ -212,13 +213,14 @@ return $default(_that.id,_that.deviceId,_that.ssid,_that.bssid);case _:
@JsonSerializable()
class _CreateWifiNetworkRequestModel implements CreateWifiNetworkRequestModel {
const _CreateWifiNetworkRequestModel({required this.id, required this.deviceId, required this.ssid, required this.bssid});
const _CreateWifiNetworkRequestModel({required this.id, required this.deviceIdentificator, required this.ssid, required this.bssid, required this.password});
factory _CreateWifiNetworkRequestModel.fromJson(Map<String, dynamic> json) => _$CreateWifiNetworkRequestModelFromJson(json);
@override final String id;
@override final String deviceId;
@override final String deviceIdentificator;
@override final String ssid;
@override final String bssid;
@override final String password;
/// Create a copy of CreateWifiNetworkRequestModel
/// with the given fields replaced by the non-null parameter values.
@@ -233,16 +235,16 @@ Map<String, dynamic> toJson() {
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CreateWifiNetworkRequestModel&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.ssid, ssid) || other.ssid == ssid)&&(identical(other.bssid, bssid) || other.bssid == bssid));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CreateWifiNetworkRequestModel&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceIdentificator, deviceIdentificator) || other.deviceIdentificator == deviceIdentificator)&&(identical(other.ssid, ssid) || other.ssid == ssid)&&(identical(other.bssid, bssid) || other.bssid == bssid)&&(identical(other.password, password) || other.password == password));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,deviceId,ssid,bssid);
int get hashCode => Object.hash(runtimeType,id,deviceIdentificator,ssid,bssid,password);
@override
String toString() {
return 'CreateWifiNetworkRequestModel(id: $id, deviceId: $deviceId, ssid: $ssid, bssid: $bssid)';
return 'CreateWifiNetworkRequestModel(id: $id, deviceIdentificator: $deviceIdentificator, ssid: $ssid, bssid: $bssid, password: $password)';
}
@@ -253,7 +255,7 @@ abstract mixin class _$CreateWifiNetworkRequestModelCopyWith<$Res> implements $C
factory _$CreateWifiNetworkRequestModelCopyWith(_CreateWifiNetworkRequestModel value, $Res Function(_CreateWifiNetworkRequestModel) _then) = __$CreateWifiNetworkRequestModelCopyWithImpl;
@override @useResult
$Res call({
String id, String deviceId, String ssid, String bssid
String id, String deviceIdentificator, String ssid, String bssid, String password
});
@@ -270,12 +272,13 @@ class __$CreateWifiNetworkRequestModelCopyWithImpl<$Res>
/// Create a copy of CreateWifiNetworkRequestModel
/// 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? ssid = null,Object? bssid = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? deviceIdentificator = null,Object? ssid = null,Object? bssid = null,Object? password = null,}) {
return _then(_CreateWifiNetworkRequestModel(
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,deviceIdentificator: null == deviceIdentificator ? _self.deviceIdentificator : deviceIdentificator // ignore: cast_nullable_to_non_nullable
as String,ssid: null == ssid ? _self.ssid : ssid // ignore: cast_nullable_to_non_nullable
as String,bssid: null == bssid ? _self.bssid : bssid // ignore: cast_nullable_to_non_nullable
as String,password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable
as String,
));
}

View File

@@ -10,16 +10,18 @@ _CreateWifiNetworkRequestModel _$CreateWifiNetworkRequestModelFromJson(
Map<String, dynamic> json,
) => _CreateWifiNetworkRequestModel(
id: json['id'] as String,
deviceId: json['deviceId'] as String,
deviceIdentificator: json['deviceIdentificator'] as String,
ssid: json['ssid'] as String,
bssid: json['bssid'] as String,
password: json['password'] as String,
);
Map<String, dynamic> _$CreateWifiNetworkRequestModelToJson(
_CreateWifiNetworkRequestModel instance,
) => <String, dynamic>{
'id': instance.id,
'deviceId': instance.deviceId,
'deviceIdentificator': instance.deviceIdentificator,
'ssid': instance.ssid,
'bssid': instance.bssid,
'password': instance.password,
};

View File

@@ -1,4 +1,5 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:settings/src/features/wifi_settings/domain/entities/wifi_network_entity.dart';
part 'get_wifi_networks_response_model.freezed.dart';
part 'get_wifi_networks_response_model.g.dart';
@@ -7,8 +8,8 @@ part 'get_wifi_networks_response_model.g.dart';
abstract class GetWifiNetworksResponseModel
with _$GetWifiNetworksResponseModel {
const factory GetWifiNetworksResponseModel({
required int total,
required List<WifiNetworkItemResponseModel> items,
@Default(0) int total,
@Default([]) List<WifiNetworkItemResponseModel> items,
}) = _GetWifiNetworksResponseModel;
factory GetWifiNetworksResponseModel.fromJson(Map<String, dynamic> json) =>
@@ -20,13 +21,31 @@ abstract class WifiNetworkItemResponseModel
with _$WifiNetworkItemResponseModel {
const factory WifiNetworkItemResponseModel({
required String id,
required String deviceId,
@Default('') String deviceIdentificator,
required String ssid,
required String bssid,
required int createdAt,
String? password,
int? createdAt,
int? updatedAt,
}) = _WifiNetworkItemResponseModel;
factory WifiNetworkItemResponseModel.fromJson(Map<String, dynamic> json) =>
_$WifiNetworkItemResponseModelFromJson(json);
}
extension WifiNetworkResponseMapper on GetWifiNetworksResponseModel {
List<WifiNetworkEntity> toEntities() {
return items
.map(
(item) => WifiNetworkEntity(
id: item.id,
ssid: item.ssid,
bssid: item.bssid,
password: item.password,
deviceIdentificator: item.deviceIdentificator,
createdAt: item.createdAt,
),
)
.toList();
}
}

View File

@@ -210,12 +210,12 @@ return $default(_that.total,_that.items);case _:
@JsonSerializable()
class _GetWifiNetworksResponseModel implements GetWifiNetworksResponseModel {
const _GetWifiNetworksResponseModel({required this.total, required final List<WifiNetworkItemResponseModel> items}): _items = items;
const _GetWifiNetworksResponseModel({this.total = 0, final List<WifiNetworkItemResponseModel> items = const []}): _items = items;
factory _GetWifiNetworksResponseModel.fromJson(Map<String, dynamic> json) => _$GetWifiNetworksResponseModelFromJson(json);
@override final int total;
@override@JsonKey() final int total;
final List<WifiNetworkItemResponseModel> _items;
@override List<WifiNetworkItemResponseModel> get items {
@override@JsonKey() List<WifiNetworkItemResponseModel> get items {
if (_items is EqualUnmodifiableListView) return _items;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_items);
@@ -287,7 +287,7 @@ as List<WifiNetworkItemResponseModel>,
/// @nodoc
mixin _$WifiNetworkItemResponseModel {
String get id; String get deviceId; String get ssid; String get bssid; int get createdAt; int? get updatedAt;
String get id; String get deviceIdentificator; String get ssid; String get bssid; String? get password; int? get createdAt; int? get updatedAt;
/// Create a copy of WifiNetworkItemResponseModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -300,16 +300,16 @@ $WifiNetworkItemResponseModelCopyWith<WifiNetworkItemResponseModel> get copyWith
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is WifiNetworkItemResponseModel&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.ssid, ssid) || other.ssid == ssid)&&(identical(other.bssid, bssid) || other.bssid == bssid)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt));
return identical(this, other) || (other.runtimeType == runtimeType&&other is WifiNetworkItemResponseModel&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceIdentificator, deviceIdentificator) || other.deviceIdentificator == deviceIdentificator)&&(identical(other.ssid, ssid) || other.ssid == ssid)&&(identical(other.bssid, bssid) || other.bssid == bssid)&&(identical(other.password, password) || other.password == password)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,deviceId,ssid,bssid,createdAt,updatedAt);
int get hashCode => Object.hash(runtimeType,id,deviceIdentificator,ssid,bssid,password,createdAt,updatedAt);
@override
String toString() {
return 'WifiNetworkItemResponseModel(id: $id, deviceId: $deviceId, ssid: $ssid, bssid: $bssid, createdAt: $createdAt, updatedAt: $updatedAt)';
return 'WifiNetworkItemResponseModel(id: $id, deviceIdentificator: $deviceIdentificator, ssid: $ssid, bssid: $bssid, password: $password, createdAt: $createdAt, updatedAt: $updatedAt)';
}
@@ -320,7 +320,7 @@ abstract mixin class $WifiNetworkItemResponseModelCopyWith<$Res> {
factory $WifiNetworkItemResponseModelCopyWith(WifiNetworkItemResponseModel value, $Res Function(WifiNetworkItemResponseModel) _then) = _$WifiNetworkItemResponseModelCopyWithImpl;
@useResult
$Res call({
String id, String deviceId, String ssid, String bssid, int createdAt, int? updatedAt
String id, String deviceIdentificator, String ssid, String bssid, String? password, int? createdAt, int? updatedAt
});
@@ -337,14 +337,15 @@ class _$WifiNetworkItemResponseModelCopyWithImpl<$Res>
/// Create a copy of WifiNetworkItemResponseModel
/// 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? ssid = null,Object? bssid = null,Object? createdAt = null,Object? updatedAt = freezed,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? deviceIdentificator = null,Object? ssid = null,Object? bssid = null,Object? password = freezed,Object? createdAt = freezed,Object? updatedAt = freezed,}) {
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,deviceIdentificator: null == deviceIdentificator ? _self.deviceIdentificator : deviceIdentificator // ignore: cast_nullable_to_non_nullable
as String,ssid: null == ssid ? _self.ssid : ssid // ignore: cast_nullable_to_non_nullable
as String,bssid: null == bssid ? _self.bssid : bssid // ignore: cast_nullable_to_non_nullable
as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as int,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as String,password: freezed == password ? _self.password : password // ignore: cast_nullable_to_non_nullable
as String?,createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as int?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as int?,
));
}
@@ -430,10 +431,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String deviceId, String ssid, String bssid, int createdAt, int? updatedAt)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String deviceIdentificator, String ssid, String bssid, String? password, int? createdAt, int? updatedAt)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _WifiNetworkItemResponseModel() when $default != null:
return $default(_that.id,_that.deviceId,_that.ssid,_that.bssid,_that.createdAt,_that.updatedAt);case _:
return $default(_that.id,_that.deviceIdentificator,_that.ssid,_that.bssid,_that.password,_that.createdAt,_that.updatedAt);case _:
return orElse();
}
@@ -451,10 +452,10 @@ return $default(_that.id,_that.deviceId,_that.ssid,_that.bssid,_that.createdAt,_
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String deviceId, String ssid, String bssid, int createdAt, int? updatedAt) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String deviceIdentificator, String ssid, String bssid, String? password, int? createdAt, int? updatedAt) $default,) {final _that = this;
switch (_that) {
case _WifiNetworkItemResponseModel():
return $default(_that.id,_that.deviceId,_that.ssid,_that.bssid,_that.createdAt,_that.updatedAt);case _:
return $default(_that.id,_that.deviceIdentificator,_that.ssid,_that.bssid,_that.password,_that.createdAt,_that.updatedAt);case _:
throw StateError('Unexpected subclass');
}
@@ -471,10 +472,10 @@ return $default(_that.id,_that.deviceId,_that.ssid,_that.bssid,_that.createdAt,_
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String deviceId, String ssid, String bssid, int createdAt, int? updatedAt)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String deviceIdentificator, String ssid, String bssid, String? password, int? createdAt, int? updatedAt)? $default,) {final _that = this;
switch (_that) {
case _WifiNetworkItemResponseModel() when $default != null:
return $default(_that.id,_that.deviceId,_that.ssid,_that.bssid,_that.createdAt,_that.updatedAt);case _:
return $default(_that.id,_that.deviceIdentificator,_that.ssid,_that.bssid,_that.password,_that.createdAt,_that.updatedAt);case _:
return null;
}
@@ -486,14 +487,15 @@ return $default(_that.id,_that.deviceId,_that.ssid,_that.bssid,_that.createdAt,_
@JsonSerializable()
class _WifiNetworkItemResponseModel implements WifiNetworkItemResponseModel {
const _WifiNetworkItemResponseModel({required this.id, required this.deviceId, required this.ssid, required this.bssid, required this.createdAt, this.updatedAt});
const _WifiNetworkItemResponseModel({required this.id, this.deviceIdentificator = '', required this.ssid, required this.bssid, this.password, this.createdAt, this.updatedAt});
factory _WifiNetworkItemResponseModel.fromJson(Map<String, dynamic> json) => _$WifiNetworkItemResponseModelFromJson(json);
@override final String id;
@override final String deviceId;
@override@JsonKey() final String deviceIdentificator;
@override final String ssid;
@override final String bssid;
@override final int createdAt;
@override final String? password;
@override final int? createdAt;
@override final int? updatedAt;
/// Create a copy of WifiNetworkItemResponseModel
@@ -509,16 +511,16 @@ Map<String, dynamic> toJson() {
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _WifiNetworkItemResponseModel&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.ssid, ssid) || other.ssid == ssid)&&(identical(other.bssid, bssid) || other.bssid == bssid)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _WifiNetworkItemResponseModel&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceIdentificator, deviceIdentificator) || other.deviceIdentificator == deviceIdentificator)&&(identical(other.ssid, ssid) || other.ssid == ssid)&&(identical(other.bssid, bssid) || other.bssid == bssid)&&(identical(other.password, password) || other.password == password)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,deviceId,ssid,bssid,createdAt,updatedAt);
int get hashCode => Object.hash(runtimeType,id,deviceIdentificator,ssid,bssid,password,createdAt,updatedAt);
@override
String toString() {
return 'WifiNetworkItemResponseModel(id: $id, deviceId: $deviceId, ssid: $ssid, bssid: $bssid, createdAt: $createdAt, updatedAt: $updatedAt)';
return 'WifiNetworkItemResponseModel(id: $id, deviceIdentificator: $deviceIdentificator, ssid: $ssid, bssid: $bssid, password: $password, createdAt: $createdAt, updatedAt: $updatedAt)';
}
@@ -529,7 +531,7 @@ abstract mixin class _$WifiNetworkItemResponseModelCopyWith<$Res> implements $Wi
factory _$WifiNetworkItemResponseModelCopyWith(_WifiNetworkItemResponseModel value, $Res Function(_WifiNetworkItemResponseModel) _then) = __$WifiNetworkItemResponseModelCopyWithImpl;
@override @useResult
$Res call({
String id, String deviceId, String ssid, String bssid, int createdAt, int? updatedAt
String id, String deviceIdentificator, String ssid, String bssid, String? password, int? createdAt, int? updatedAt
});
@@ -546,14 +548,15 @@ class __$WifiNetworkItemResponseModelCopyWithImpl<$Res>
/// Create a copy of WifiNetworkItemResponseModel
/// 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? ssid = null,Object? bssid = null,Object? createdAt = null,Object? updatedAt = freezed,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? deviceIdentificator = null,Object? ssid = null,Object? bssid = null,Object? password = freezed,Object? createdAt = freezed,Object? updatedAt = freezed,}) {
return _then(_WifiNetworkItemResponseModel(
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,deviceIdentificator: null == deviceIdentificator ? _self.deviceIdentificator : deviceIdentificator // ignore: cast_nullable_to_non_nullable
as String,ssid: null == ssid ? _self.ssid : ssid // ignore: cast_nullable_to_non_nullable
as String,bssid: null == bssid ? _self.bssid : bssid // ignore: cast_nullable_to_non_nullable
as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as int,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as String,password: freezed == password ? _self.password : password // ignore: cast_nullable_to_non_nullable
as String?,createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as int?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as int?,
));
}

View File

@@ -9,12 +9,16 @@ part of 'get_wifi_networks_response_model.dart';
_GetWifiNetworksResponseModel _$GetWifiNetworksResponseModelFromJson(
Map<String, dynamic> json,
) => _GetWifiNetworksResponseModel(
total: (json['total'] as num).toInt(),
items: (json['items'] as List<dynamic>)
.map(
(e) => WifiNetworkItemResponseModel.fromJson(e as Map<String, dynamic>),
)
.toList(),
total: (json['total'] as num?)?.toInt() ?? 0,
items:
(json['items'] as List<dynamic>?)
?.map(
(e) => WifiNetworkItemResponseModel.fromJson(
e as Map<String, dynamic>,
),
)
.toList() ??
const [],
);
Map<String, dynamic> _$GetWifiNetworksResponseModelToJson(
@@ -25,10 +29,11 @@ _WifiNetworkItemResponseModel _$WifiNetworkItemResponseModelFromJson(
Map<String, dynamic> json,
) => _WifiNetworkItemResponseModel(
id: json['id'] as String,
deviceId: json['deviceId'] as String,
deviceIdentificator: json['deviceIdentificator'] as String? ?? '',
ssid: json['ssid'] as String,
bssid: json['bssid'] as String,
createdAt: (json['createdAt'] as num).toInt(),
password: json['password'] as String?,
createdAt: (json['createdAt'] as num?)?.toInt(),
updatedAt: (json['updatedAt'] as num?)?.toInt(),
);
@@ -36,9 +41,10 @@ Map<String, dynamic> _$WifiNetworkItemResponseModelToJson(
_WifiNetworkItemResponseModel instance,
) => <String, dynamic>{
'id': instance.id,
'deviceId': instance.deviceId,
'deviceIdentificator': instance.deviceIdentificator,
'ssid': instance.ssid,
'bssid': instance.bssid,
'password': instance.password,
'createdAt': instance.createdAt,
'updatedAt': instance.updatedAt,
};

View File

@@ -1,51 +1,33 @@
import 'package:uuid/uuid.dart';
import '../../../features/wifi_settings/domain/entities/wifi_network_entity.dart';
import '../../domain/repositories/wifi_repository.dart';
import '../datasources/wifi_remote_datasource.dart';
import '../models/create_wifi_network_request_model.dart';
class WifiRepositoryImpl implements WifiRepository {
WifiRepositoryImpl(this._datasource);
static const _uuid = Uuid();
final WifiRemoteDatasource _datasource;
@override
Future<List<WifiNetworkEntity>> getWifiNetworks({
required String deviceId,
}) async {
final response = await _datasource.getWifiNetworks(deviceId: deviceId);
return response.items
.map(
(item) => WifiNetworkEntity(
id: item.id,
ssid: item.ssid,
bssid: item.bssid,
),
)
.toList();
}
required String deviceIdentificator,
}) => _datasource.getWifiNetworks(deviceIdentificator: deviceIdentificator);
@override
Future<void> createWifiNetwork({
required String deviceId,
required String id,
required String deviceIdentificator,
required String ssid,
required String bssid,
}) async {
final request = CreateWifiNetworkRequestModel(
id: _uuid.v4(),
deviceId: deviceId,
ssid: ssid,
bssid: bssid,
);
await _datasource.createWifiNetwork(request: request);
}
required String password,
}) => _datasource.createWifiNetwork(
id: id,
deviceIdentificator: deviceIdentificator,
ssid: ssid,
bssid: bssid,
password: password,
);
@override
Future<void> deleteWifiNetwork({required String networkId}) {
return _datasource.deleteWifiNetwork(networkId: networkId);
}
Future<void> deleteWifiNetwork({required String networkId}) =>
_datasource.deleteWifiNetwork(networkId: networkId);
}

View File

@@ -1,12 +1,16 @@
import 'package:settings/src/features/wifi_settings/domain/entities/wifi_network_entity.dart';
abstract class WifiRepository {
Future<List<WifiNetworkEntity>> getWifiNetworks({required String deviceId});
Future<List<WifiNetworkEntity>> getWifiNetworks({
required String deviceIdentificator,
});
Future<void> createWifiNetwork({
required String deviceId,
required String id,
required String deviceIdentificator,
required String ssid,
required String bssid,
required String password,
});
Future<void> deleteWifiNetwork({required String networkId});

View File

@@ -15,6 +15,12 @@ class SettingsScreen extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final color = theme.getColorFor(ThemeCode.legacyPrimary);
final hasWifi = ref.watch(
selectedDeviceProvider.select((d) {
final types = d.value?.capabilities?.commands;
return types?.types.contains('WIFI_SEARCH') ?? false;
}),
);
return LegacyPageLayout(
theme: theme,
@@ -101,14 +107,15 @@ class SettingsScreen extends ConsumerWidget {
text: I18n.sound,
color: color,
),
// _item(
// context,
// onPressed: () =>
// navigationContract.pushTo(AppRoutes.wifiSettings),
// icon: Icons.wifi_find_outlined,
// text: I18n.wifiSettings,
// color: color,
// ),
if (hasWifi)
_item(
context,
onPressed: () =>
navigationContract.pushTo(AppRoutes.wifiSettings),
icon: Icons.wifi_find_outlined,
text: I18n.wifiSettings,
color: color,
),
// _item(
// context,
// onPressed: () =>

View File

@@ -0,0 +1,11 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'scanned_wifi_network.freezed.dart';
@freezed
abstract class ScannedWifiNetwork with _$ScannedWifiNetwork {
const factory ScannedWifiNetwork({
required String ssid,
required String bssid,
}) = _ScannedWifiNetwork;
}

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

View File

@@ -8,5 +8,8 @@ abstract class WifiNetworkEntity with _$WifiNetworkEntity {
required String id,
required String ssid,
required String bssid,
String? password,
String? deviceIdentificator,
int? createdAt,
}) = _WifiNetworkEntity;
}

View File

@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
/// @nodoc
mixin _$WifiNetworkEntity {
String get id; String get ssid; String get bssid;
String get id; String get ssid; String get bssid; String? get password; String? get deviceIdentificator; int? get createdAt;
/// Create a copy of WifiNetworkEntity
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -25,16 +25,16 @@ $WifiNetworkEntityCopyWith<WifiNetworkEntity> get copyWith => _$WifiNetworkEntit
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is WifiNetworkEntity&&(identical(other.id, id) || other.id == id)&&(identical(other.ssid, ssid) || other.ssid == ssid)&&(identical(other.bssid, bssid) || other.bssid == bssid));
return identical(this, other) || (other.runtimeType == runtimeType&&other is WifiNetworkEntity&&(identical(other.id, id) || other.id == id)&&(identical(other.ssid, ssid) || other.ssid == ssid)&&(identical(other.bssid, bssid) || other.bssid == bssid)&&(identical(other.password, password) || other.password == password)&&(identical(other.deviceIdentificator, deviceIdentificator) || other.deviceIdentificator == deviceIdentificator)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt));
}
@override
int get hashCode => Object.hash(runtimeType,id,ssid,bssid);
int get hashCode => Object.hash(runtimeType,id,ssid,bssid,password,deviceIdentificator,createdAt);
@override
String toString() {
return 'WifiNetworkEntity(id: $id, ssid: $ssid, bssid: $bssid)';
return 'WifiNetworkEntity(id: $id, ssid: $ssid, bssid: $bssid, password: $password, deviceIdentificator: $deviceIdentificator, createdAt: $createdAt)';
}
@@ -45,7 +45,7 @@ abstract mixin class $WifiNetworkEntityCopyWith<$Res> {
factory $WifiNetworkEntityCopyWith(WifiNetworkEntity value, $Res Function(WifiNetworkEntity) _then) = _$WifiNetworkEntityCopyWithImpl;
@useResult
$Res call({
String id, String ssid, String bssid
String id, String ssid, String bssid, String? password, String? deviceIdentificator, int? createdAt
});
@@ -62,12 +62,15 @@ class _$WifiNetworkEntityCopyWithImpl<$Res>
/// Create a copy of WifiNetworkEntity
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? ssid = null,Object? bssid = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? ssid = null,Object? bssid = null,Object? password = freezed,Object? deviceIdentificator = freezed,Object? createdAt = freezed,}) {
return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,ssid: null == ssid ? _self.ssid : ssid // ignore: cast_nullable_to_non_nullable
as String,bssid: null == bssid ? _self.bssid : bssid // ignore: cast_nullable_to_non_nullable
as String,
as String,password: freezed == password ? _self.password : password // ignore: cast_nullable_to_non_nullable
as String?,deviceIdentificator: freezed == deviceIdentificator ? _self.deviceIdentificator : deviceIdentificator // ignore: cast_nullable_to_non_nullable
as String?,createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as int?,
));
}
@@ -152,10 +155,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String ssid, String bssid)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String ssid, String bssid, String? password, String? deviceIdentificator, int? createdAt)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _WifiNetworkEntity() when $default != null:
return $default(_that.id,_that.ssid,_that.bssid);case _:
return $default(_that.id,_that.ssid,_that.bssid,_that.password,_that.deviceIdentificator,_that.createdAt);case _:
return orElse();
}
@@ -173,10 +176,10 @@ return $default(_that.id,_that.ssid,_that.bssid);case _:
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String ssid, String bssid) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String ssid, String bssid, String? password, String? deviceIdentificator, int? createdAt) $default,) {final _that = this;
switch (_that) {
case _WifiNetworkEntity():
return $default(_that.id,_that.ssid,_that.bssid);case _:
return $default(_that.id,_that.ssid,_that.bssid,_that.password,_that.deviceIdentificator,_that.createdAt);case _:
throw StateError('Unexpected subclass');
}
@@ -193,10 +196,10 @@ return $default(_that.id,_that.ssid,_that.bssid);case _:
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String ssid, String bssid)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String ssid, String bssid, String? password, String? deviceIdentificator, int? createdAt)? $default,) {final _that = this;
switch (_that) {
case _WifiNetworkEntity() when $default != null:
return $default(_that.id,_that.ssid,_that.bssid);case _:
return $default(_that.id,_that.ssid,_that.bssid,_that.password,_that.deviceIdentificator,_that.createdAt);case _:
return null;
}
@@ -208,12 +211,15 @@ return $default(_that.id,_that.ssid,_that.bssid);case _:
class _WifiNetworkEntity implements WifiNetworkEntity {
const _WifiNetworkEntity({required this.id, required this.ssid, required this.bssid});
const _WifiNetworkEntity({required this.id, required this.ssid, required this.bssid, this.password, this.deviceIdentificator, this.createdAt});
@override final String id;
@override final String ssid;
@override final String bssid;
@override final String? password;
@override final String? deviceIdentificator;
@override final int? createdAt;
/// Create a copy of WifiNetworkEntity
/// with the given fields replaced by the non-null parameter values.
@@ -225,16 +231,16 @@ _$WifiNetworkEntityCopyWith<_WifiNetworkEntity> get copyWith => __$WifiNetworkEn
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _WifiNetworkEntity&&(identical(other.id, id) || other.id == id)&&(identical(other.ssid, ssid) || other.ssid == ssid)&&(identical(other.bssid, bssid) || other.bssid == bssid));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _WifiNetworkEntity&&(identical(other.id, id) || other.id == id)&&(identical(other.ssid, ssid) || other.ssid == ssid)&&(identical(other.bssid, bssid) || other.bssid == bssid)&&(identical(other.password, password) || other.password == password)&&(identical(other.deviceIdentificator, deviceIdentificator) || other.deviceIdentificator == deviceIdentificator)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt));
}
@override
int get hashCode => Object.hash(runtimeType,id,ssid,bssid);
int get hashCode => Object.hash(runtimeType,id,ssid,bssid,password,deviceIdentificator,createdAt);
@override
String toString() {
return 'WifiNetworkEntity(id: $id, ssid: $ssid, bssid: $bssid)';
return 'WifiNetworkEntity(id: $id, ssid: $ssid, bssid: $bssid, password: $password, deviceIdentificator: $deviceIdentificator, createdAt: $createdAt)';
}
@@ -245,7 +251,7 @@ abstract mixin class _$WifiNetworkEntityCopyWith<$Res> implements $WifiNetworkEn
factory _$WifiNetworkEntityCopyWith(_WifiNetworkEntity value, $Res Function(_WifiNetworkEntity) _then) = __$WifiNetworkEntityCopyWithImpl;
@override @useResult
$Res call({
String id, String ssid, String bssid
String id, String ssid, String bssid, String? password, String? deviceIdentificator, int? createdAt
});
@@ -262,12 +268,15 @@ class __$WifiNetworkEntityCopyWithImpl<$Res>
/// Create a copy of WifiNetworkEntity
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? ssid = null,Object? bssid = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? ssid = null,Object? bssid = null,Object? password = freezed,Object? deviceIdentificator = freezed,Object? createdAt = freezed,}) {
return _then(_WifiNetworkEntity(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,ssid: null == ssid ? _self.ssid : ssid // ignore: cast_nullable_to_non_nullable
as String,bssid: null == bssid ? _self.bssid : bssid // ignore: cast_nullable_to_non_nullable
as String,
as String,password: freezed == password ? _self.password : password // ignore: cast_nullable_to_non_nullable
as String?,deviceIdentificator: freezed == deviceIdentificator ? _self.deviceIdentificator : deviceIdentificator // ignore: cast_nullable_to_non_nullable
as String?,createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as int?,
));
}

View File

@@ -1,12 +1,16 @@
import 'dart:async';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:get_it/get_it.dart';
import 'package:legacy_shared/legacy_shared.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
import 'package:sf_tracking/sf_tracking.dart';
import 'package:uuid/uuid.dart';
import '../../../../core/domain/repositories/wifi_repository.dart';
import '../../../../core/providers/wifi_repository_provider.dart';
import '../../domain/entities/scanned_wifi_network.dart';
import '../../domain/entities/wifi_network_entity.dart';
import 'wifi_settings_view_state.dart';
final wifiSettingsViewModelProvider =
@@ -16,90 +20,260 @@ final wifiSettingsViewModelProvider =
class WifiSettingsViewModel extends Notifier<WifiSettingsViewState> {
late final WifiRepository _repository;
late final CommandsRepository _commands;
late final SfTrackingRepository _tracking;
late final WebSocketService _webSocket;
StreamSubscription<WebSocketEvent>? _webSocketSubscription;
Timer? _scanCountdown;
Timer? _currentNetworkTimeout;
static const _scanDurationSeconds = 30;
@override
WifiSettingsViewState build() {
_repository = ref.read(wifiRepositoryProvider);
_commands = ref.read(commandsRepositoryProvider);
_tracking = ref.read(sfTrackingProvider);
_webSocket = GetIt.I<WebSocketService>();
_webSocketSubscription = _webSocket.events.listen(_onWebSocketEvent);
ref.onDispose(() {
_webSocketSubscription?.cancel();
_scanCountdown?.cancel();
_currentNetworkTimeout?.cancel();
});
Future.microtask(_load);
return const WifiSettingsViewState();
}
Future<void> _load() async {
try {
final device = ref.read(selectedDeviceProvider).value;
if (device == null) return;
String? get _identificator =>
ref.read(selectedDeviceProvider).value?.identificator;
final networks = await _repository.getWifiNetworks(deviceId: device.id);
state = state.copyWith(networks: networks, isLoading: false);
} catch (e) {
void _onWebSocketEvent(WebSocketEvent event) {
switch (event) {
case WifiEvent():
_currentNetworkTimeout?.cancel();
final network = WifiNetworkEntity(
id: '',
ssid: event.ssid,
bssid: event.bssid,
password: event.password,
);
state = state.copyWith(
currentNetwork: network,
isConnecting: false,
isLoadingCurrentNetwork: false,
);
case WifiSearchEvent():
_scanCountdown?.cancel();
final networks = event.wifis
.map(
(wifi) => ScannedWifiNetwork(
ssid: wifi['ssid'] as String? ?? '',
bssid: wifi['bssid'] as String? ?? '',
),
)
.where((network) => network.ssid.isNotEmpty)
.toList();
state = state.copyWith(
availableNetworks: networks,
isScanning: false,
scanSecondsRemaining: 0,
);
default:
break;
}
}
Future<void> _load() async {
final identificator = _identificator;
if (identificator == null) {
state = state.copyWith(isLoading: false);
return;
}
try {
final networks = await _repository.getWifiNetworks(
deviceIdentificator: identificator,
);
if (!ref.mounted) return;
state = state.copyWith(
savedNetworks: networks,
isLoading: false,
isLoadingCurrentNetwork: true,
);
unawaited(
_commands
.send(
request: SendCommandRequestModel(
device: identificator,
command: DeviceCommand.wifiCurrent,
),
)
.catchError((_) {
if (ref.mounted) {
state = state.copyWith(isLoadingCurrentNetwork: false);
}
}),
);
_currentNetworkTimeout?.cancel();
_currentNetworkTimeout = Timer(const Duration(seconds: 15), () {
if (!ref.mounted) return;
if (state.isLoadingCurrentNetwork) {
state = state.copyWith(isLoadingCurrentNetwork: false);
}
});
} catch (_) {
if (!ref.mounted) return;
state = state.copyWith(
isLoading: false,
errorMessage: formatErrorMessage(e),
error: WifiSettingsError.loadFailed,
);
}
}
Future<void> addNetwork({required String ssid, required String bssid}) async {
state = state.copyWith(isSaving: true, errorMessage: '');
Future<void> scanNetworks() async {
final identificator = _identificator;
if (identificator == null) return;
state = state.copyWith(
isScanning: true,
scanSecondsRemaining: _scanDurationSeconds,
error: null,
);
try {
final device = ref.read(selectedDeviceProvider).value;
if (device == null) return;
await _repository.createWifiNetwork(
deviceId: device.id,
ssid: ssid,
bssid: bssid,
await _commands.send(
request: SendCommandRequestModel(
device: identificator,
command: DeviceCommand.wifiSearch,
),
);
final networks = await _repository.getWifiNetworks(deviceId: device.id);
_scanCountdown?.cancel();
_scanCountdown = Timer.periodic(const Duration(seconds: 1), (_) {
if (!ref.mounted) {
_scanCountdown?.cancel();
return;
}
final remaining = state.scanSecondsRemaining - 1;
if (remaining <= 0) {
_scanCountdown?.cancel();
if (state.isScanning) {
state = state.copyWith(
isScanning: false,
scanSecondsRemaining: 0,
error: WifiSettingsError.scanFailed,
);
}
} else {
state = state.copyWith(scanSecondsRemaining: remaining);
}
});
} catch (_) {
if (!ref.mounted) return;
_scanCountdown?.cancel();
state = state.copyWith(
isScanning: false,
scanSecondsRemaining: 0,
error: WifiSettingsError.scanFailed,
);
}
}
Future<void> connectAndSave({
required String ssid,
required String bssid,
required String password,
}) async {
final identificator = _identificator;
if (identificator == null) return;
state = state.copyWith(isConnecting: true, error: null, success: null);
try {
await _commands.send(
request: SendCommandRequestModel(
device: identificator,
command: DeviceCommand.setWifi,
data: {'ssid': ssid, 'bssid': bssid, 'password': password},
),
);
final id = const Uuid().v4();
await _repository.createWifiNetwork(
id: id,
deviceIdentificator: identificator,
ssid: ssid,
bssid: bssid,
password: password,
);
if (!ref.mounted) return;
final networks = await _repository.getWifiNetworks(
deviceIdentificator: identificator,
);
if (!ref.mounted) return;
unawaited(_tracking.legacySettingsWifiAdded(totalCount: networks.length));
state = state.copyWith(
networks: networks,
isSaving: false,
successMessage: I18n.wifiNetworkAdded,
savedNetworks: networks,
isConnecting: false,
success: WifiSettingsSuccess.networkSaved,
);
} catch (e) {
} catch (_) {
if (!ref.mounted) return;
state = state.copyWith(
isSaving: false,
errorMessage: formatErrorMessage(e),
isConnecting: false,
error: WifiSettingsError.saveFailed,
);
}
}
Future<void> removeNetwork(String networkId) async {
state = state.copyWith(isSaving: true, errorMessage: '');
final identificator = _identificator;
if (identificator == null) return;
state = state.copyWith(isSaving: true, error: null, success: null);
try {
final device = ref.read(selectedDeviceProvider).value;
if (device == null) return;
await _repository.deleteWifiNetwork(networkId: networkId);
if (!ref.mounted) return;
final networks = await _repository.getWifiNetworks(deviceId: device.id);
final networks = await _repository.getWifiNetworks(
deviceIdentificator: identificator,
);
if (!ref.mounted) return;
unawaited(
_tracking.legacySettingsWifiRemoved(totalCount: networks.length),
);
state = state.copyWith(
networks: networks,
savedNetworks: networks,
isSaving: false,
successMessage: I18n.wifiNetworkRemoved,
success: WifiSettingsSuccess.networkDeleted,
);
} catch (e) {
} catch (_) {
if (!ref.mounted) return;
state = state.copyWith(
isSaving: false,
errorMessage: formatErrorMessage(e),
error: WifiSettingsError.deleteFailed,
);
}
}
void clearError() {
if (state.error != null) state = state.copyWith(error: null);
}
void clearSuccess() {
state = state.copyWith(successMessage: '');
if (state.success != null) state = state.copyWith(success: null);
}
}

View File

@@ -1,16 +1,27 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import '../../domain/entities/scanned_wifi_network.dart';
import '../../domain/entities/wifi_network_entity.dart';
part 'wifi_settings_view_state.freezed.dart';
enum WifiSettingsError { loadFailed, scanFailed, connectFailed, saveFailed, deleteFailed }
enum WifiSettingsSuccess { networkSaved, networkDeleted, connected }
@freezed
abstract class WifiSettingsViewState with _$WifiSettingsViewState {
const factory WifiSettingsViewState({
@Default([]) List<WifiNetworkEntity> networks,
@Default([]) List<WifiNetworkEntity> savedNetworks,
@Default([]) List<ScannedWifiNetwork> availableNetworks,
WifiNetworkEntity? currentNetwork,
@Default(true) bool isLoading,
@Default(false) bool isLoadingCurrentNetwork,
@Default(false) bool isScanning,
@Default(0) int scanSecondsRemaining,
@Default(false) bool isSaving,
@Default('') String successMessage,
@Default('') String errorMessage,
@Default(false) bool isConnecting,
WifiSettingsError? error,
WifiSettingsSuccess? success,
}) = _WifiSettingsViewState;
}

View File

@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
/// @nodoc
mixin _$WifiSettingsViewState {
List<WifiNetworkEntity> get networks; bool get isLoading; bool get isSaving; String get successMessage; String get errorMessage;
List<WifiNetworkEntity> get savedNetworks; List<ScannedWifiNetwork> get availableNetworks; WifiNetworkEntity? get currentNetwork; bool get isLoading; bool get isLoadingCurrentNetwork; bool get isScanning; int get scanSecondsRemaining; bool get isSaving; bool get isConnecting; WifiSettingsError? get error; WifiSettingsSuccess? get success;
/// Create a copy of WifiSettingsViewState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -25,16 +25,16 @@ $WifiSettingsViewStateCopyWith<WifiSettingsViewState> get copyWith => _$WifiSett
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is WifiSettingsViewState&&const DeepCollectionEquality().equals(other.networks, networks)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isSaving, isSaving) || other.isSaving == isSaving)&&(identical(other.successMessage, successMessage) || other.successMessage == successMessage)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
return identical(this, other) || (other.runtimeType == runtimeType&&other is WifiSettingsViewState&&const DeepCollectionEquality().equals(other.savedNetworks, savedNetworks)&&const DeepCollectionEquality().equals(other.availableNetworks, availableNetworks)&&(identical(other.currentNetwork, currentNetwork) || other.currentNetwork == currentNetwork)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isLoadingCurrentNetwork, isLoadingCurrentNetwork) || other.isLoadingCurrentNetwork == isLoadingCurrentNetwork)&&(identical(other.isScanning, isScanning) || other.isScanning == isScanning)&&(identical(other.scanSecondsRemaining, scanSecondsRemaining) || other.scanSecondsRemaining == scanSecondsRemaining)&&(identical(other.isSaving, isSaving) || other.isSaving == isSaving)&&(identical(other.isConnecting, isConnecting) || other.isConnecting == isConnecting)&&(identical(other.error, error) || other.error == error)&&(identical(other.success, success) || other.success == success));
}
@override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(networks),isLoading,isSaving,successMessage,errorMessage);
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(savedNetworks),const DeepCollectionEquality().hash(availableNetworks),currentNetwork,isLoading,isLoadingCurrentNetwork,isScanning,scanSecondsRemaining,isSaving,isConnecting,error,success);
@override
String toString() {
return 'WifiSettingsViewState(networks: $networks, isLoading: $isLoading, isSaving: $isSaving, successMessage: $successMessage, errorMessage: $errorMessage)';
return 'WifiSettingsViewState(savedNetworks: $savedNetworks, availableNetworks: $availableNetworks, currentNetwork: $currentNetwork, isLoading: $isLoading, isLoadingCurrentNetwork: $isLoadingCurrentNetwork, isScanning: $isScanning, scanSecondsRemaining: $scanSecondsRemaining, isSaving: $isSaving, isConnecting: $isConnecting, error: $error, success: $success)';
}
@@ -45,11 +45,11 @@ abstract mixin class $WifiSettingsViewStateCopyWith<$Res> {
factory $WifiSettingsViewStateCopyWith(WifiSettingsViewState value, $Res Function(WifiSettingsViewState) _then) = _$WifiSettingsViewStateCopyWithImpl;
@useResult
$Res call({
List<WifiNetworkEntity> networks, bool isLoading, bool isSaving, String successMessage, String errorMessage
List<WifiNetworkEntity> savedNetworks, List<ScannedWifiNetwork> availableNetworks, WifiNetworkEntity? currentNetwork, bool isLoading, bool isLoadingCurrentNetwork, bool isScanning, int scanSecondsRemaining, bool isSaving, bool isConnecting, WifiSettingsError? error, WifiSettingsSuccess? success
});
$WifiNetworkEntityCopyWith<$Res>? get currentNetwork;
}
/// @nodoc
@@ -62,17 +62,35 @@ class _$WifiSettingsViewStateCopyWithImpl<$Res>
/// Create a copy of WifiSettingsViewState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? networks = null,Object? isLoading = null,Object? isSaving = null,Object? successMessage = null,Object? errorMessage = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? savedNetworks = null,Object? availableNetworks = null,Object? currentNetwork = freezed,Object? isLoading = null,Object? isLoadingCurrentNetwork = null,Object? isScanning = null,Object? scanSecondsRemaining = null,Object? isSaving = null,Object? isConnecting = null,Object? error = freezed,Object? success = freezed,}) {
return _then(_self.copyWith(
networks: null == networks ? _self.networks : networks // ignore: cast_nullable_to_non_nullable
as List<WifiNetworkEntity>,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,successMessage: null == successMessage ? _self.successMessage : successMessage // ignore: cast_nullable_to_non_nullable
as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,
savedNetworks: null == savedNetworks ? _self.savedNetworks : savedNetworks // ignore: cast_nullable_to_non_nullable
as List<WifiNetworkEntity>,availableNetworks: null == availableNetworks ? _self.availableNetworks : availableNetworks // ignore: cast_nullable_to_non_nullable
as List<ScannedWifiNetwork>,currentNetwork: freezed == currentNetwork ? _self.currentNetwork : currentNetwork // ignore: cast_nullable_to_non_nullable
as WifiNetworkEntity?,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,isLoadingCurrentNetwork: null == isLoadingCurrentNetwork ? _self.isLoadingCurrentNetwork : isLoadingCurrentNetwork // ignore: cast_nullable_to_non_nullable
as bool,isScanning: null == isScanning ? _self.isScanning : isScanning // ignore: cast_nullable_to_non_nullable
as bool,scanSecondsRemaining: null == scanSecondsRemaining ? _self.scanSecondsRemaining : scanSecondsRemaining // ignore: cast_nullable_to_non_nullable
as int,isSaving: null == isSaving ? _self.isSaving : isSaving // ignore: cast_nullable_to_non_nullable
as bool,isConnecting: null == isConnecting ? _self.isConnecting : isConnecting // ignore: cast_nullable_to_non_nullable
as bool,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable
as WifiSettingsError?,success: freezed == success ? _self.success : success // ignore: cast_nullable_to_non_nullable
as WifiSettingsSuccess?,
));
}
/// Create a copy of WifiSettingsViewState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$WifiNetworkEntityCopyWith<$Res>? get currentNetwork {
if (_self.currentNetwork == null) {
return null;
}
return $WifiNetworkEntityCopyWith<$Res>(_self.currentNetwork!, (value) {
return _then(_self.copyWith(currentNetwork: value));
});
}
}
@@ -154,10 +172,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<WifiNetworkEntity> networks, bool isLoading, bool isSaving, String successMessage, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<WifiNetworkEntity> savedNetworks, List<ScannedWifiNetwork> availableNetworks, WifiNetworkEntity? currentNetwork, bool isLoading, bool isLoadingCurrentNetwork, bool isScanning, int scanSecondsRemaining, bool isSaving, bool isConnecting, WifiSettingsError? error, WifiSettingsSuccess? success)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _WifiSettingsViewState() when $default != null:
return $default(_that.networks,_that.isLoading,_that.isSaving,_that.successMessage,_that.errorMessage);case _:
return $default(_that.savedNetworks,_that.availableNetworks,_that.currentNetwork,_that.isLoading,_that.isLoadingCurrentNetwork,_that.isScanning,_that.scanSecondsRemaining,_that.isSaving,_that.isConnecting,_that.error,_that.success);case _:
return orElse();
}
@@ -175,10 +193,10 @@ return $default(_that.networks,_that.isLoading,_that.isSaving,_that.successMessa
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<WifiNetworkEntity> networks, bool isLoading, bool isSaving, String successMessage, String errorMessage) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<WifiNetworkEntity> savedNetworks, List<ScannedWifiNetwork> availableNetworks, WifiNetworkEntity? currentNetwork, bool isLoading, bool isLoadingCurrentNetwork, bool isScanning, int scanSecondsRemaining, bool isSaving, bool isConnecting, WifiSettingsError? error, WifiSettingsSuccess? success) $default,) {final _that = this;
switch (_that) {
case _WifiSettingsViewState():
return $default(_that.networks,_that.isLoading,_that.isSaving,_that.successMessage,_that.errorMessage);case _:
return $default(_that.savedNetworks,_that.availableNetworks,_that.currentNetwork,_that.isLoading,_that.isLoadingCurrentNetwork,_that.isScanning,_that.scanSecondsRemaining,_that.isSaving,_that.isConnecting,_that.error,_that.success);case _:
throw StateError('Unexpected subclass');
}
@@ -195,10 +213,10 @@ return $default(_that.networks,_that.isLoading,_that.isSaving,_that.successMessa
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<WifiNetworkEntity> networks, bool isLoading, bool isSaving, String successMessage, String errorMessage)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<WifiNetworkEntity> savedNetworks, List<ScannedWifiNetwork> availableNetworks, WifiNetworkEntity? currentNetwork, bool isLoading, bool isLoadingCurrentNetwork, bool isScanning, int scanSecondsRemaining, bool isSaving, bool isConnecting, WifiSettingsError? error, WifiSettingsSuccess? success)? $default,) {final _that = this;
switch (_that) {
case _WifiSettingsViewState() when $default != null:
return $default(_that.networks,_that.isLoading,_that.isSaving,_that.successMessage,_that.errorMessage);case _:
return $default(_that.savedNetworks,_that.availableNetworks,_that.currentNetwork,_that.isLoading,_that.isLoadingCurrentNetwork,_that.isScanning,_that.scanSecondsRemaining,_that.isSaving,_that.isConnecting,_that.error,_that.success);case _:
return null;
}
@@ -210,20 +228,32 @@ return $default(_that.networks,_that.isLoading,_that.isSaving,_that.successMessa
class _WifiSettingsViewState implements WifiSettingsViewState {
const _WifiSettingsViewState({final List<WifiNetworkEntity> networks = const [], this.isLoading = true, this.isSaving = false, this.successMessage = '', this.errorMessage = ''}): _networks = networks;
const _WifiSettingsViewState({final List<WifiNetworkEntity> savedNetworks = const [], final List<ScannedWifiNetwork> availableNetworks = const [], this.currentNetwork, this.isLoading = true, this.isLoadingCurrentNetwork = false, this.isScanning = false, this.scanSecondsRemaining = 0, this.isSaving = false, this.isConnecting = false, this.error, this.success}): _savedNetworks = savedNetworks,_availableNetworks = availableNetworks;
final List<WifiNetworkEntity> _networks;
@override@JsonKey() List<WifiNetworkEntity> get networks {
if (_networks is EqualUnmodifiableListView) return _networks;
final List<WifiNetworkEntity> _savedNetworks;
@override@JsonKey() List<WifiNetworkEntity> get savedNetworks {
if (_savedNetworks is EqualUnmodifiableListView) return _savedNetworks;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_networks);
return EqualUnmodifiableListView(_savedNetworks);
}
final List<ScannedWifiNetwork> _availableNetworks;
@override@JsonKey() List<ScannedWifiNetwork> get availableNetworks {
if (_availableNetworks is EqualUnmodifiableListView) return _availableNetworks;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_availableNetworks);
}
@override final WifiNetworkEntity? currentNetwork;
@override@JsonKey() final bool isLoading;
@override@JsonKey() final bool isLoadingCurrentNetwork;
@override@JsonKey() final bool isScanning;
@override@JsonKey() final int scanSecondsRemaining;
@override@JsonKey() final bool isSaving;
@override@JsonKey() final String successMessage;
@override@JsonKey() final String errorMessage;
@override@JsonKey() final bool isConnecting;
@override final WifiSettingsError? error;
@override final WifiSettingsSuccess? success;
/// Create a copy of WifiSettingsViewState
/// with the given fields replaced by the non-null parameter values.
@@ -235,16 +265,16 @@ _$WifiSettingsViewStateCopyWith<_WifiSettingsViewState> get copyWith => __$WifiS
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _WifiSettingsViewState&&const DeepCollectionEquality().equals(other._networks, _networks)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isSaving, isSaving) || other.isSaving == isSaving)&&(identical(other.successMessage, successMessage) || other.successMessage == successMessage)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _WifiSettingsViewState&&const DeepCollectionEquality().equals(other._savedNetworks, _savedNetworks)&&const DeepCollectionEquality().equals(other._availableNetworks, _availableNetworks)&&(identical(other.currentNetwork, currentNetwork) || other.currentNetwork == currentNetwork)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isLoadingCurrentNetwork, isLoadingCurrentNetwork) || other.isLoadingCurrentNetwork == isLoadingCurrentNetwork)&&(identical(other.isScanning, isScanning) || other.isScanning == isScanning)&&(identical(other.scanSecondsRemaining, scanSecondsRemaining) || other.scanSecondsRemaining == scanSecondsRemaining)&&(identical(other.isSaving, isSaving) || other.isSaving == isSaving)&&(identical(other.isConnecting, isConnecting) || other.isConnecting == isConnecting)&&(identical(other.error, error) || other.error == error)&&(identical(other.success, success) || other.success == success));
}
@override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_networks),isLoading,isSaving,successMessage,errorMessage);
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_savedNetworks),const DeepCollectionEquality().hash(_availableNetworks),currentNetwork,isLoading,isLoadingCurrentNetwork,isScanning,scanSecondsRemaining,isSaving,isConnecting,error,success);
@override
String toString() {
return 'WifiSettingsViewState(networks: $networks, isLoading: $isLoading, isSaving: $isSaving, successMessage: $successMessage, errorMessage: $errorMessage)';
return 'WifiSettingsViewState(savedNetworks: $savedNetworks, availableNetworks: $availableNetworks, currentNetwork: $currentNetwork, isLoading: $isLoading, isLoadingCurrentNetwork: $isLoadingCurrentNetwork, isScanning: $isScanning, scanSecondsRemaining: $scanSecondsRemaining, isSaving: $isSaving, isConnecting: $isConnecting, error: $error, success: $success)';
}
@@ -255,11 +285,11 @@ abstract mixin class _$WifiSettingsViewStateCopyWith<$Res> implements $WifiSetti
factory _$WifiSettingsViewStateCopyWith(_WifiSettingsViewState value, $Res Function(_WifiSettingsViewState) _then) = __$WifiSettingsViewStateCopyWithImpl;
@override @useResult
$Res call({
List<WifiNetworkEntity> networks, bool isLoading, bool isSaving, String successMessage, String errorMessage
List<WifiNetworkEntity> savedNetworks, List<ScannedWifiNetwork> availableNetworks, WifiNetworkEntity? currentNetwork, bool isLoading, bool isLoadingCurrentNetwork, bool isScanning, int scanSecondsRemaining, bool isSaving, bool isConnecting, WifiSettingsError? error, WifiSettingsSuccess? success
});
@override $WifiNetworkEntityCopyWith<$Res>? get currentNetwork;
}
/// @nodoc
@@ -272,18 +302,36 @@ class __$WifiSettingsViewStateCopyWithImpl<$Res>
/// Create a copy of WifiSettingsViewState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? networks = null,Object? isLoading = null,Object? isSaving = null,Object? successMessage = null,Object? errorMessage = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? savedNetworks = null,Object? availableNetworks = null,Object? currentNetwork = freezed,Object? isLoading = null,Object? isLoadingCurrentNetwork = null,Object? isScanning = null,Object? scanSecondsRemaining = null,Object? isSaving = null,Object? isConnecting = null,Object? error = freezed,Object? success = freezed,}) {
return _then(_WifiSettingsViewState(
networks: null == networks ? _self._networks : networks // ignore: cast_nullable_to_non_nullable
as List<WifiNetworkEntity>,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,successMessage: null == successMessage ? _self.successMessage : successMessage // ignore: cast_nullable_to_non_nullable
as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,
savedNetworks: null == savedNetworks ? _self._savedNetworks : savedNetworks // ignore: cast_nullable_to_non_nullable
as List<WifiNetworkEntity>,availableNetworks: null == availableNetworks ? _self._availableNetworks : availableNetworks // ignore: cast_nullable_to_non_nullable
as List<ScannedWifiNetwork>,currentNetwork: freezed == currentNetwork ? _self.currentNetwork : currentNetwork // ignore: cast_nullable_to_non_nullable
as WifiNetworkEntity?,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,isLoadingCurrentNetwork: null == isLoadingCurrentNetwork ? _self.isLoadingCurrentNetwork : isLoadingCurrentNetwork // ignore: cast_nullable_to_non_nullable
as bool,isScanning: null == isScanning ? _self.isScanning : isScanning // ignore: cast_nullable_to_non_nullable
as bool,scanSecondsRemaining: null == scanSecondsRemaining ? _self.scanSecondsRemaining : scanSecondsRemaining // ignore: cast_nullable_to_non_nullable
as int,isSaving: null == isSaving ? _self.isSaving : isSaving // ignore: cast_nullable_to_non_nullable
as bool,isConnecting: null == isConnecting ? _self.isConnecting : isConnecting // ignore: cast_nullable_to_non_nullable
as bool,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable
as WifiSettingsError?,success: freezed == success ? _self.success : success // ignore: cast_nullable_to_non_nullable
as WifiSettingsSuccess?,
));
}
/// Create a copy of WifiSettingsViewState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$WifiNetworkEntityCopyWith<$Res>? get currentNetwork {
if (_self.currentNetwork == null) {
return null;
}
return $WifiNetworkEntityCopyWith<$Res>(_self.currentNetwork!, (value) {
return _then(_self.copyWith(currentNetwork: value));
});
}
}
// dart format on

View File

@@ -6,45 +6,63 @@ import 'package:utils/utils.dart';
import '../state/wifi_settings_view_model.dart';
void showAddWifiNetworkSheet(BuildContext context) {
void showConnectWifiSheet(
BuildContext context, {
String? ssid,
String? bssid,
}) {
showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (_) => const _AddWifiNetworkSheet(),
builder: (_) => _ConnectWifiSheet(ssid: ssid, bssid: bssid),
);
}
class _AddWifiNetworkSheet extends ConsumerStatefulWidget {
const _AddWifiNetworkSheet();
class _ConnectWifiSheet extends ConsumerStatefulWidget {
final String? ssid;
final String? bssid;
const _ConnectWifiSheet({this.ssid, this.bssid});
@override
ConsumerState<_AddWifiNetworkSheet> createState() =>
_AddWifiNetworkSheetState();
ConsumerState<_ConnectWifiSheet> createState() => _ConnectWifiSheetState();
}
class _AddWifiNetworkSheetState extends ConsumerState<_AddWifiNetworkSheet> {
final _ssidController = TextEditingController();
final _bssidController = TextEditingController();
class _ConnectWifiSheetState extends ConsumerState<_ConnectWifiSheet> {
late final TextEditingController _ssidController;
late final TextEditingController _bssidController;
final _passwordController = TextEditingController();
bool _obscurePassword = true;
@override
void initState() {
super.initState();
_ssidController = TextEditingController(text: widget.ssid ?? '');
_bssidController = TextEditingController(text: widget.bssid ?? '');
}
@override
void dispose() {
_ssidController.dispose();
_bssidController.dispose();
_passwordController.dispose();
super.dispose();
}
bool get _canSave =>
_ssidController.text.trim().isNotEmpty &&
_bssidController.text.trim().isNotEmpty;
_bssidController.text.trim().isNotEmpty &&
_passwordController.text.trim().isNotEmpty;
void _submit() {
if (!_canSave) return;
final vm = ref.read(wifiSettingsViewModelProvider.notifier);
vm.addNetwork(
vm.connectAndSave(
ssid: _ssidController.text.trim(),
bssid: _bssidController.text.trim(),
password: _passwordController.text.trim(),
);
Navigator.pop(context);
}
@@ -53,10 +71,11 @@ class _AddWifiNetworkSheetState extends ConsumerState<_AddWifiNetworkSheet> {
Widget build(BuildContext context) {
final theme = ref.watch(themePortProvider);
final primaryColor = theme.getColorFor(ThemeCode.legacyPrimary);
final isSaving = ref.watch(
wifiSettingsViewModelProvider.select((s) => s.isSaving),
final isConnecting = ref.watch(
wifiSettingsViewModelProvider.select((s) => s.isConnecting),
);
final bottomInset = MediaQuery.of(context).viewInsets.bottom;
final hasPrefilled = widget.ssid != null;
return Padding(
padding: EdgeInsets.only(bottom: bottomInset),
@@ -91,7 +110,9 @@ class _AddWifiNetworkSheetState extends ConsumerState<_AddWifiNetworkSheet> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
context.translate(I18n.addWifiNetwork),
hasPrefilled
? context.translate(I18n.wifiConnectToNetwork)
: context.translate(I18n.addWifiNetwork),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 20, big: 21),
fontWeight: FontWeight.w600,
@@ -99,11 +120,12 @@ class _AddWifiNetworkSheetState extends ConsumerState<_AddWifiNetworkSheet> {
),
),
TextButton(
onPressed: _canSave && !isSaving ? _submit : null,
child: isSaving
onPressed: _canSave && !isConnecting ? _submit : null,
child: isConnecting
? SizedBox(
width: SizeUtils.getByScreen(small: 20, big: 22),
height: SizeUtils.getByScreen(small: 20, big: 22),
height:
SizeUtils.getByScreen(small: 20, big: 22),
child: CircularProgressIndicator(
strokeWidth: 2,
color: primaryColor,
@@ -134,6 +156,7 @@ class _AddWifiNetworkSheetState extends ConsumerState<_AddWifiNetworkSheet> {
SizedBox(height: 8),
TextField(
controller: _ssidController,
readOnly: hasPrefilled,
onChanged: (_) => setState(() {}),
decoration: _inputDecoration(
hintText: context.translate(I18n.wifiSsidHint),
@@ -151,6 +174,7 @@ class _AddWifiNetworkSheetState extends ConsumerState<_AddWifiNetworkSheet> {
SizedBox(height: 8),
TextField(
controller: _bssidController,
readOnly: hasPrefilled,
onChanged: (_) => setState(() {}),
decoration: _inputDecoration(
hintText: context.translate(I18n.wifiBssidHint),
@@ -158,6 +182,36 @@ class _AddWifiNetworkSheetState extends ConsumerState<_AddWifiNetworkSheet> {
),
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 18)),
Text(
context.translate(I18n.wifiPassword),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 15, big: 16),
fontWeight: FontWeight.w500,
),
),
SizedBox(height: 8),
TextField(
controller: _passwordController,
obscureText: _obscurePassword,
onChanged: (_) => setState(() {}),
decoration: _inputDecoration(
hintText: context.translate(I18n.wifiPasswordHint),
primaryColor: primaryColor,
).copyWith(
suffixIcon: IconButton(
onPressed: () =>
setState(() => _obscurePassword = !_obscurePassword),
icon: Icon(
_obscurePassword
? Icons.visibility_off
: Icons.visibility,
color: Colors.grey,
size: SizeUtils.getByScreen(small: 22, big: 24),
),
),
),
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 18)),
],
),
),

View File

@@ -0,0 +1,84 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:utils/utils.dart';
import '../../domain/entities/scanned_wifi_network.dart';
class AvailableWifiNetworkCard extends ConsumerWidget {
final ScannedWifiNetwork network;
final VoidCallback onTap;
const AvailableWifiNetworkCard({
super.key,
required this.network,
required this.onTap,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final primaryColor = theme.getColorFor(ThemeCode.legacyPrimary);
return Padding(
padding: EdgeInsets.only(
bottom: SizeUtils.getByScreen(small: 10, big: 8),
),
child: GestureDetector(
onTap: onTap,
child: Container(
padding: EdgeInsets.symmetric(
horizontal: SizeUtils.getByScreen(small: 16, big: 14),
vertical: SizeUtils.getByScreen(small: 14, big: 12),
),
decoration: BoxDecoration(
color: theme.getColorFor(ThemeCode.backgroundSecondary),
borderRadius: BorderRadius.all(Radius.circular(16)),
),
child: Row(
children: [
Icon(
Icons.wifi,
color: primaryColor,
size: SizeUtils.getByScreen(small: 28, big: 30),
),
SizedBox(width: SizeUtils.getByScreen(small: 12, big: 14)),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
network.ssid,
style: TextStyle(
fontWeight: FontWeight.w600,
color: theme.getColorFor(ThemeCode.textPrimary),
fontSize: SizeUtils.getByScreen(small: 15, big: 16),
),
),
SizedBox(height: 2),
Text(
network.bssid,
style: TextStyle(
color: theme
.getColorFor(ThemeCode.textPrimary)
.withAlpha(178),
fontSize: SizeUtils.getByScreen(small: 13, big: 14),
),
),
],
),
),
Icon(
Icons.chevron_right,
color: theme
.getColorFor(ThemeCode.textPrimary)
.withAlpha(178),
size: SizeUtils.getByScreen(small: 24, big: 26),
),
],
),
),
),
);
}
}

View File

@@ -7,6 +7,8 @@ import 'package:sf_localizations/sf_localizations.dart';
import 'package:utils/utils.dart';
import 'state/wifi_settings_view_model.dart';
import 'state/wifi_settings_view_state.dart';
import 'widgets/available_wifi_network_card.dart';
import 'widgets/add_wifi_network_sheet.dart';
import 'widgets/wifi_network_card.dart';
@@ -19,33 +21,41 @@ class WifiSettingsScreen extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final state = ref.watch(wifiSettingsViewModelProvider);
final vm = ref.read(wifiSettingsViewModelProvider.notifier);
final primaryColor = theme.getColorFor(ThemeCode.legacyPrimary);
ref.listen(wifiSettingsViewModelProvider.select((s) => s.errorMessage), (
ref.listen(wifiSettingsViewModelProvider.select((s) => s.error), (
_,
errorMessage,
error,
) {
if (errorMessage.isNotEmpty) {
showTopSnackbar(
context,
message: errorMessage,
type: MessageType.error,
);
}
if (error == null) return;
final message = switch (error) {
WifiSettingsError.loadFailed => context.translate(I18n.wifiLoadError),
WifiSettingsError.scanFailed => context.translate(I18n.wifiScanFailed),
WifiSettingsError.connectFailed =>
context.translate(I18n.wifiConnectFailed),
WifiSettingsError.saveFailed => context.translate(I18n.wifiSaveFailed),
WifiSettingsError.deleteFailed =>
context.translate(I18n.wifiDeleteFailed),
};
showTopSnackbar(context, message: message, type: MessageType.error);
vm.clearError();
});
ref.listen(wifiSettingsViewModelProvider.select((s) => s.successMessage), (
ref.listen(wifiSettingsViewModelProvider.select((s) => s.success), (
_,
successMessage,
success,
) {
if (successMessage.isNotEmpty) {
showTopSnackbar(
context,
message: context.translate(successMessage),
type: MessageType.success,
);
ref.read(wifiSettingsViewModelProvider.notifier).clearSuccess();
}
if (success == null) return;
final message = switch (success) {
WifiSettingsSuccess.networkSaved =>
context.translate(I18n.wifiNetworkSaved),
WifiSettingsSuccess.networkDeleted =>
context.translate(I18n.wifiNetworkDeleted),
WifiSettingsSuccess.connected => context.translate(I18n.wifiConnected),
};
showTopSnackbar(context, message: message, type: MessageType.success);
vm.clearSuccess();
});
return Scaffold(
@@ -65,7 +75,7 @@ class WifiSettingsScreen extends ConsumerWidget {
),
),
title: Text(
context.translate(I18n.wifiSettings).toUpperCase(),
'WiFi',
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 20, big: 19),
fontWeight: FontWeight.w500,
@@ -86,10 +96,10 @@ class WifiSettingsScreen extends ConsumerWidget {
child: IconButton(
onPressed: () {
if (!guardDeviceCommand(context, ref)) return;
showAddWifiNetworkSheet(context);
vm.scanNetworks();
},
icon: Icon(
Icons.add,
Icons.wifi_find,
color: Colors.white,
size: SizeUtils.getByScreen(small: 24, big: 22),
),
@@ -102,68 +112,19 @@ class WifiSettingsScreen extends ConsumerWidget {
top: false,
child: state.isLoading
? const Center(child: CircularProgressIndicator())
: state.networks.isEmpty
? _EmptyState(primaryColor: primaryColor)
: _NetworkList(),
: _Body(),
),
);
}
}
class _EmptyState extends StatelessWidget {
final Color primaryColor;
const _EmptyState({required this.primaryColor});
@override
Widget build(BuildContext context) {
return Center(
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: SizeUtils.getByScreen(small: 32, big: 30),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.wifi_off_outlined,
color: primaryColor,
size: SizeUtils.getByScreen(small: 120, big: 140),
),
SizedBox(height: SizeUtils.getByScreen(small: 20, big: 24)),
Text(
context.translate(I18n.noWifiNetworks),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 16, big: 17),
fontWeight: FontWeight.w500,
color: Colors.grey,
),
),
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 10)),
Text(
context.translate(I18n.noWifiNetworksDescription),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 14, big: 15),
color: Colors.grey,
),
),
],
),
),
);
}
}
class _NetworkList extends ConsumerWidget {
const _NetworkList();
class _Body extends ConsumerWidget {
const _Body();
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final networks = ref.watch(
wifiSettingsViewModelProvider.select((s) => s.networks),
);
final state = ref.watch(wifiSettingsViewModelProvider);
final primaryColor = theme.getColorFor(ThemeCode.legacyPrimary);
return SingleChildScrollView(
@@ -175,46 +136,145 @@ class _NetworkList extends ConsumerWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(
bottom: SizeUtils.getByScreen(small: 12, big: 10),
),
child: Text(
context.translate(I18n.wifiDescription),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 14, big: 15),
color: theme
.getColorFor(ThemeCode.textPrimary)
.withAlpha(178),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_sectionTitle(
context,
context.translate(I18n.wifiCurrentNetwork),
primaryColor,
),
if (state.isLoadingCurrentNetwork)
SizedBox(
width: SizeUtils.getByScreen(small: 16, big: 14),
height: SizeUtils.getByScreen(small: 16, big: 14),
child: CircularProgressIndicator(
strokeWidth: 2,
color: primaryColor,
),
),
],
),
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 6)),
if (state.currentNetwork != null)
_CurrentNetworkCard(
ssid: state.currentNetwork!.ssid,
bssid: state.currentNetwork!.bssid,
theme: theme,
)
else if (!state.isLoadingCurrentNetwork)
Padding(
padding: EdgeInsets.only(
bottom: SizeUtils.getByScreen(small: 12, big: 10),
),
child: Text(
context.translate(I18n.wifiNoCurrentNetwork),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 14, big: 15),
color: theme
.getColorFor(ThemeCode.textPrimary)
.withAlpha(178),
),
),
),
),
...List.generate(networks.length, (index) {
final network = networks[index];
return WifiNetworkCard(
network: network,
onDelete: () => _confirmDelete(context, ref, network),
theme: theme,
);
}),
SizedBox(height: SizeUtils.getByScreen(small: 12, big: 10)),
Text(
if (state.availableNetworks.isNotEmpty || state.isScanning) ...[
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 14)),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_sectionTitle(
context,
context.translate(I18n.wifiAvailableNetworks),
primaryColor,
),
if (state.isScanning)
Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'${state.scanSecondsRemaining}s',
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 13, big: 12),
fontWeight: FontWeight.w600,
color: primaryColor,
),
),
SizedBox(width: SizeUtils.getByScreen(small: 6, big: 5)),
SizedBox(
width: SizeUtils.getByScreen(small: 16, big: 14),
height: SizeUtils.getByScreen(small: 16, big: 14),
child: CircularProgressIndicator(
strokeWidth: 2,
color: primaryColor,
),
),
],
),
],
),
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 6)),
...state.availableNetworks.map(
(network) => AvailableWifiNetworkCard(
network: network,
onTap: () => showConnectWifiSheet(
context,
ssid: network.ssid,
bssid: network.bssid,
),
),
),
],
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 14)),
_sectionTitle(
context,
context.translate(
I18n.wifiNetworksCount,
args: {'count': networks.length.toString()},
),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 13, big: 14),
color: primaryColor,
fontWeight: FontWeight.w500,
I18n.wifiSavedNetworks,
args: {'count': state.savedNetworks.length.toString()},
),
primaryColor,
),
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 6)),
if (state.savedNetworks.isEmpty)
Padding(
padding: EdgeInsets.only(
bottom: SizeUtils.getByScreen(small: 12, big: 10),
),
child: Text(
context.translate(I18n.noWifiNetworks),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 14, big: 15),
color: theme
.getColorFor(ThemeCode.textPrimary)
.withAlpha(178),
),
),
)
else
...state.savedNetworks.map(
(network) => WifiNetworkCard(
network: network,
onDelete: () => _confirmDelete(context, ref, network),
theme: theme,
),
),
],
),
),
);
}
Widget _sectionTitle(BuildContext context, String text, Color color) {
return Text(
text.toUpperCase(),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 13, big: 14),
fontWeight: FontWeight.w600,
letterSpacing: 0.5,
color: color,
),
);
}
void _confirmDelete(BuildContext context, WidgetRef ref, dynamic network) {
if (!guardDeviceCommand(context, ref)) return;
final theme = ref.read(themePortProvider);
@@ -255,3 +315,69 @@ class _NetworkList extends ConsumerWidget {
);
}
}
class _CurrentNetworkCard extends StatelessWidget {
final String ssid;
final String bssid;
final ThemePort theme;
const _CurrentNetworkCard({
required this.ssid,
required this.bssid,
required this.theme,
});
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(
bottom: SizeUtils.getByScreen(small: 10, big: 8),
),
child: Container(
padding: EdgeInsets.symmetric(
horizontal: SizeUtils.getByScreen(small: 16, big: 14),
vertical: SizeUtils.getByScreen(small: 14, big: 12),
),
decoration: BoxDecoration(
color: theme.getColorFor(ThemeCode.backgroundSecondary),
borderRadius: BorderRadius.all(Radius.circular(16)),
),
child: Row(
children: [
Icon(
Icons.wifi,
color: theme.getColorFor(ThemeCode.legacyPrimary),
size: SizeUtils.getByScreen(small: 28, big: 30),
),
SizedBox(width: SizeUtils.getByScreen(small: 12, big: 14)),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
ssid,
style: TextStyle(
fontWeight: FontWeight.w600,
color: theme.getColorFor(ThemeCode.textPrimary),
fontSize: SizeUtils.getByScreen(small: 15, big: 16),
),
),
SizedBox(height: 2),
Text(
bssid,
style: TextStyle(
color: theme
.getColorFor(ThemeCode.textPrimary)
.withAlpha(178),
fontSize: SizeUtils.getByScreen(small: 13, big: 14),
),
),
],
),
),
],
),
),
);
}
}

View File

@@ -24,6 +24,14 @@ enum DeviceCommand {
setBackgroundImage,
@JsonValue('SET_SOUND_MODE')
setSoundMode,
@JsonValue('WIFI_CURRENT')
wifiCurrent,
@JsonValue('WIFI_SEARCH')
wifiSearch,
@JsonValue('SET_WIFI')
setWifi,
@JsonValue('WIFI_DELETE')
wifiDelete,
}
@freezed

View File

@@ -33,4 +33,8 @@ const _$DeviceCommandEnumMap = {
DeviceCommand.shutdown: 'SHUTDOWN',
DeviceCommand.setBackgroundImage: 'SET_BACKGROUND_IMG',
DeviceCommand.setSoundMode: 'SET_SOUND_MODE',
DeviceCommand.wifiCurrent: 'WIFI_CURRENT',
DeviceCommand.wifiSearch: 'WIFI_SEARCH',
DeviceCommand.setWifi: 'SET_WIFI',
DeviceCommand.wifiDelete: 'WIFI_DELETE',
};

View File

@@ -576,6 +576,22 @@
"wifiBssid": "MAC-Adresse (BSSID)",
"wifiSsidHint": "z.B. MeinHeimWLAN",
"wifiBssidHint": "z.B. 0c:80:63:e4:cb:e1",
"wifiCurrentNetwork": "Aktuelles Netzwerk",
"wifiAvailableNetworks": "Verfügbare Netzwerke",
"wifiSavedNetworks": "{count} gespeicherte Netzwerke",
"wifiScan": "Scannen",
"wifiNoCurrentNetwork": "Kein Netzwerk verbunden",
"wifiLoadError": "Fehler beim Laden der WLAN-Netzwerke",
"wifiScanFailed": "Netzwerksuche fehlgeschlagen",
"wifiConnectFailed": "Verbindung fehlgeschlagen",
"wifiSaveFailed": "Netzwerk speichern fehlgeschlagen",
"wifiDeleteFailed": "Netzwerk löschen fehlgeschlagen",
"wifiNetworkSaved": "Netzwerk erfolgreich gespeichert",
"wifiNetworkDeleted": "Netzwerk erfolgreich gelöscht",
"wifiConnected": "Erfolgreich verbunden",
"wifiPassword": "Passwort",
"wifiPasswordHint": "Netzwerk-Passwort eingeben",
"wifiConnectToNetwork": "Mit Netzwerk verbinden",
"editChildProfile": "Profil bearbeiten",
"editChildProfileTitle": "Kinderprofil bearbeiten",
"editChildProfileSaveSuccess": "Kinderprofil erfolgreich aktualisiert",

View File

@@ -733,6 +733,22 @@
"wifiBssid": "MAC address (BSSID)",
"wifiSsidHint": "e.g. MyHomeWiFi",
"wifiBssidHint": "e.g. 0c:80:63:e4:cb:e1",
"wifiCurrentNetwork": "Current network",
"wifiAvailableNetworks": "Available networks",
"wifiSavedNetworks": "{count} saved networks",
"wifiScan": "Scan",
"wifiNoCurrentNetwork": "No network connected",
"wifiLoadError": "Error loading WiFi networks",
"wifiScanFailed": "Failed to scan networks",
"wifiConnectFailed": "Failed to connect",
"wifiSaveFailed": "Failed to save network",
"wifiDeleteFailed": "Failed to delete network",
"wifiNetworkSaved": "Network saved successfully",
"wifiNetworkDeleted": "Network deleted successfully",
"wifiConnected": "Connected successfully",
"wifiPassword": "Password",
"wifiPasswordHint": "Enter the network password",
"wifiConnectToNetwork": "Connect to network",
"editChildProfile": "Edit profile",
"editChildProfileSaveSuccess": "Child profile updated successfully",
"editChildProfileTitle": "Edit child profile",

View File

@@ -734,6 +734,22 @@
"wifiBssid": "Dirección MAC (BSSID)",
"wifiSsidHint": "ej. MiWiFiCasa",
"wifiBssidHint": "ej. 0c:80:63:e4:cb:e1",
"wifiCurrentNetwork": "Red actual",
"wifiAvailableNetworks": "Redes disponibles",
"wifiSavedNetworks": "{count} redes guardadas",
"wifiScan": "Escanear",
"wifiNoCurrentNetwork": "Sin red conectada",
"wifiLoadError": "Error al cargar redes WiFi",
"wifiScanFailed": "Error al escanear redes",
"wifiConnectFailed": "Error al conectar",
"wifiSaveFailed": "Error al guardar red",
"wifiDeleteFailed": "Error al eliminar red",
"wifiNetworkSaved": "Red guardada correctamente",
"wifiNetworkDeleted": "Red eliminada correctamente",
"wifiConnected": "Conectado correctamente",
"wifiPassword": "Contraseña",
"wifiPasswordHint": "Ingresa la contraseña de la red",
"wifiConnectToNetwork": "Conectar a red",
"editChildProfile": "Editar perfil",
"editChildProfileTitle": "Editar perfil del niño",
"editChildProfileSaveSuccess": "Perfil del niño actualizado correctamente",

View File

@@ -576,6 +576,22 @@
"wifiBssid": "Adresse MAC (BSSID)",
"wifiSsidHint": "ex. MonWiFiMaison",
"wifiBssidHint": "ex. 0c:80:63:e4:cb:e1",
"wifiCurrentNetwork": "Réseau actuel",
"wifiAvailableNetworks": "Réseaux disponibles",
"wifiSavedNetworks": "{count} réseaux enregistrés",
"wifiScan": "Scanner",
"wifiNoCurrentNetwork": "Aucun réseau connecté",
"wifiLoadError": "Erreur lors du chargement des réseaux WiFi",
"wifiScanFailed": "Échec de la recherche de réseaux",
"wifiConnectFailed": "Échec de la connexion",
"wifiSaveFailed": "Échec de l'enregistrement du réseau",
"wifiDeleteFailed": "Échec de la suppression du réseau",
"wifiNetworkSaved": "Réseau enregistré avec succès",
"wifiNetworkDeleted": "Réseau supprimé avec succès",
"wifiConnected": "Connecté avec succès",
"wifiPassword": "Mot de passe",
"wifiPasswordHint": "Entrez le mot de passe du réseau",
"wifiConnectToNetwork": "Se connecter au réseau",
"editChildProfile": "Modifier le profil",
"editChildProfileTitle": "Modifier le profil de l'enfant",
"editChildProfileSaveSuccess": "Profil de l'enfant mis à jour avec succès",

View File

@@ -576,6 +576,22 @@
"wifiBssid": "Indirizzo MAC (BSSID)",
"wifiSsidHint": "es. MiaReteCasa",
"wifiBssidHint": "es. 0c:80:63:e4:cb:e1",
"wifiCurrentNetwork": "Rete attuale",
"wifiAvailableNetworks": "Reti disponibili",
"wifiSavedNetworks": "{count} reti salvate",
"wifiScan": "Scansiona",
"wifiNoCurrentNetwork": "Nessuna rete connessa",
"wifiLoadError": "Errore nel caricamento delle reti WiFi",
"wifiScanFailed": "Scansione reti fallita",
"wifiConnectFailed": "Connessione fallita",
"wifiSaveFailed": "Salvataggio rete fallito",
"wifiDeleteFailed": "Eliminazione rete fallita",
"wifiNetworkSaved": "Rete salvata con successo",
"wifiNetworkDeleted": "Rete eliminata con successo",
"wifiConnected": "Connesso con successo",
"wifiPassword": "Password",
"wifiPasswordHint": "Inserisci la password della rete",
"wifiConnectToNetwork": "Connetti alla rete",
"editChildProfile": "Modifica profilo",
"editChildProfileTitle": "Modifica profilo del bambino",
"editChildProfileSaveSuccess": "Profilo del bambino aggiornato con successo",

View File

@@ -576,6 +576,22 @@
"wifiBssid": "Endereço MAC (BSSID)",
"wifiSsidHint": "ex. MinhaRedeWiFi",
"wifiBssidHint": "ex. 0c:80:63:e4:cb:e1",
"wifiCurrentNetwork": "Rede atual",
"wifiAvailableNetworks": "Redes disponíveis",
"wifiSavedNetworks": "{count} redes guardadas",
"wifiScan": "Procurar",
"wifiNoCurrentNetwork": "Nenhuma rede conectada",
"wifiLoadError": "Erro ao carregar redes WiFi",
"wifiScanFailed": "Falha ao procurar redes",
"wifiConnectFailed": "Falha ao conectar",
"wifiSaveFailed": "Falha ao guardar rede",
"wifiDeleteFailed": "Falha ao eliminar rede",
"wifiNetworkSaved": "Rede guardada com sucesso",
"wifiNetworkDeleted": "Rede eliminada com sucesso",
"wifiConnected": "Conectado com sucesso",
"wifiPassword": "Palavra-passe",
"wifiPasswordHint": "Introduza a palavra-passe da rede",
"wifiConnectToNetwork": "Conectar à rede",
"editChildProfile": "Editar perfil",
"editChildProfileTitle": "Editar perfil da criança",
"editChildProfileSaveSuccess": "Perfil da criança atualizado com sucesso",

View File

@@ -986,12 +986,28 @@ class I18n {
static const String wednesday = 'wednesday';
static const String welcome = 'welcome';
static const String whitelistDescription = 'whitelistDescription';
static const String wifiAvailableNetworks = 'wifiAvailableNetworks';
static const String wifiBssid = 'wifiBssid';
static const String wifiBssidHint = 'wifiBssidHint';
static const String wifiConnectFailed = 'wifiConnectFailed';
static const String wifiConnectToNetwork = 'wifiConnectToNetwork';
static const String wifiConnected = 'wifiConnected';
static const String wifiCurrentNetwork = 'wifiCurrentNetwork';
static const String wifiDeleteFailed = 'wifiDeleteFailed';
static const String wifiDescription = 'wifiDescription';
static const String wifiLoadError = 'wifiLoadError';
static const String wifiNetworkAdded = 'wifiNetworkAdded';
static const String wifiNetworkDeleted = 'wifiNetworkDeleted';
static const String wifiNetworkRemoved = 'wifiNetworkRemoved';
static const String wifiNetworkSaved = 'wifiNetworkSaved';
static const String wifiNetworksCount = 'wifiNetworksCount';
static const String wifiNoCurrentNetwork = 'wifiNoCurrentNetwork';
static const String wifiPassword = 'wifiPassword';
static const String wifiPasswordHint = 'wifiPasswordHint';
static const String wifiSaveFailed = 'wifiSaveFailed';
static const String wifiSavedNetworks = 'wifiSavedNetworks';
static const String wifiScan = 'wifiScan';
static const String wifiScanFailed = 'wifiScanFailed';
static const String wifiSettings = 'wifiSettings';
static const String wifiSsid = 'wifiSsid';
static const String wifiSsidHint = 'wifiSsidHint';