settings ui
This commit is contained in:
@@ -1 +1 @@
|
||||
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_secure_storage","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.3/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"flutter_secure_storage","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_android","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_android-2.2.20/","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"flutter_secure_storage_macos","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_macos-3.1.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.3/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"flutter_secure_storage_linux","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_linux-1.2.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_linux","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[],"dev_dependency":false}],"windows":[{"name":"flutter_secure_storage_windows","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_windows-3.1.2/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_windows","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[],"dev_dependency":false}],"web":[{"name":"flutter_secure_storage_web","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_web-1.2.1/","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"flutter_secure_storage","dependencies":["flutter_secure_storage_linux","flutter_secure_storage_macos","flutter_secure_storage_web","flutter_secure_storage_windows"]},{"name":"flutter_secure_storage_linux","dependencies":[]},{"name":"flutter_secure_storage_macos","dependencies":[]},{"name":"flutter_secure_storage_web","dependencies":[]},{"name":"flutter_secure_storage_windows","dependencies":["path_provider"]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]}],"date_created":"2026-02-27 12:35:56.235180","version":"3.35.7","swift_package_manager_enabled":{"ios":false,"macos":false}}
|
||||
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_secure_storage","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_secure_storage-9.2.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.4.3\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"flutter_secure_storage","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_secure_storage-9.2.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_android","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_android-2.2.20\\\\","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"flutter_secure_storage_macos","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_secure_storage_macos-3.1.3\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.4.3\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"flutter_secure_storage_linux","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_secure_storage_linux-1.2.3\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_linux","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_linux-2.2.1\\\\","native_build":false,"dependencies":[],"dev_dependency":false}],"windows":[{"name":"flutter_secure_storage_windows","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_secure_storage_windows-3.1.2\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_windows","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_windows-2.3.0\\\\","native_build":false,"dependencies":[],"dev_dependency":false}],"web":[{"name":"flutter_secure_storage_web","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_secure_storage_web-1.2.1\\\\","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"flutter_secure_storage","dependencies":["flutter_secure_storage_linux","flutter_secure_storage_macos","flutter_secure_storage_web","flutter_secure_storage_windows"]},{"name":"flutter_secure_storage_linux","dependencies":[]},{"name":"flutter_secure_storage_macos","dependencies":[]},{"name":"flutter_secure_storage_web","dependencies":[]},{"name":"flutter_secure_storage_windows","dependencies":["path_provider"]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]}],"date_created":"2026-03-09 09:15:02.914305","version":"3.35.6","swift_package_manager_enabled":{"ios":false,"macos":false}}
|
||||
File diff suppressed because one or more lines are too long
@@ -16,6 +16,7 @@ import 'package:navigation/navigation.dart';
|
||||
import 'package:notifications/notifications.dart';
|
||||
import 'package:payments/payments.dart';
|
||||
import 'package:profile/profile.dart';
|
||||
import 'package:settings/settings.dart';
|
||||
import 'package:splash/splash.dart';
|
||||
|
||||
final GlobalKey<NavigatorState> rootNavigatorKey = GlobalKey<NavigatorState>();
|
||||
@@ -141,8 +142,95 @@ void configureAppRouter() {
|
||||
),
|
||||
],
|
||||
),
|
||||
StatefulShellBranch(
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: AppRoutes.settings,
|
||||
name: 'settings',
|
||||
pageBuilder: SettingsBuilder().buildPage,
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: AppRoutes.alarm,
|
||||
name: 'alarm',
|
||||
pageBuilder: AlarmBuilder().buildPage,
|
||||
),
|
||||
/*GoRoute (
|
||||
path: AppRoutes.appStore,
|
||||
name: 'app_store',
|
||||
// pageBuilder: AppStoreBuilder().buildPage,
|
||||
),
|
||||
GoRoute (
|
||||
path: AppRoutes.battery,
|
||||
name: 'battery',
|
||||
// pageBuilder: BatteryBuilder().buildPage,
|
||||
),
|
||||
GoRoute (
|
||||
path: AppRoutes.blockPhone,
|
||||
name: 'block_phone',
|
||||
// pageBuilder: BlockPhoneBuilder().buildPage,
|
||||
),
|
||||
GoRoute (
|
||||
path: AppRoutes.disableFunctions,
|
||||
name: 'disable_functions',
|
||||
// pageBuilder: DisableFunctionsBuilder().buildPage,
|
||||
),
|
||||
GoRoute (
|
||||
path: AppRoutes.language,
|
||||
name: 'language',
|
||||
// pageBuilder: LanguageBuilder().buildPage,
|
||||
),
|
||||
GoRoute (
|
||||
path: AppRoutes.legacyNotifications,
|
||||
name: 'legacy_notifications',
|
||||
// pageBuilder: LegacyNotificationsBuilder().buildPage,
|
||||
),*/
|
||||
GoRoute (
|
||||
path: AppRoutes.remoteManagement,
|
||||
name: 'remote_management',
|
||||
pageBuilder: RemoteManagementBuilder().buildPage,
|
||||
),
|
||||
/*GoRoute (
|
||||
path: AppRoutes.remoteOnOff,
|
||||
name: 'remote_on_off',
|
||||
// pageBuilder: RemoteOnOffBuilder().buildPage,
|
||||
),
|
||||
GoRoute (
|
||||
path: AppRoutes.smsAlert,
|
||||
name: 'sms_alert',
|
||||
// pageBuilder: SmsAlertBuilder().buildPage,
|
||||
),*/
|
||||
GoRoute (
|
||||
path: AppRoutes.sosContacts,
|
||||
name: 'sos_agenda',
|
||||
pageBuilder: SosContactsBuilder().buildPage,
|
||||
),
|
||||
GoRoute (
|
||||
path: AppRoutes.sound,
|
||||
name: 'sound',
|
||||
pageBuilder: SoundBuilder().buildPage,
|
||||
),
|
||||
/*GoRoute (
|
||||
path: AppRoutes.syncClock,
|
||||
name: 'sync_clock',
|
||||
// pageBuilder: SyncClockBuilder().buildPage,
|
||||
),
|
||||
GoRoute (
|
||||
path: AppRoutes.timezone,
|
||||
name: 'timezone',
|
||||
// pageBuilder: TimezoneBuilder().buildPage,
|
||||
),
|
||||
GoRoute (
|
||||
path: AppRoutes.wifiSettings,
|
||||
name: 'wifi_settings',
|
||||
// pageBuilder: WifiSettingsBuilder().buildPage,
|
||||
),*/
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
GoRoute(
|
||||
path: AppRoutes.login,
|
||||
name: 'login',
|
||||
|
||||
@@ -1081,6 +1081,13 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.0"
|
||||
settings:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../modules/legacy/modules/settings"
|
||||
relative: true
|
||||
source: path
|
||||
version: "1.0.0+1"
|
||||
sf_infrastructure:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
@@ -65,6 +65,8 @@ dependencies:
|
||||
path: ../../modules/legacy/modules/location
|
||||
legacy_auth:
|
||||
path: ../../modules/legacy/modules/legacy_auth
|
||||
settings:
|
||||
path: ../../modules/legacy/modules/settings
|
||||
#packages dependencies go here
|
||||
navigation:
|
||||
path: ../../packages/navigation
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
# melos_managed_dependency_overrides: account,activity,auth,customer_service,dashboard_shell,design_system,flutter_treezor_entrust_sdk_bridge,fonts,home,legacy_dashboard_shell,legacy_shared,navigation,notifications,payments,profile,sca_treezor,sf_infrastructure,sf_localizations,sf_shared,splash,utils,control_panel,device_management,legacy_auth,location
|
||||
# melos_managed_dependency_overrides: settings
|
||||
# melos_managed_dependency_overrides: account,activity,auth,customer_service,dashboard_shell,design_system,flutter_treezor_entrust_sdk_bridge,fonts,home,legacy_dashboard_shell,legacy_shared,navigation,notifications,payments,profile,sca_treezor,sf_infrastructure,sf_localizations,sf_shared,splash,utils,control_panel,device_management
|
||||
dependency_overrides:
|
||||
account:
|
||||
path: ../../modules/legacy/modules/account
|
||||
@@ -40,6 +42,8 @@ dependency_overrides:
|
||||
path: ../../modules/profile
|
||||
sca_treezor:
|
||||
path: ../../packages/sca_treezor
|
||||
settings:
|
||||
path: ..\\..\\modules\\legacy\\modules\\settings
|
||||
sf_infrastructure:
|
||||
path: ../../packages/sf_infrastructure
|
||||
sf_localizations:
|
||||
|
||||
@@ -176,9 +176,10 @@ class _MenuSection extends ConsumerWidget {
|
||||
text: I18n.accountSettings,
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 7)),
|
||||
// TODO: Implementar navegación a Device Settings
|
||||
_SectionButton(
|
||||
onPressed: () {},
|
||||
onPressed: () {
|
||||
navigationContract.pushTo(AppRoutes.settings);
|
||||
},
|
||||
icon: Icons.settings_outlined,
|
||||
text: I18n.deviceSettings,
|
||||
),
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
abstract class SettingsRemoteDatasource {
|
||||
// Future<UserEntity> getLoggedUser({required String token});
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:settings/src/core/data/datasources/settings_remote_datasource.dart';
|
||||
import 'package:sf_infrastructure/sf_infrastructure.dart';
|
||||
|
||||
class SettingsRemoteDatasourceImpl implements SettingsRemoteDatasource {
|
||||
SettingsRemoteDatasourceImpl(this._repository);
|
||||
|
||||
final QuestiaRepository _repository;
|
||||
|
||||
/*@override
|
||||
Future<UserEntity> getLoggedUser({required String token}) async {
|
||||
try {
|
||||
final response = await _repository.get<Map<String, dynamic>>(
|
||||
'/users/api/auth/me',
|
||||
);
|
||||
final data = response.data!['item'];
|
||||
if (data == null || data.isEmpty) {
|
||||
throw Exception('Empty response from /auth/me');
|
||||
}
|
||||
|
||||
final model = GetLoggedUserResponseModel.fromJson(data);
|
||||
final model = GetLoggedUserResponseModel(item:
|
||||
GetLoggedUserItemResponseModel(
|
||||
id: '1111',
|
||||
firstName: 'Juan',
|
||||
email: 'juan@test.com',
|
||||
phone: '111111111'));
|
||||
return model.toEntity();
|
||||
} on DioException catch (error) {
|
||||
throw _mapDioError(
|
||||
error,
|
||||
defaultMessage: error.message ?? 'Error getting logged user',
|
||||
);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
Exception _mapDioError(DioException error, {required String defaultMessage}) {
|
||||
final apiMsg = _extractApiMessage(error.response?.data);
|
||||
final msg = apiMsg ?? error.message ?? defaultMessage;
|
||||
return Exception(msg);
|
||||
}
|
||||
|
||||
String? _extractApiMessage(Object? data) {
|
||||
if (data == null) return null;
|
||||
|
||||
if (data is Map) {
|
||||
final errorObj = data['error'];
|
||||
if (errorObj is Map && errorObj['message'] is String) {
|
||||
return (errorObj['message'] as String).trim();
|
||||
}
|
||||
if (data['message'] is String) {
|
||||
return (data['message'] as String).trim();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (data is String) {
|
||||
final raw = data.trim();
|
||||
if (raw.isEmpty) return null;
|
||||
|
||||
try {
|
||||
final decoded = jsonDecode(raw);
|
||||
return _extractApiMessage(decoded);
|
||||
} catch (_) {
|
||||
return raw;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import 'package:settings/src/core/data/datasources/settings_remote_datasource.dart';
|
||||
import 'package:settings/src/core/domain/repositories/settings_repository.dart';
|
||||
|
||||
class SettingsRepositoryImpl implements SettingsRepository {
|
||||
const SettingsRepositoryImpl(this._remote);
|
||||
|
||||
final SettingsRemoteDatasource _remote;
|
||||
|
||||
/*@override
|
||||
Future<List<ContactEntity>> getContacts({required String userId}) {
|
||||
return _remote.getContacts(userId: userId);
|
||||
}*/
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
abstract class SettingsRepository {
|
||||
// Future<List<ContactEntity>> getContacts({required String userId});
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:settings/src/core/data/datasources/settings_remote_datasource.dart';
|
||||
import 'package:settings/src/core/data/datasources/settings_remote_datasource_impl.dart';
|
||||
import 'package:sf_infrastructure/sf_infrastructure.dart';
|
||||
|
||||
final settingsRemoteDatasourceProvider = Provider<SettingsRemoteDatasource>((ref) {
|
||||
final questiaRepository = getIt<QuestiaRepository>();
|
||||
return SettingsRemoteDatasourceImpl(questiaRepository);
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:settings/src/core/data/repositories/settings_repository_impl.dart';
|
||||
import 'package:settings/src/core/domain/repositories/settings_repository.dart';
|
||||
import 'package:settings/src/core/providers/settings_remote_datasource_provider.dart';
|
||||
|
||||
final settingsRepositoryProvider = Provider<SettingsRepository>((ref) {
|
||||
final remote = ref.read(settingsRemoteDatasourceProvider);
|
||||
return SettingsRepositoryImpl(remote);
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:navigation/navigation.dart';
|
||||
import 'package:settings/src/features/alarm/presentation/alarm_screen.dart';
|
||||
|
||||
class AlarmBuilder {
|
||||
const AlarmBuilder();
|
||||
|
||||
Page<void> buildPage(BuildContext context, GoRouterState state) {
|
||||
final NavigationContract navigationContract = GetIt.I<NavigationContract>();
|
||||
|
||||
return MaterialPage<void>(
|
||||
key: state.pageKey,
|
||||
child: AlarmScreen(navigationContract: navigationContract),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'alarm_entity.freezed.dart';
|
||||
|
||||
@freezed
|
||||
abstract class AlarmEntity with _$AlarmEntity {
|
||||
const factory AlarmEntity({
|
||||
required String id,
|
||||
}) = _AlarmEntity;
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
// 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 'alarm_entity.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$AlarmEntity {
|
||||
|
||||
String get id;
|
||||
/// Create a copy of AlarmEntity
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$AlarmEntityCopyWith<AlarmEntity> get copyWith => _$AlarmEntityCopyWithImpl<AlarmEntity>(this as AlarmEntity, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is AlarmEntity&&(identical(other.id, id) || other.id == id));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AlarmEntity(id: $id)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $AlarmEntityCopyWith<$Res> {
|
||||
factory $AlarmEntityCopyWith(AlarmEntity value, $Res Function(AlarmEntity) _then) = _$AlarmEntityCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String id
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$AlarmEntityCopyWithImpl<$Res>
|
||||
implements $AlarmEntityCopyWith<$Res> {
|
||||
_$AlarmEntityCopyWithImpl(this._self, this._then);
|
||||
|
||||
final AlarmEntity _self;
|
||||
final $Res Function(AlarmEntity) _then;
|
||||
|
||||
/// Create a copy of AlarmEntity
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [AlarmEntity].
|
||||
extension AlarmEntityPatterns on AlarmEntity {
|
||||
/// 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( _AlarmEntity value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _AlarmEntity() 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( _AlarmEntity value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _AlarmEntity():
|
||||
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( _AlarmEntity value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _AlarmEntity() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AlarmEntity() when $default != null:
|
||||
return $default(_that.id);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AlarmEntity():
|
||||
return $default(_that.id);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AlarmEntity() when $default != null:
|
||||
return $default(_that.id);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _AlarmEntity implements AlarmEntity {
|
||||
const _AlarmEntity({required this.id});
|
||||
|
||||
|
||||
@override final String id;
|
||||
|
||||
/// Create a copy of AlarmEntity
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$AlarmEntityCopyWith<_AlarmEntity> get copyWith => __$AlarmEntityCopyWithImpl<_AlarmEntity>(this, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AlarmEntity&&(identical(other.id, id) || other.id == id));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AlarmEntity(id: $id)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$AlarmEntityCopyWith<$Res> implements $AlarmEntityCopyWith<$Res> {
|
||||
factory _$AlarmEntityCopyWith(_AlarmEntity value, $Res Function(_AlarmEntity) _then) = __$AlarmEntityCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String id
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$AlarmEntityCopyWithImpl<$Res>
|
||||
implements _$AlarmEntityCopyWith<$Res> {
|
||||
__$AlarmEntityCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _AlarmEntity _self;
|
||||
final $Res Function(_AlarmEntity) _then;
|
||||
|
||||
/// Create a copy of AlarmEntity
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,}) {
|
||||
return _then(_AlarmEntity(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
import 'package:settings/src/features/alarm/domain/entities/alarm_entity.dart';
|
||||
|
||||
abstract class GetAlarmsUseCase {
|
||||
Future<List<AlarmEntity>> getAlarms({required String deviceId});
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import 'package:settings/src/core/domain/repositories/settings_repository.dart';
|
||||
import 'package:settings/src/features/alarm/domain/entities/alarm_entity.dart';
|
||||
import 'package:settings/src/features/alarm/domain/get_alarms_use_case.dart';
|
||||
|
||||
class GetAlarmsUseCaseImpl implements GetAlarmsUseCase {
|
||||
GetAlarmsUseCaseImpl(this._repository);
|
||||
|
||||
final SettingsRepository _repository;
|
||||
|
||||
@override
|
||||
Future<List<AlarmEntity>> getAlarms({required String deviceId}) async {
|
||||
return [];
|
||||
// return _repository.getAlarms(deviceId: deviceId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:navigation/navigation.dart';
|
||||
import 'package:settings/src/features/alarm/domain/entities/alarm_entity.dart';
|
||||
import 'package:settings/src/features/alarm/presentation/state/alarm_view_model.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
|
||||
import 'widgets/new_alarm_dialog.dart';
|
||||
|
||||
class AlarmScreen extends ConsumerWidget {
|
||||
|
||||
final NavigationContract navigationContract;
|
||||
|
||||
const AlarmScreen({
|
||||
super.key,
|
||||
required this.navigationContract,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.read(themePortProvider);
|
||||
|
||||
return LegacyPageLayout(
|
||||
theme: theme,
|
||||
title: context.translate(I18n.alarm),
|
||||
body: Column(
|
||||
children: [
|
||||
Center(child:
|
||||
Icon(Icons.alarm_outlined,
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
size: 240,
|
||||
)
|
||||
),
|
||||
SizedBox(height: 36),
|
||||
Text(context.translate(I18n.alarmsMessage),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
SizedBox(height: 36),
|
||||
_AlarmsSection()
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AlarmsSection extends ConsumerWidget {
|
||||
|
||||
const _AlarmsSection();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final alarms = ref.watch(
|
||||
alarmViewModelProvider.select((s)=>s.alarms)
|
||||
);
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
children: alarms.map((alarm) {
|
||||
if (alarm == null) {
|
||||
return const _NewAlarmButton();
|
||||
}
|
||||
else{
|
||||
return _AlarmButton(alarm: alarm);
|
||||
}
|
||||
}).toList()
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _NewAlarmButton extends ConsumerWidget {
|
||||
|
||||
const _NewAlarmButton();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.read(themePortProvider);
|
||||
|
||||
return TextButton(
|
||||
onPressed: (){showDialog(context: context, builder: (context)=>Dialog(
|
||||
child: NewAlarmDialog()
|
||||
));},
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 18, vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.getColorFor(ThemeCode.backgroundSecondary),
|
||||
borderRadius: BorderRadius.all(Radius.circular(18))
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(context.translate(I18n.addAlarm),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: theme.getColorFor(ThemeCode.textPrimary),
|
||||
fontSize: 18
|
||||
),
|
||||
),
|
||||
DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary)
|
||||
),
|
||||
child: Icon(
|
||||
Icons.add,
|
||||
color: theme.getColorFor(ThemeCode.textSecondary),
|
||||
size: 58,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class _AlarmButton extends ConsumerWidget {
|
||||
|
||||
final AlarmEntity alarm;
|
||||
|
||||
const _AlarmButton({
|
||||
required this.alarm,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.read(themePortProvider);
|
||||
|
||||
return TextButton(
|
||||
onPressed: (){showDialog(context: context, builder: (context)=>Dialog(
|
||||
child: NewAlarmDialog()
|
||||
));},
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.getColorFor(ThemeCode.backgroundSecondary),
|
||||
borderRadius: BorderRadius.all(Radius.circular(18))
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:settings/src/core/providers/settings_repository_provider.dart';
|
||||
import 'package:settings/src/features/alarm/domain/get_alarms_use_case.dart';
|
||||
import 'package:settings/src/features/alarm/domain/get_alarms_use_case_impl.dart';
|
||||
|
||||
final getAlarmsUseCaseProvider = Provider.autoDispose<GetAlarmsUseCase>((ref) {
|
||||
final settingsRepository = ref.read(settingsRepositoryProvider);
|
||||
return GetAlarmsUseCaseImpl(settingsRepository);
|
||||
});
|
||||
@@ -0,0 +1,73 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:settings/src/features/alarm/domain/get_alarms_use_case.dart';
|
||||
import 'package:settings/src/features/alarm/presentation/providers/get_alarms_use_case_provider.dart';
|
||||
import 'package:settings/src/features/alarm/presentation/state/alarm_view_state.dart';
|
||||
|
||||
import '../../domain/entities/alarm_entity.dart';
|
||||
|
||||
final alarmViewModelProvider =
|
||||
NotifierProvider.autoDispose<AlarmViewModel, AlarmViewState>(
|
||||
AlarmViewModel.new,
|
||||
);
|
||||
|
||||
class AlarmViewModel extends Notifier<AlarmViewState> {
|
||||
late final GetAlarmsUseCase _getAlarmsUseCase;
|
||||
|
||||
// late final UserEntity loggedUser;
|
||||
|
||||
@override
|
||||
AlarmViewState build() {
|
||||
_getAlarmsUseCase = ref.read(getAlarmsUseCaseProvider);
|
||||
|
||||
Future.microtask(() => load());
|
||||
|
||||
return const AlarmViewState();
|
||||
}
|
||||
|
||||
Future<void> load() async {
|
||||
final device = ref.read(selectedDeviceProvider);
|
||||
|
||||
// final alarms = await _getAlarmsUseCase.getAlarms(deviceId: device.identificator);
|
||||
// setAlarms(alarms);
|
||||
}
|
||||
|
||||
Future<void> setAlarms(List<AlarmEntity> alarms) async {
|
||||
state = state.copyWith(
|
||||
alarms: alarms,
|
||||
isLoading: false,
|
||||
);
|
||||
}
|
||||
|
||||
void setAlarmTime(TimeOfDay? value) {
|
||||
if (value == null) return;
|
||||
if (state.alarmTime == value) return;
|
||||
|
||||
state = state.copyWith(
|
||||
alarmTime: value,
|
||||
);
|
||||
}
|
||||
|
||||
void setDateOption(String value) {
|
||||
if (state.dateOption == value) return;
|
||||
|
||||
state = state.copyWith(
|
||||
dateOption: value,
|
||||
);
|
||||
}
|
||||
|
||||
void toggleAlarmDay(int index) {
|
||||
List<bool> alarmDays = state.alarmDays.toList();
|
||||
|
||||
alarmDays[index] = !alarmDays[index];
|
||||
|
||||
state = state.copyWith(
|
||||
alarmDays: alarmDays,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> setAlarm() async {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:settings/src/features/alarm/domain/entities/alarm_entity.dart';
|
||||
|
||||
part 'alarm_view_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
abstract class AlarmViewState with _$AlarmViewState {
|
||||
const factory AlarmViewState({
|
||||
@Default([null, null, null]) List<AlarmEntity?> alarms,
|
||||
@Default(TimeOfDay(hour: 0, minute: 0)) TimeOfDay alarmTime,
|
||||
@Default('ONCE') String dateOption,
|
||||
@Default([false, false, false, false, false, false, false]) List<bool> alarmDays,
|
||||
@Default(true) bool isLoading,
|
||||
@Default('') String errorMessage,
|
||||
}) = _AlarmViewState;
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
// 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 'alarm_view_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$AlarmViewState {
|
||||
|
||||
List<AlarmEntity?> get alarms; TimeOfDay get alarmTime; String get dateOption; List<bool> get alarmDays; bool get isLoading; String get errorMessage;
|
||||
/// Create a copy of AlarmViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$AlarmViewStateCopyWith<AlarmViewState> get copyWith => _$AlarmViewStateCopyWithImpl<AlarmViewState>(this as AlarmViewState, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is AlarmViewState&&const DeepCollectionEquality().equals(other.alarms, alarms)&&(identical(other.alarmTime, alarmTime) || other.alarmTime == alarmTime)&&(identical(other.dateOption, dateOption) || other.dateOption == dateOption)&&const DeepCollectionEquality().equals(other.alarmDays, alarmDays)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(alarms),alarmTime,dateOption,const DeepCollectionEquality().hash(alarmDays),isLoading,errorMessage);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AlarmViewState(alarms: $alarms, alarmTime: $alarmTime, dateOption: $dateOption, alarmDays: $alarmDays, isLoading: $isLoading, errorMessage: $errorMessage)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $AlarmViewStateCopyWith<$Res> {
|
||||
factory $AlarmViewStateCopyWith(AlarmViewState value, $Res Function(AlarmViewState) _then) = _$AlarmViewStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
List<AlarmEntity?> alarms, TimeOfDay alarmTime, String dateOption, List<bool> alarmDays, bool isLoading, String errorMessage
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$AlarmViewStateCopyWithImpl<$Res>
|
||||
implements $AlarmViewStateCopyWith<$Res> {
|
||||
_$AlarmViewStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final AlarmViewState _self;
|
||||
final $Res Function(AlarmViewState) _then;
|
||||
|
||||
/// Create a copy of AlarmViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? alarms = null,Object? alarmTime = null,Object? dateOption = null,Object? alarmDays = null,Object? isLoading = null,Object? errorMessage = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
alarms: null == alarms ? _self.alarms : alarms // ignore: cast_nullable_to_non_nullable
|
||||
as List<AlarmEntity?>,alarmTime: null == alarmTime ? _self.alarmTime : alarmTime // ignore: cast_nullable_to_non_nullable
|
||||
as TimeOfDay,dateOption: null == dateOption ? _self.dateOption : dateOption // ignore: cast_nullable_to_non_nullable
|
||||
as String,alarmDays: null == alarmDays ? _self.alarmDays : alarmDays // ignore: cast_nullable_to_non_nullable
|
||||
as List<bool>,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [AlarmViewState].
|
||||
extension AlarmViewStatePatterns on AlarmViewState {
|
||||
/// 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( _AlarmViewState value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _AlarmViewState() 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( _AlarmViewState value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _AlarmViewState():
|
||||
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( _AlarmViewState value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _AlarmViewState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<AlarmEntity?> alarms, TimeOfDay alarmTime, String dateOption, List<bool> alarmDays, bool isLoading, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AlarmViewState() when $default != null:
|
||||
return $default(_that.alarms,_that.alarmTime,_that.dateOption,_that.alarmDays,_that.isLoading,_that.errorMessage);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<AlarmEntity?> alarms, TimeOfDay alarmTime, String dateOption, List<bool> alarmDays, bool isLoading, String errorMessage) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AlarmViewState():
|
||||
return $default(_that.alarms,_that.alarmTime,_that.dateOption,_that.alarmDays,_that.isLoading,_that.errorMessage);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<AlarmEntity?> alarms, TimeOfDay alarmTime, String dateOption, List<bool> alarmDays, bool isLoading, String errorMessage)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AlarmViewState() when $default != null:
|
||||
return $default(_that.alarms,_that.alarmTime,_that.dateOption,_that.alarmDays,_that.isLoading,_that.errorMessage);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _AlarmViewState implements AlarmViewState {
|
||||
const _AlarmViewState({final List<AlarmEntity?> alarms = const [null, null, null], this.alarmTime = const TimeOfDay(hour: 0, minute: 0), this.dateOption = 'ONCE', final List<bool> alarmDays = const [false, false, false, false, false, false, false], this.isLoading = true, this.errorMessage = ''}): _alarms = alarms,_alarmDays = alarmDays;
|
||||
|
||||
|
||||
final List<AlarmEntity?> _alarms;
|
||||
@override@JsonKey() List<AlarmEntity?> get alarms {
|
||||
if (_alarms is EqualUnmodifiableListView) return _alarms;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_alarms);
|
||||
}
|
||||
|
||||
@override@JsonKey() final TimeOfDay alarmTime;
|
||||
@override@JsonKey() final String dateOption;
|
||||
final List<bool> _alarmDays;
|
||||
@override@JsonKey() List<bool> get alarmDays {
|
||||
if (_alarmDays is EqualUnmodifiableListView) return _alarmDays;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_alarmDays);
|
||||
}
|
||||
|
||||
@override@JsonKey() final bool isLoading;
|
||||
@override@JsonKey() final String errorMessage;
|
||||
|
||||
/// Create a copy of AlarmViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$AlarmViewStateCopyWith<_AlarmViewState> get copyWith => __$AlarmViewStateCopyWithImpl<_AlarmViewState>(this, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AlarmViewState&&const DeepCollectionEquality().equals(other._alarms, _alarms)&&(identical(other.alarmTime, alarmTime) || other.alarmTime == alarmTime)&&(identical(other.dateOption, dateOption) || other.dateOption == dateOption)&&const DeepCollectionEquality().equals(other._alarmDays, _alarmDays)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_alarms),alarmTime,dateOption,const DeepCollectionEquality().hash(_alarmDays),isLoading,errorMessage);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AlarmViewState(alarms: $alarms, alarmTime: $alarmTime, dateOption: $dateOption, alarmDays: $alarmDays, isLoading: $isLoading, errorMessage: $errorMessage)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$AlarmViewStateCopyWith<$Res> implements $AlarmViewStateCopyWith<$Res> {
|
||||
factory _$AlarmViewStateCopyWith(_AlarmViewState value, $Res Function(_AlarmViewState) _then) = __$AlarmViewStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
List<AlarmEntity?> alarms, TimeOfDay alarmTime, String dateOption, List<bool> alarmDays, bool isLoading, String errorMessage
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$AlarmViewStateCopyWithImpl<$Res>
|
||||
implements _$AlarmViewStateCopyWith<$Res> {
|
||||
__$AlarmViewStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _AlarmViewState _self;
|
||||
final $Res Function(_AlarmViewState) _then;
|
||||
|
||||
/// Create a copy of AlarmViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? alarms = null,Object? alarmTime = null,Object? dateOption = null,Object? alarmDays = null,Object? isLoading = null,Object? errorMessage = null,}) {
|
||||
return _then(_AlarmViewState(
|
||||
alarms: null == alarms ? _self._alarms : alarms // ignore: cast_nullable_to_non_nullable
|
||||
as List<AlarmEntity?>,alarmTime: null == alarmTime ? _self.alarmTime : alarmTime // ignore: cast_nullable_to_non_nullable
|
||||
as TimeOfDay,dateOption: null == dateOption ? _self.dateOption : dateOption // ignore: cast_nullable_to_non_nullable
|
||||
as String,alarmDays: null == alarmDays ? _self._alarmDays : alarmDays // ignore: cast_nullable_to_non_nullable
|
||||
as List<bool>,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
@@ -0,0 +1,245 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:settings/src/features/alarm/presentation/state/alarm_view_model.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
|
||||
class NewAlarmDialog extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.read(themePortProvider);
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: theme.getColorFor(ThemeCode.backgroundSecondary),
|
||||
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||
),
|
||||
height: 290,
|
||||
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 20),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
_Header(theme: theme),
|
||||
const _TimeSection(),
|
||||
const _DaysSection(),
|
||||
const _SaveSection(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _Header extends StatelessWidget {
|
||||
|
||||
final ThemePort theme;
|
||||
|
||||
const _Header({
|
||||
required this.theme,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
Center(child: Text(context.translate(I18n.alarmSettings).toUpperCase(),
|
||||
style: TextStyle(
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary)
|
||||
),
|
||||
)),
|
||||
Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: IconButton(
|
||||
onPressed: (){Navigator.pop(context);},
|
||||
padding: EdgeInsets.zero,
|
||||
icon: Icon(
|
||||
Icons.close,
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
size: 18,
|
||||
)
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _TimeSection extends ConsumerWidget {
|
||||
|
||||
const _TimeSection();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.read(themePortProvider);
|
||||
|
||||
final vm = ref.read(alarmViewModelProvider.notifier);
|
||||
final alarmTime = ref.watch(
|
||||
alarmViewModelProvider.select((s) => s.alarmTime)
|
||||
);
|
||||
final dateOption = ref.watch(
|
||||
alarmViewModelProvider.select((s) => s.dateOption)
|
||||
);
|
||||
|
||||
Map<String, String> dateOptions = <String, String>{
|
||||
'ONCE': context.translate(I18n.once),
|
||||
'DAILY': context.translate(I18n.daily),
|
||||
'SELECT': context.translate(I18n.selectDay),
|
||||
};
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Text(context.translate(I18n.setDateTime),
|
||||
style: TextStyle(
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary)
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
final time = await showTimePicker(
|
||||
context: context,
|
||||
initialTime: const TimeOfDay(hour: 00, minute: 00),
|
||||
builder: (BuildContext context, Widget? child) {
|
||||
return MediaQuery(
|
||||
data: MediaQuery.of(context).copyWith(alwaysUse24HourFormat: true),
|
||||
child: child!,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
vm.setAlarmTime(time);
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.fromBorderSide(BorderSide(
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary)
|
||||
)),
|
||||
borderRadius: BorderRadius.all(Radius.circular(14))
|
||||
),
|
||||
width: 128,
|
||||
height: 50,
|
||||
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4),
|
||||
child: Center(
|
||||
child: Text('${alarmTime.hour.toString().padLeft(2, '0')}:'
|
||||
'${alarmTime.minute.toString().padLeft(2, '0')}',
|
||||
style: TextStyle(
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary)
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(child: CustomDropdown(
|
||||
items: dateOptions.values
|
||||
.map(Text.new)
|
||||
.toList(growable: false),
|
||||
values: dateOptions.keys.toList(growable: false),
|
||||
value: dateOption,
|
||||
onChanged: (value){
|
||||
vm.setDateOption(value);
|
||||
},
|
||||
height: 50,
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
))
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DaysSection extends ConsumerWidget {
|
||||
|
||||
const _DaysSection();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
|
||||
final theme = ref.read(themePortProvider);
|
||||
|
||||
final vm = ref.read(alarmViewModelProvider.notifier);
|
||||
final dateOption = ref.watch(
|
||||
alarmViewModelProvider.select((s)=>s.dateOption)
|
||||
);
|
||||
final alarmDays = ref.watch(
|
||||
alarmViewModelProvider.select((s)=>s.alarmDays)
|
||||
);
|
||||
|
||||
final days = [
|
||||
'LUN',
|
||||
'MAR',
|
||||
'MIE',
|
||||
'JUE',
|
||||
'VIE',
|
||||
'SAB',
|
||||
'DOM',
|
||||
];
|
||||
|
||||
if (dateOption == 'SELECT') {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: List<Widget>.generate(days.length, (int index){
|
||||
return TextButton(
|
||||
onPressed: (){
|
||||
vm.toggleAlarmDay(index);
|
||||
},
|
||||
style: ButtonStyle(
|
||||
padding: WidgetStatePropertyAll(EdgeInsets.zero),
|
||||
minimumSize: WidgetStatePropertyAll(Size.zero),
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: alarmDays[index]
|
||||
? theme.getColorFor(ThemeCode.legacyPrimary)
|
||||
: theme.getColorFor(ThemeCode.backgroundPrimary),
|
||||
border: Border.fromBorderSide(
|
||||
BorderSide(color: theme.getColorFor(ThemeCode.legacyPrimary))
|
||||
),
|
||||
),
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Text(days[index],
|
||||
style: TextStyle(
|
||||
color: alarmDays[index]
|
||||
? theme.getColorFor(ThemeCode.textSecondary)
|
||||
: theme.getColorFor(ThemeCode.textPrimary),
|
||||
fontSize: 12
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
else {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class _SaveSection extends ConsumerWidget {
|
||||
|
||||
const _SaveSection();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.read(themePortProvider);
|
||||
|
||||
final vm = ref.read(alarmViewModelProvider.notifier);
|
||||
|
||||
return PrimaryButton(
|
||||
onPressed: (){
|
||||
vm.setAlarm();
|
||||
|
||||
Navigator.pop(context);
|
||||
},
|
||||
text: context.translate(I18n.save),
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
abstract class ShutdownUseCase {
|
||||
Future<void> shutdown({required String deviceId});
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import 'package:settings/src/core/domain/repositories/settings_repository.dart';
|
||||
|
||||
import 'shutdown_use_case.dart';
|
||||
|
||||
class ShutdownUseCaseImpl implements ShutdownUseCase {
|
||||
ShutdownUseCaseImpl(this._repository);
|
||||
|
||||
final SettingsRepository _repository;
|
||||
|
||||
@override
|
||||
Future<void> shutdown({required String deviceId}) async {
|
||||
return;
|
||||
// return _repository.shutdown(deviceId: deviceId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:settings/src/core/providers/settings_repository_provider.dart';
|
||||
|
||||
import '../../domain/shutdown_use_case.dart';
|
||||
import '../../domain/shutdown_use_case_impl.dart';
|
||||
|
||||
final shutdownUseCaseProvider = Provider.autoDispose<ShutdownUseCase>((ref) {
|
||||
final settingsRepository = ref.read(settingsRepositoryProvider);
|
||||
return ShutdownUseCaseImpl(settingsRepository);
|
||||
});
|
||||
@@ -0,0 +1,150 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:navigation/navigation.dart';
|
||||
import 'package:settings/src/features/remote_management/presentation/state/remote_management_view_model.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
|
||||
import 'widgets/confirm_dialog.dart';
|
||||
|
||||
class RemoteManagementScreen extends ConsumerWidget {
|
||||
|
||||
final NavigationContract navigationContract;
|
||||
|
||||
const RemoteManagementScreen({
|
||||
super.key,
|
||||
required this.navigationContract,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.read(themePortProvider);
|
||||
|
||||
return LegacyPageLayout(
|
||||
theme: theme,
|
||||
title: context.translate(I18n.remoteManagement),
|
||||
body: Column(
|
||||
children: [
|
||||
Center(child:
|
||||
Icon(Icons.settings_remote_outlined,
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
size: 180,
|
||||
)
|
||||
),
|
||||
SizedBox(height: 36),
|
||||
_OptionsSection()
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _OptionsSection extends ConsumerWidget {
|
||||
|
||||
const _OptionsSection();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final vm = ref.read(remoteManagementViewModelProvider.notifier);
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
_SectionButton(
|
||||
title: context.translate(I18n.remoteTurnOff),
|
||||
subtitle: context.translate(I18n.remoteTurnOffMessage),
|
||||
icon: Icons.settings_power_outlined,
|
||||
onPressed: () {showDialog(context: context, builder: (context)=>Dialog(
|
||||
child: ConfirmDialog(
|
||||
title: context.translate(I18n.remoteTurnOff),
|
||||
message: context.translate(I18n.remoteTurnOffConfirm),
|
||||
onConfirm: () {
|
||||
vm.turnOff();
|
||||
Navigator.pop(context);
|
||||
}
|
||||
),
|
||||
));},
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
_SectionButton(
|
||||
title: context.translate(I18n.remoteRestart),
|
||||
subtitle: context.translate(I18n.remoteRestartMessage),
|
||||
icon: Icons.refresh_outlined,
|
||||
onPressed: () {showDialog(context: context, builder: (context)=>Dialog(
|
||||
child: ConfirmDialog(
|
||||
title: context.translate(I18n.remoteRestart),
|
||||
message: context.translate(I18n.remoteRestartConfirm),
|
||||
onConfirm: () {
|
||||
vm.restart();
|
||||
Navigator.pop(context);
|
||||
}
|
||||
),
|
||||
));},
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
_SectionButton(
|
||||
title: context.translate(I18n.remoteFactoryReset),
|
||||
subtitle: context.translate(I18n.remoteFactoryResetMessage),
|
||||
icon: Icons.restart_alt_outlined,
|
||||
onPressed: () {showDialog(context: context, builder: (context)=>Dialog(
|
||||
child: ConfirmDialog(
|
||||
title: context.translate(I18n.remoteFactoryReset),
|
||||
message: context.translate(I18n.remoteFactoryResetConfirm),
|
||||
onConfirm: () {
|
||||
vm.factoryReset();
|
||||
Navigator.pop(context);
|
||||
}
|
||||
),
|
||||
));},
|
||||
),
|
||||
]
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SectionButton extends ConsumerWidget {
|
||||
|
||||
final String title;
|
||||
final String subtitle;
|
||||
final IconData icon;
|
||||
final VoidCallback onPressed;
|
||||
|
||||
const _SectionButton({
|
||||
required this.title,
|
||||
required this.subtitle,
|
||||
required this.icon,
|
||||
required this.onPressed,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.read(themePortProvider);
|
||||
|
||||
return SectionButton(
|
||||
onPressed: onPressed,
|
||||
icon: Icon(icon,
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
size: 48,
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
Text(title,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14
|
||||
)
|
||||
),
|
||||
Text(subtitle,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: theme.getColorFor(ThemeCode.textPrimary)
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:sf_shared/sf_shared.dart';
|
||||
import 'remote_management_view_state.dart';
|
||||
|
||||
final remoteManagementViewModelProvider =
|
||||
NotifierProvider.autoDispose<RemoteManagementViewModel, RemoteManagementViewState>(
|
||||
RemoteManagementViewModel.new,
|
||||
);
|
||||
|
||||
class RemoteManagementViewModel extends Notifier<RemoteManagementViewState> {
|
||||
// late final TurnOffUsecase _turnOffUseCase;
|
||||
// late final RestartUsecase _restartUseCase;
|
||||
// late final FactoryResetUseCase _factoryResetUseCase;
|
||||
|
||||
@override
|
||||
RemoteManagementViewState build() {
|
||||
// _turnOffUseCase = ref.read(turnOffUseCaseProvider);
|
||||
// _restartUseCase = ref.read(restartUseCaseProvider);
|
||||
// _factoryResetUseCase = ref.read(factoryResetUseCaseProvider);
|
||||
|
||||
Future.microtask(() => load());
|
||||
|
||||
return const RemoteManagementViewState();
|
||||
}
|
||||
|
||||
Future<void> load() async {
|
||||
final device = ref.read(selectedDeviceProvider);
|
||||
|
||||
setDevice(device!);
|
||||
}
|
||||
|
||||
void setDevice(DeviceEntity device) {
|
||||
state = state.copyWith(
|
||||
deviceId: device.identificator,
|
||||
isLoading: false,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> turnOff() async {
|
||||
try {
|
||||
state = state.copyWith(
|
||||
isLoading: true,
|
||||
);
|
||||
// _turnOffUseCase.turnOff();
|
||||
} catch (e) {
|
||||
state = state.copyWith(
|
||||
isLoading: false,
|
||||
errorMessage: e.toString()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> restart() async {
|
||||
try {
|
||||
state = state.copyWith(
|
||||
isLoading: true,
|
||||
);
|
||||
// _restartUseCase.restart();
|
||||
} catch (e) {
|
||||
state = state.copyWith(
|
||||
isLoading: false,
|
||||
errorMessage: e.toString()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> factoryReset() async {
|
||||
try {
|
||||
state = state.copyWith(
|
||||
isLoading: true,
|
||||
);
|
||||
// _factoryResetUseCase.fatoryReset();
|
||||
} catch (e) {
|
||||
state = state.copyWith(
|
||||
isLoading: false,
|
||||
errorMessage: e.toString()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:settings/src/features/alarm/domain/entities/alarm_entity.dart';
|
||||
|
||||
part 'remote_management_view_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
abstract class RemoteManagementViewState with _$RemoteManagementViewState {
|
||||
const factory RemoteManagementViewState({
|
||||
@Default('') String deviceId,
|
||||
@Default(true) bool isLoading,
|
||||
@Default('') String errorMessage,
|
||||
}) = _RemoteManagementViewState;
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'remote_management_view_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$RemoteManagementViewState {
|
||||
|
||||
String get deviceId; bool get isLoading; String get errorMessage;
|
||||
/// Create a copy of RemoteManagementViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$RemoteManagementViewStateCopyWith<RemoteManagementViewState> get copyWith => _$RemoteManagementViewStateCopyWithImpl<RemoteManagementViewState>(this as RemoteManagementViewState, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is RemoteManagementViewState&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,deviceId,isLoading,errorMessage);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'RemoteManagementViewState(deviceId: $deviceId, isLoading: $isLoading, errorMessage: $errorMessage)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $RemoteManagementViewStateCopyWith<$Res> {
|
||||
factory $RemoteManagementViewStateCopyWith(RemoteManagementViewState value, $Res Function(RemoteManagementViewState) _then) = _$RemoteManagementViewStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String deviceId, bool isLoading, String errorMessage
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$RemoteManagementViewStateCopyWithImpl<$Res>
|
||||
implements $RemoteManagementViewStateCopyWith<$Res> {
|
||||
_$RemoteManagementViewStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final RemoteManagementViewState _self;
|
||||
final $Res Function(RemoteManagementViewState) _then;
|
||||
|
||||
/// Create a copy of RemoteManagementViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? deviceId = null,Object? isLoading = null,Object? errorMessage = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
||||
as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [RemoteManagementViewState].
|
||||
extension RemoteManagementViewStatePatterns on RemoteManagementViewState {
|
||||
/// 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( _RemoteManagementViewState value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _RemoteManagementViewState() 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( _RemoteManagementViewState value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _RemoteManagementViewState():
|
||||
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( _RemoteManagementViewState value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _RemoteManagementViewState() 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 deviceId, bool isLoading, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _RemoteManagementViewState() when $default != null:
|
||||
return $default(_that.deviceId,_that.isLoading,_that.errorMessage);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String deviceId, bool isLoading, String errorMessage) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _RemoteManagementViewState():
|
||||
return $default(_that.deviceId,_that.isLoading,_that.errorMessage);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String deviceId, bool isLoading, String errorMessage)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _RemoteManagementViewState() when $default != null:
|
||||
return $default(_that.deviceId,_that.isLoading,_that.errorMessage);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _RemoteManagementViewState implements RemoteManagementViewState {
|
||||
const _RemoteManagementViewState({this.deviceId = '', this.isLoading = true, this.errorMessage = ''});
|
||||
|
||||
|
||||
@override@JsonKey() final String deviceId;
|
||||
@override@JsonKey() final bool isLoading;
|
||||
@override@JsonKey() final String errorMessage;
|
||||
|
||||
/// Create a copy of RemoteManagementViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$RemoteManagementViewStateCopyWith<_RemoteManagementViewState> get copyWith => __$RemoteManagementViewStateCopyWithImpl<_RemoteManagementViewState>(this, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _RemoteManagementViewState&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,deviceId,isLoading,errorMessage);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'RemoteManagementViewState(deviceId: $deviceId, isLoading: $isLoading, errorMessage: $errorMessage)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$RemoteManagementViewStateCopyWith<$Res> implements $RemoteManagementViewStateCopyWith<$Res> {
|
||||
factory _$RemoteManagementViewStateCopyWith(_RemoteManagementViewState value, $Res Function(_RemoteManagementViewState) _then) = __$RemoteManagementViewStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String deviceId, bool isLoading, String errorMessage
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$RemoteManagementViewStateCopyWithImpl<$Res>
|
||||
implements _$RemoteManagementViewStateCopyWith<$Res> {
|
||||
__$RemoteManagementViewStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _RemoteManagementViewState _self;
|
||||
final $Res Function(_RemoteManagementViewState) _then;
|
||||
|
||||
/// Create a copy of RemoteManagementViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? deviceId = null,Object? isLoading = null,Object? errorMessage = null,}) {
|
||||
return _then(_RemoteManagementViewState(
|
||||
deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
||||
as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
@@ -0,0 +1,59 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
|
||||
class ConfirmDialog extends ConsumerWidget {
|
||||
|
||||
final String title;
|
||||
final String message;
|
||||
final VoidCallback onConfirm;
|
||||
|
||||
const ConfirmDialog({
|
||||
required this.title,
|
||||
required this.message,
|
||||
required this.onConfirm,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.read(themePortProvider);
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
height: 180,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(title,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 16
|
||||
),
|
||||
),
|
||||
Text(message),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: PrimaryButton(
|
||||
onPressed: (){Navigator.pop(context);},
|
||||
text: context.translate(I18n.cancel),
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
)),
|
||||
SizedBox(width: 14),
|
||||
Expanded(child: PrimaryButton(
|
||||
onPressed: onConfirm,
|
||||
text: context.translate(I18n.accept),
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
)),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:navigation/navigation.dart';
|
||||
|
||||
import 'presentation/remote_management_screen.dart';
|
||||
|
||||
class RemoteManagementBuilder {
|
||||
const RemoteManagementBuilder();
|
||||
|
||||
Page<void> buildPage(BuildContext context, GoRouterState state) {
|
||||
final NavigationContract navigationContract = GetIt.I<NavigationContract>();
|
||||
|
||||
return MaterialPage<void>(
|
||||
key: state.pageKey,
|
||||
child: RemoteManagementScreen(navigationContract: navigationContract),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:navigation/navigation.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
|
||||
class SettingsScreen extends ConsumerWidget {
|
||||
|
||||
final NavigationContract navigationContract;
|
||||
|
||||
const SettingsScreen({
|
||||
super.key,
|
||||
required this.navigationContract,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.read(themePortProvider);
|
||||
|
||||
return LegacyPageLayout(
|
||||
theme: theme,
|
||||
title: context.translate(I18n.settings),
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
_AppSectionButton(
|
||||
onPressed: (){navigationContract.pushTo(AppRoutes.alarm);},
|
||||
icon: Icons.notifications_outlined,
|
||||
text: context.translate(I18n.alarm)
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
|
||||
_AppSectionButton(
|
||||
onPressed: (){},
|
||||
icon: Icons.apps_rounded,
|
||||
text: context.translate(I18n.appStore)
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
|
||||
_AppSectionButton(
|
||||
onPressed: (){},
|
||||
icon: Icons.phone_outlined,
|
||||
text: context.translate(I18n.blockPhone)
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
|
||||
_AppSectionButton(
|
||||
onPressed: (){},
|
||||
icon: Icons.check,
|
||||
text: context.translate(I18n.timezone)
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
|
||||
_AppSectionButton(
|
||||
onPressed: (){},
|
||||
icon: Icons.translate_outlined,
|
||||
text: context.translate(I18n.language)
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
|
||||
_AppSectionButton(
|
||||
onPressed: (){},
|
||||
icon: Icons.nightlight_outlined,
|
||||
text: context.translate(I18n.battery)
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
|
||||
_AppSectionButton(
|
||||
onPressed: (){navigationContract.pushTo(AppRoutes.remoteManagement);},
|
||||
icon: Icons.settings_remote_outlined,
|
||||
text: context.translate(I18n.remoteManagement)
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
|
||||
_AppSectionButton(
|
||||
onPressed: (){},
|
||||
icon: Icons.message_outlined,
|
||||
text: context.translate(I18n.legacyNotifications)
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
|
||||
_AppSectionButton(
|
||||
onPressed: (){},
|
||||
icon: Icons.sms_outlined,
|
||||
text: context.translate(I18n.smsAlert)
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
|
||||
_AppSectionButton(
|
||||
onPressed: (){navigationContract.pushTo(AppRoutes.sosContacts);},
|
||||
icon: Icons.perm_contact_calendar_outlined,
|
||||
text: context.translate(I18n.sosContacts)
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
|
||||
_AppSectionButton(
|
||||
onPressed: (){navigationContract.pushTo(AppRoutes.sound);},
|
||||
icon: Icons.volume_up_outlined,
|
||||
text: context.translate(I18n.sound)
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
|
||||
_AppSectionButton(
|
||||
onPressed: (){},
|
||||
icon: Icons.wifi_find_outlined,
|
||||
text: context.translate(I18n.wifiSettings)
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
|
||||
_AppSectionButton(
|
||||
onPressed: (){},
|
||||
icon: Icons.settings_power_outlined,
|
||||
text: context.translate(I18n.remoteOnOff)
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
|
||||
_AppSectionButton(
|
||||
onPressed: (){},
|
||||
icon: Icons.dashboard_customize_outlined,
|
||||
text: context.translate(I18n.disableFunctions)
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
|
||||
_AppSectionButton(
|
||||
onPressed: (){},
|
||||
icon: Icons.share_arrival_time_outlined,
|
||||
text: context.translate(I18n.syncClock)
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
|
||||
],
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class _AppSectionButton extends ConsumerWidget {
|
||||
|
||||
final GestureTapCallback onPressed;
|
||||
final IconData icon;
|
||||
final String text;
|
||||
|
||||
const _AppSectionButton({
|
||||
required this.onPressed,
|
||||
required this.icon,
|
||||
required this.text,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.read(themePortProvider);
|
||||
|
||||
return GestureDetector(
|
||||
onTap: onPressed,
|
||||
child: Container(
|
||||
padding: SizeUtils.getByScreen(
|
||||
small: EdgeInsets.symmetric(horizontal: 22, vertical: 14),
|
||||
big: EdgeInsets.symmetric(horizontal: 21, vertical: 12)
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(SizeUtils.getByScreen(small: 12, big: 18))),
|
||||
color: theme.getColorFor(ThemeCode.backgroundSecondary),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
||||
),
|
||||
padding: EdgeInsets.all(SizeUtils.getByScreen(small: 0, big: 0)),
|
||||
child: Icon(icon,
|
||||
size: SizeUtils.getByScreen(small: 52, big: 48),
|
||||
color: Color(0xFF588EA5),
|
||||
weight: 30,
|
||||
),
|
||||
),
|
||||
SizedBox(width: SizeUtils.getByScreen(small: 16, big: 15)),
|
||||
Expanded(
|
||||
child: Text(context.translate(text),
|
||||
style: TextStyle(
|
||||
fontSize: SizeUtils.getByScreen(small: 18, big: 19),
|
||||
fontWeight: FontWeight.w500
|
||||
)
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:navigation/navigation.dart';
|
||||
import 'package:settings/src/features/settings/presentation/settings_screen.dart';
|
||||
|
||||
class SettingsBuilder {
|
||||
const SettingsBuilder();
|
||||
|
||||
Page<void> buildPage(BuildContext context, GoRouterState state) {
|
||||
final NavigationContract navigationContract = GetIt.I<NavigationContract>();
|
||||
|
||||
return MaterialPage<void>(
|
||||
key: state.pageKey,
|
||||
child: SettingsScreen(navigationContract: navigationContract),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
abstract class SetContactsUseCase {
|
||||
Future<void> setContacts({required String deviceId, required String phone});
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import 'package:settings/src/core/domain/repositories/settings_repository.dart';
|
||||
|
||||
import 'set_contacts_use_case.dart';
|
||||
|
||||
class SetContactsUseCaseImpl implements SetContactsUseCase {
|
||||
SetContactsUseCaseImpl(this._repository);
|
||||
|
||||
final SettingsRepository _repository;
|
||||
|
||||
@override
|
||||
Future<void> setContacts({required String deviceId, required String phone}) async {
|
||||
return;
|
||||
// return _repository.setContact(deviceId: deviceId, phone: phone);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:settings/src/core/providers/settings_repository_provider.dart';
|
||||
|
||||
import '../../domain/set_contacts_use_case.dart';
|
||||
import '../../domain/set_contacts_use_case_impl.dart';
|
||||
|
||||
final setContactsUseCaseProvider = Provider.autoDispose<SetContactsUseCase>((ref) {
|
||||
final settingsRepository = ref.read(settingsRepositoryProvider);
|
||||
return SetContactsUseCaseImpl(settingsRepository);
|
||||
});
|
||||
@@ -0,0 +1,132 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:navigation/navigation.dart';
|
||||
import 'package:settings/src/features/sos_contacts/presentation/widgets/edit_phone_dialog.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
|
||||
import 'state/sos_contacts_view_model.dart';
|
||||
|
||||
class SosContactsScreen extends ConsumerWidget {
|
||||
|
||||
final NavigationContract navigationContract;
|
||||
|
||||
const SosContactsScreen({
|
||||
super.key,
|
||||
required this.navigationContract,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.read(themePortProvider);
|
||||
|
||||
return LegacyPageLayout(
|
||||
theme: theme,
|
||||
title: context.translate(I18n.sosContacts),
|
||||
body: Column(
|
||||
children: [
|
||||
_ContactsSection()
|
||||
],
|
||||
),
|
||||
footer: _SaveSection(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ContactsSection extends ConsumerWidget {
|
||||
|
||||
const _ContactsSection();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final vm = ref.read(sosContactsViewModelProvider.notifier);
|
||||
|
||||
final contacts = ref.watch(
|
||||
sosContactsViewModelProvider.select((s)=>s.contacts)
|
||||
);
|
||||
|
||||
return Column(
|
||||
children: List<Widget>.generate(contacts.length, (index) =>
|
||||
_ContactButton(
|
||||
index: index,
|
||||
phone: contacts[index]
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ContactButton extends ConsumerWidget {
|
||||
|
||||
final int index;
|
||||
final String phone;
|
||||
|
||||
const _ContactButton({
|
||||
required this.index,
|
||||
required this.phone,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.read(themePortProvider);
|
||||
|
||||
return SectionButton(
|
||||
onPressed: (){showDialog(context: context, builder: (context)=>Dialog(
|
||||
child: EditPhoneDialog(index: index, phone: phone)
|
||||
));},
|
||||
icon: Icon(Icons.perm_contact_calendar_outlined,
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
size: 36,
|
||||
),
|
||||
iconPadding: 8,
|
||||
body: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Text(context.translate(I18n.number, args: {'num': index + 1}),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14,
|
||||
color: theme.getColorFor(ThemeCode.textPrimary)
|
||||
)
|
||||
),
|
||||
Text(phone,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: theme.getColorFor(ThemeCode.textTertiary)
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
Icon(Icons.settings_phone_outlined,
|
||||
color: theme.getColorFor(ThemeCode.textTertiary),
|
||||
size: 24,
|
||||
)
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SaveSection extends ConsumerWidget {
|
||||
|
||||
const _SaveSection();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.read(themePortProvider);
|
||||
|
||||
final vm = ref.read(sosContactsViewModelProvider.notifier);
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
child: PrimaryButton(
|
||||
onPressed: vm.submit,
|
||||
text: context.translate(I18n.save),
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary)
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:sf_shared/sf_shared.dart';
|
||||
|
||||
import 'sos_contacts_view_state.dart';
|
||||
|
||||
final sosContactsViewModelProvider =
|
||||
NotifierProvider.autoDispose<SosContactsViewModel, SosContactsViewState>(
|
||||
SosContactsViewModel.new,
|
||||
);
|
||||
|
||||
class SosContactsViewModel extends Notifier<SosContactsViewState> {
|
||||
|
||||
static final RegExp _phoneRegex = RegExp(r'^\+?\d{6,15}$');
|
||||
|
||||
// late final SetContactsUseCase _setContactsUseCase;
|
||||
late final TextEditingController phoneController;
|
||||
|
||||
@override
|
||||
SosContactsViewState build() {
|
||||
// _setContactsUseCase = ref.read(setContactsUseCaseProvider);
|
||||
|
||||
phoneController = TextEditingController();
|
||||
phoneController.addListener(_onPhoneChanged);
|
||||
|
||||
Future.microtask(() => load());
|
||||
|
||||
return const SosContactsViewState();
|
||||
}
|
||||
|
||||
Future<void> load() async {
|
||||
final device = ref.read(selectedDeviceProvider);
|
||||
|
||||
setDevice(device!);
|
||||
}
|
||||
|
||||
void setDevice(DeviceEntity device) {
|
||||
state = state.copyWith(
|
||||
deviceId: device.identificator,
|
||||
isLoading: false,
|
||||
);
|
||||
}
|
||||
|
||||
void _onPhoneChanged() {
|
||||
final value = phoneController.text;
|
||||
|
||||
if (value == state.phone) return;
|
||||
|
||||
state = state.copyWith(
|
||||
phone: value,
|
||||
errorMessage: '',
|
||||
);
|
||||
}
|
||||
|
||||
void setContactPhone(int index) {
|
||||
List<String> contacts = state.contacts.toList();
|
||||
final phone = state.phone;
|
||||
|
||||
if (phone.isEmpty){
|
||||
state = state.copyWith(errorMessage: 'errorMessagePhoneIsEmpty');
|
||||
return;
|
||||
}
|
||||
if (!_phoneRegex.hasMatch(phone)) {
|
||||
state = state.copyWith(errorMessage: 'errorMessagePhoneIsInvalid');
|
||||
return;
|
||||
}
|
||||
|
||||
contacts[index] = phone;
|
||||
|
||||
phoneController.text = '';
|
||||
state = state.copyWith(
|
||||
contacts: contacts,
|
||||
phone: '',
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> submit() async {
|
||||
try {
|
||||
state = state.copyWith(
|
||||
isLoading: true,
|
||||
);
|
||||
// _setContactsUseCase.setContacts();
|
||||
} catch (e) {
|
||||
state = state.copyWith(
|
||||
isLoading: false,
|
||||
errorMessage: e.toString()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> restart() async {
|
||||
try {
|
||||
state = state.copyWith(
|
||||
isLoading: true,
|
||||
);
|
||||
// _restartUseCase.restart();
|
||||
} catch (e) {
|
||||
state = state.copyWith(
|
||||
isLoading: false,
|
||||
errorMessage: e.toString()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> factoryReset() async {
|
||||
try {
|
||||
state = state.copyWith(
|
||||
isLoading: true,
|
||||
);
|
||||
// _factoryResetUseCase.fatoryReset();
|
||||
} catch (e) {
|
||||
state = state.copyWith(
|
||||
isLoading: false,
|
||||
errorMessage: e.toString()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:settings/src/features/alarm/domain/entities/alarm_entity.dart';
|
||||
|
||||
part 'sos_contacts_view_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
abstract class SosContactsViewState with _$SosContactsViewState {
|
||||
const factory SosContactsViewState({
|
||||
@Default('') String deviceId,
|
||||
@Default(['', '', '']) List<String> contacts,
|
||||
@Default('') String phone,
|
||||
@Default(true) bool isLoading,
|
||||
@Default('') String errorMessage,
|
||||
}) = _SosContactsViewState;
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
// 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 'sos_contacts_view_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$SosContactsViewState {
|
||||
|
||||
String get deviceId; List<String> get contacts; String get phone; bool get isLoading; String get errorMessage;
|
||||
/// Create a copy of SosContactsViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$SosContactsViewStateCopyWith<SosContactsViewState> get copyWith => _$SosContactsViewStateCopyWithImpl<SosContactsViewState>(this as SosContactsViewState, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is SosContactsViewState&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&const DeepCollectionEquality().equals(other.contacts, contacts)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,deviceId,const DeepCollectionEquality().hash(contacts),phone,isLoading,errorMessage);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SosContactsViewState(deviceId: $deviceId, contacts: $contacts, phone: $phone, isLoading: $isLoading, errorMessage: $errorMessage)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $SosContactsViewStateCopyWith<$Res> {
|
||||
factory $SosContactsViewStateCopyWith(SosContactsViewState value, $Res Function(SosContactsViewState) _then) = _$SosContactsViewStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String deviceId, List<String> contacts, String phone, bool isLoading, String errorMessage
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$SosContactsViewStateCopyWithImpl<$Res>
|
||||
implements $SosContactsViewStateCopyWith<$Res> {
|
||||
_$SosContactsViewStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final SosContactsViewState _self;
|
||||
final $Res Function(SosContactsViewState) _then;
|
||||
|
||||
/// Create a copy of SosContactsViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? deviceId = null,Object? contacts = null,Object? phone = null,Object? isLoading = null,Object? errorMessage = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
||||
as String,contacts: null == contacts ? _self.contacts : contacts // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
|
||||
as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [SosContactsViewState].
|
||||
extension SosContactsViewStatePatterns on SosContactsViewState {
|
||||
/// 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( _SosContactsViewState value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _SosContactsViewState() 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( _SosContactsViewState value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _SosContactsViewState():
|
||||
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( _SosContactsViewState value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _SosContactsViewState() 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 deviceId, List<String> contacts, String phone, bool isLoading, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _SosContactsViewState() when $default != null:
|
||||
return $default(_that.deviceId,_that.contacts,_that.phone,_that.isLoading,_that.errorMessage);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String deviceId, List<String> contacts, String phone, bool isLoading, String errorMessage) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _SosContactsViewState():
|
||||
return $default(_that.deviceId,_that.contacts,_that.phone,_that.isLoading,_that.errorMessage);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String deviceId, List<String> contacts, String phone, bool isLoading, String errorMessage)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _SosContactsViewState() when $default != null:
|
||||
return $default(_that.deviceId,_that.contacts,_that.phone,_that.isLoading,_that.errorMessage);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _SosContactsViewState implements SosContactsViewState {
|
||||
const _SosContactsViewState({this.deviceId = '', final List<String> contacts = const ['', '', ''], this.phone = '', this.isLoading = true, this.errorMessage = ''}): _contacts = contacts;
|
||||
|
||||
|
||||
@override@JsonKey() final String deviceId;
|
||||
final List<String> _contacts;
|
||||
@override@JsonKey() List<String> get contacts {
|
||||
if (_contacts is EqualUnmodifiableListView) return _contacts;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_contacts);
|
||||
}
|
||||
|
||||
@override@JsonKey() final String phone;
|
||||
@override@JsonKey() final bool isLoading;
|
||||
@override@JsonKey() final String errorMessage;
|
||||
|
||||
/// Create a copy of SosContactsViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$SosContactsViewStateCopyWith<_SosContactsViewState> get copyWith => __$SosContactsViewStateCopyWithImpl<_SosContactsViewState>(this, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SosContactsViewState&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&const DeepCollectionEquality().equals(other._contacts, _contacts)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,deviceId,const DeepCollectionEquality().hash(_contacts),phone,isLoading,errorMessage);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SosContactsViewState(deviceId: $deviceId, contacts: $contacts, phone: $phone, isLoading: $isLoading, errorMessage: $errorMessage)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$SosContactsViewStateCopyWith<$Res> implements $SosContactsViewStateCopyWith<$Res> {
|
||||
factory _$SosContactsViewStateCopyWith(_SosContactsViewState value, $Res Function(_SosContactsViewState) _then) = __$SosContactsViewStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String deviceId, List<String> contacts, String phone, bool isLoading, String errorMessage
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$SosContactsViewStateCopyWithImpl<$Res>
|
||||
implements _$SosContactsViewStateCopyWith<$Res> {
|
||||
__$SosContactsViewStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _SosContactsViewState _self;
|
||||
final $Res Function(_SosContactsViewState) _then;
|
||||
|
||||
/// Create a copy of SosContactsViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? deviceId = null,Object? contacts = null,Object? phone = null,Object? isLoading = null,Object? errorMessage = null,}) {
|
||||
return _then(_SosContactsViewState(
|
||||
deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
||||
as String,contacts: null == contacts ? _self._contacts : contacts // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
|
||||
as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
@@ -0,0 +1,92 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:settings/src/features/sos_contacts/presentation/state/sos_contacts_view_model.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
|
||||
class EditPhoneDialog extends ConsumerWidget {
|
||||
|
||||
final int index;
|
||||
final String phone;
|
||||
|
||||
const EditPhoneDialog({
|
||||
required this.index,
|
||||
required this.phone,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.read(themePortProvider);
|
||||
|
||||
final vm = ref.read(sosContactsViewModelProvider.notifier);
|
||||
final errorMessage = ref.watch(
|
||||
sosContactsViewModelProvider.select((s)=>s.errorMessage)
|
||||
);
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
height: 220,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Stack(
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: IconButton(
|
||||
onPressed: (){Navigator.pop(context);},
|
||||
icon: Icon(Icons.close,
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary)
|
||||
)
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: Text(
|
||||
context.translate(I18n.number, args: {'num': index + 1}).toUpperCase(),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 16,
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
CustomTextField(
|
||||
controller: vm.phoneController,
|
||||
keyboardType: TextInputType.phone,
|
||||
hint: context.translate(I18n.insertPhone),
|
||||
),
|
||||
if (errorMessage.isNotEmpty) ...[
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
errorMessage,
|
||||
style: const TextStyle(color: Colors.red, fontSize: 13),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
PrimaryButton(
|
||||
onPressed: (){
|
||||
vm.setContactPhone(index);
|
||||
|
||||
final errorMessage = ref.read(
|
||||
sosContactsViewModelProvider.select((s)=>s.errorMessage)
|
||||
);
|
||||
|
||||
if (errorMessage.isEmpty){
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
text: context.translate(I18n.save),
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary)
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:navigation/navigation.dart';
|
||||
|
||||
import 'presentation/sos_contacts_screen.dart';
|
||||
|
||||
class SosContactsBuilder {
|
||||
const SosContactsBuilder();
|
||||
|
||||
Page<void> buildPage(BuildContext context, GoRouterState state) {
|
||||
final NavigationContract navigationContract = GetIt.I<NavigationContract>();
|
||||
|
||||
return MaterialPage<void>(
|
||||
key: state.pageKey,
|
||||
child: SosContactsScreen(navigationContract: navigationContract),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
abstract class SetSoundUseCase {
|
||||
Future<void> setSound({required String deviceId});
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import 'package:settings/src/core/domain/repositories/settings_repository.dart';
|
||||
|
||||
import 'set_sound_use_case.dart';
|
||||
|
||||
|
||||
class SetSoundUseCaseImpl implements SetSoundUseCase {
|
||||
SetSoundUseCaseImpl(this._repository);
|
||||
|
||||
final SettingsRepository _repository;
|
||||
|
||||
@override
|
||||
Future<void> setSound({required String deviceId}) async {
|
||||
return;
|
||||
// return _repository.setSound(deviceId: deviceId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:settings/src/core/providers/settings_repository_provider.dart';
|
||||
|
||||
import '../../domain/set_sound_use_case.dart';
|
||||
import '../../domain/set_sound_use_case_impl.dart';
|
||||
|
||||
final setSoundUseCaseProvider = Provider.autoDispose<SetSoundUseCase>((ref) {
|
||||
final settingsRepository = ref.read(settingsRepositoryProvider);
|
||||
return SetSoundUseCaseImpl(settingsRepository);
|
||||
});
|
||||
@@ -0,0 +1,162 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:navigation/navigation.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
|
||||
import 'state/sound_view_model.dart';
|
||||
|
||||
class SoundScreen extends ConsumerWidget {
|
||||
|
||||
final NavigationContract navigationContract;
|
||||
|
||||
const SoundScreen({
|
||||
super.key,
|
||||
required this.navigationContract,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.read(themePortProvider);
|
||||
|
||||
return LegacyPageLayout(
|
||||
theme: theme,
|
||||
title: context.translate(I18n.sound),
|
||||
body: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 18, vertical: 12),
|
||||
child: Column(
|
||||
children: [
|
||||
Center(child:
|
||||
Icon(Icons.volume_up,
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
size: 180,
|
||||
)
|
||||
),
|
||||
SizedBox(height: 36),
|
||||
const _OptionsSection()
|
||||
],
|
||||
),
|
||||
),
|
||||
footer: const _SaveSection(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _OptionsSection extends ConsumerWidget {
|
||||
|
||||
const _OptionsSection();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final vm = ref.read(soundViewModelProvider.notifier);
|
||||
|
||||
final soundOption = ref.watch(
|
||||
soundViewModelProvider.select((s)=>s.soundOption)
|
||||
);
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
_SectionButton(
|
||||
title: context.translate(I18n.soundAndVibration),
|
||||
icon: Icons.volume_up_outlined,
|
||||
active: soundOption == 'SOUND_AND_VIBRATION',
|
||||
onPressed: () {vm.setSoundOption('SOUND_AND_VIBRATION');},
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
_SectionButton(
|
||||
title: context.translate(I18n.soundOnly),
|
||||
icon: Icons.volume_up_outlined,
|
||||
active: soundOption == 'SOUND',
|
||||
onPressed: () {vm.setSoundOption('SOUND');},
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
_SectionButton(
|
||||
title: context.translate(I18n.vibrationOnly),
|
||||
icon: Icons.vibration_outlined,
|
||||
active: soundOption == 'VIBRATION',
|
||||
onPressed: () {vm.setSoundOption('VIBRATION');},
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
_SectionButton(
|
||||
title: context.translate(I18n.silent),
|
||||
icon: Icons.volume_mute_outlined,
|
||||
active: soundOption == 'SILENT',
|
||||
onPressed: () {vm.setSoundOption('SILENT');},
|
||||
),
|
||||
]
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SectionButton extends ConsumerWidget {
|
||||
|
||||
final String title;
|
||||
final bool active;
|
||||
final IconData icon;
|
||||
final VoidCallback onPressed;
|
||||
|
||||
const _SectionButton({
|
||||
required this.title,
|
||||
required this.active,
|
||||
required this.icon,
|
||||
required this.onPressed,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.read(themePortProvider);
|
||||
|
||||
return SectionButton(
|
||||
onPressed: onPressed,
|
||||
icon: Icon(icon,
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
size: 36,
|
||||
),
|
||||
iconPadding: 8,
|
||||
body: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(title,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14,
|
||||
color: active
|
||||
? theme.getColorFor(ThemeCode.textPrimary)
|
||||
: theme.getColorFor(ThemeCode.textTertiary),
|
||||
)
|
||||
),
|
||||
Switch(
|
||||
value: active,
|
||||
onChanged: (_){onPressed();},
|
||||
)
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class _SaveSection extends ConsumerWidget {
|
||||
|
||||
const _SaveSection();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.read(themePortProvider);
|
||||
|
||||
final vm = ref.read(soundViewModelProvider.notifier);
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
child: PrimaryButton(
|
||||
onPressed: vm.submit,
|
||||
text: context.translate(I18n.save),
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:sf_shared/sf_shared.dart';
|
||||
|
||||
import '../../domain/set_sound_use_case.dart';
|
||||
import '../providers/set_sound_use_case_provider.dart';
|
||||
import 'sound_view_state.dart';
|
||||
|
||||
final soundViewModelProvider =
|
||||
NotifierProvider.autoDispose<SoundViewModel, SoundViewState>(
|
||||
SoundViewModel.new,
|
||||
);
|
||||
|
||||
class SoundViewModel extends Notifier<SoundViewState> {
|
||||
late final SetSoundUseCase _setSoundUseCase;
|
||||
|
||||
@override
|
||||
SoundViewState build() {
|
||||
_setSoundUseCase = ref.read(setSoundUseCaseProvider);
|
||||
|
||||
Future.microtask(() => load());
|
||||
|
||||
return const SoundViewState();
|
||||
}
|
||||
|
||||
Future<void> load() async {
|
||||
final device = ref.read(selectedDeviceProvider);
|
||||
setDevice(device!);
|
||||
state = state.copyWith(
|
||||
soundOption: 'SOUND_AND_VIBRATION',
|
||||
isLoading: false,
|
||||
);
|
||||
}
|
||||
|
||||
void setDevice(DeviceEntity device) {
|
||||
state = state.copyWith(
|
||||
deviceId: device.identificator,
|
||||
);
|
||||
}
|
||||
|
||||
void setSoundOption(String value) {
|
||||
if (state.soundOption == value) return;
|
||||
|
||||
state = state.copyWith(
|
||||
soundOption: value
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> submit() async {
|
||||
try {
|
||||
state = state.copyWith(
|
||||
isLoading: true,
|
||||
);
|
||||
_setSoundUseCase.setSound(deviceId: state.deviceId);
|
||||
} catch (e) {
|
||||
state = state.copyWith(
|
||||
isLoading: false,
|
||||
errorMessage: e.toString()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'sound_view_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
abstract class SoundViewState with _$SoundViewState {
|
||||
const factory SoundViewState({
|
||||
@Default('') String deviceId,
|
||||
String? soundOption,
|
||||
@Default(true) bool isLoading,
|
||||
@Default('') String errorMessage,
|
||||
}) = _SoundViewState;
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'sound_view_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$SoundViewState {
|
||||
|
||||
String get deviceId; String? get soundOption; bool get isLoading; String get errorMessage;
|
||||
/// Create a copy of SoundViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$SoundViewStateCopyWith<SoundViewState> get copyWith => _$SoundViewStateCopyWithImpl<SoundViewState>(this as SoundViewState, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is SoundViewState&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.soundOption, soundOption) || other.soundOption == soundOption)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,deviceId,soundOption,isLoading,errorMessage);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SoundViewState(deviceId: $deviceId, soundOption: $soundOption, isLoading: $isLoading, errorMessage: $errorMessage)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $SoundViewStateCopyWith<$Res> {
|
||||
factory $SoundViewStateCopyWith(SoundViewState value, $Res Function(SoundViewState) _then) = _$SoundViewStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String deviceId, String? soundOption, bool isLoading, String errorMessage
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$SoundViewStateCopyWithImpl<$Res>
|
||||
implements $SoundViewStateCopyWith<$Res> {
|
||||
_$SoundViewStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final SoundViewState _self;
|
||||
final $Res Function(SoundViewState) _then;
|
||||
|
||||
/// Create a copy of SoundViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? deviceId = null,Object? soundOption = freezed,Object? isLoading = null,Object? errorMessage = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
||||
as String,soundOption: freezed == soundOption ? _self.soundOption : soundOption // ignore: cast_nullable_to_non_nullable
|
||||
as String?,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [SoundViewState].
|
||||
extension SoundViewStatePatterns on SoundViewState {
|
||||
/// 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( _SoundViewState value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _SoundViewState() 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( _SoundViewState value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _SoundViewState():
|
||||
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( _SoundViewState value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _SoundViewState() 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 deviceId, String? soundOption, bool isLoading, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _SoundViewState() when $default != null:
|
||||
return $default(_that.deviceId,_that.soundOption,_that.isLoading,_that.errorMessage);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String deviceId, String? soundOption, bool isLoading, String errorMessage) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _SoundViewState():
|
||||
return $default(_that.deviceId,_that.soundOption,_that.isLoading,_that.errorMessage);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String deviceId, String? soundOption, bool isLoading, String errorMessage)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _SoundViewState() when $default != null:
|
||||
return $default(_that.deviceId,_that.soundOption,_that.isLoading,_that.errorMessage);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _SoundViewState implements SoundViewState {
|
||||
const _SoundViewState({this.deviceId = '', this.soundOption, this.isLoading = true, this.errorMessage = ''});
|
||||
|
||||
|
||||
@override@JsonKey() final String deviceId;
|
||||
@override final String? soundOption;
|
||||
@override@JsonKey() final bool isLoading;
|
||||
@override@JsonKey() final String errorMessage;
|
||||
|
||||
/// Create a copy of SoundViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$SoundViewStateCopyWith<_SoundViewState> get copyWith => __$SoundViewStateCopyWithImpl<_SoundViewState>(this, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SoundViewState&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.soundOption, soundOption) || other.soundOption == soundOption)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,deviceId,soundOption,isLoading,errorMessage);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SoundViewState(deviceId: $deviceId, soundOption: $soundOption, isLoading: $isLoading, errorMessage: $errorMessage)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$SoundViewStateCopyWith<$Res> implements $SoundViewStateCopyWith<$Res> {
|
||||
factory _$SoundViewStateCopyWith(_SoundViewState value, $Res Function(_SoundViewState) _then) = __$SoundViewStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String deviceId, String? soundOption, bool isLoading, String errorMessage
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$SoundViewStateCopyWithImpl<$Res>
|
||||
implements _$SoundViewStateCopyWith<$Res> {
|
||||
__$SoundViewStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _SoundViewState _self;
|
||||
final $Res Function(_SoundViewState) _then;
|
||||
|
||||
/// Create a copy of SoundViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? deviceId = null,Object? soundOption = freezed,Object? isLoading = null,Object? errorMessage = null,}) {
|
||||
return _then(_SoundViewState(
|
||||
deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
||||
as String,soundOption: freezed == soundOption ? _self.soundOption : soundOption // ignore: cast_nullable_to_non_nullable
|
||||
as String?,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:navigation/navigation.dart';
|
||||
|
||||
import 'presentation/sound_screen.dart';
|
||||
|
||||
class SoundBuilder {
|
||||
const SoundBuilder();
|
||||
|
||||
Page<void> buildPage(BuildContext context, GoRouterState state) {
|
||||
final NavigationContract navigationContract = GetIt.I<NavigationContract>();
|
||||
|
||||
return MaterialPage<void>(
|
||||
key: state.pageKey,
|
||||
child: SoundScreen(navigationContract: navigationContract),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
abstract class SyncClockUseCase {
|
||||
Future<void> syncClock({required String deviceId});
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import 'package:settings/src/core/domain/repositories/settings_repository.dart';
|
||||
|
||||
import 'sync_clock_use_case.dart';
|
||||
|
||||
class SyncClockUseCaseImpl implements SyncClockUseCase {
|
||||
|
||||
SyncClockUseCaseImpl(this._repository);
|
||||
|
||||
final SettingsRepository _repository;
|
||||
|
||||
@override
|
||||
Future<void> syncClock({required String deviceId}) async {
|
||||
return;
|
||||
// return _repository.syncClock(deviceId: deviceId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:settings/src/core/providers/settings_repository_provider.dart';
|
||||
|
||||
import '../../domain/sync_clock_use_case.dart';
|
||||
import '../../domain/sync_clock_use_case_impl.dart';
|
||||
|
||||
final syncClockUseCaseProvider = Provider.autoDispose<SyncClockUseCase>((ref) {
|
||||
final settingsRepository = ref.read(settingsRepositoryProvider);
|
||||
return SyncClockUseCaseImpl(settingsRepository);
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:sf_shared/sf_shared.dart';
|
||||
|
||||
import '../../domain/sync_clock_use_case.dart';
|
||||
import '../providers/set_sound_use_case_provider.dart';
|
||||
import 'sync_clock_view_state.dart';
|
||||
|
||||
final syncClockViewModelProvider =
|
||||
NotifierProvider.autoDispose<SyncClockViewModel, SyncClockViewState>(
|
||||
SyncClockViewModel.new,
|
||||
);
|
||||
|
||||
class SyncClockViewModel extends Notifier<SyncClockViewState> {
|
||||
late final SyncClockUseCase _syncClockUseCase;
|
||||
|
||||
@override
|
||||
SyncClockViewState build() {
|
||||
_syncClockUseCase = ref.read(syncClockUseCaseProvider);
|
||||
|
||||
Future.microtask(() => load());
|
||||
|
||||
return const SyncClockViewState();
|
||||
}
|
||||
|
||||
Future<void> load() async {
|
||||
final device = ref.read(selectedDeviceProvider);
|
||||
setDevice(device!);
|
||||
}
|
||||
|
||||
void setDevice(DeviceEntity device) {
|
||||
state = state.copyWith(
|
||||
deviceId: device.identificator,
|
||||
isLoading: false,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> syncClock() async {
|
||||
if (state.isLoading) return;
|
||||
|
||||
try {
|
||||
state = state.copyWith(
|
||||
isLoading: true,
|
||||
);
|
||||
_syncClockUseCase.syncClock(deviceId: state.deviceId);
|
||||
} catch (e) {
|
||||
state = state.copyWith(
|
||||
isLoading: false,
|
||||
errorMessage: e.toString()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'sync_clock_view_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
abstract class SyncClockViewState with _$SyncClockViewState {
|
||||
const factory SyncClockViewState({
|
||||
@Default('') String deviceId,
|
||||
@Default(true) bool isLoading,
|
||||
@Default('') String errorMessage,
|
||||
}) = _SyncClockViewState;
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'sync_clock_view_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$SyncClockViewState {
|
||||
|
||||
String get deviceId; bool get isLoading; String get errorMessage;
|
||||
/// Create a copy of SyncClockViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$SyncClockViewStateCopyWith<SyncClockViewState> get copyWith => _$SyncClockViewStateCopyWithImpl<SyncClockViewState>(this as SyncClockViewState, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is SyncClockViewState&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,deviceId,isLoading,errorMessage);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SyncClockViewState(deviceId: $deviceId, isLoading: $isLoading, errorMessage: $errorMessage)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $SyncClockViewStateCopyWith<$Res> {
|
||||
factory $SyncClockViewStateCopyWith(SyncClockViewState value, $Res Function(SyncClockViewState) _then) = _$SyncClockViewStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String deviceId, bool isLoading, String errorMessage
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$SyncClockViewStateCopyWithImpl<$Res>
|
||||
implements $SyncClockViewStateCopyWith<$Res> {
|
||||
_$SyncClockViewStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final SyncClockViewState _self;
|
||||
final $Res Function(SyncClockViewState) _then;
|
||||
|
||||
/// Create a copy of SyncClockViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? deviceId = null,Object? isLoading = null,Object? errorMessage = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
||||
as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [SyncClockViewState].
|
||||
extension SyncClockViewStatePatterns on SyncClockViewState {
|
||||
/// 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( _SyncClockViewState value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _SyncClockViewState() 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( _SyncClockViewState value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _SyncClockViewState():
|
||||
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( _SyncClockViewState value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _SyncClockViewState() 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 deviceId, bool isLoading, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _SyncClockViewState() when $default != null:
|
||||
return $default(_that.deviceId,_that.isLoading,_that.errorMessage);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String deviceId, bool isLoading, String errorMessage) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _SyncClockViewState():
|
||||
return $default(_that.deviceId,_that.isLoading,_that.errorMessage);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String deviceId, bool isLoading, String errorMessage)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _SyncClockViewState() when $default != null:
|
||||
return $default(_that.deviceId,_that.isLoading,_that.errorMessage);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _SyncClockViewState implements SyncClockViewState {
|
||||
const _SyncClockViewState({this.deviceId = '', this.isLoading = true, this.errorMessage = ''});
|
||||
|
||||
|
||||
@override@JsonKey() final String deviceId;
|
||||
@override@JsonKey() final bool isLoading;
|
||||
@override@JsonKey() final String errorMessage;
|
||||
|
||||
/// Create a copy of SyncClockViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$SyncClockViewStateCopyWith<_SyncClockViewState> get copyWith => __$SyncClockViewStateCopyWithImpl<_SyncClockViewState>(this, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SyncClockViewState&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,deviceId,isLoading,errorMessage);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SyncClockViewState(deviceId: $deviceId, isLoading: $isLoading, errorMessage: $errorMessage)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$SyncClockViewStateCopyWith<$Res> implements $SyncClockViewStateCopyWith<$Res> {
|
||||
factory _$SyncClockViewStateCopyWith(_SyncClockViewState value, $Res Function(_SyncClockViewState) _then) = __$SyncClockViewStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String deviceId, bool isLoading, String errorMessage
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$SyncClockViewStateCopyWithImpl<$Res>
|
||||
implements _$SyncClockViewStateCopyWith<$Res> {
|
||||
__$SyncClockViewStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _SyncClockViewState _self;
|
||||
final $Res Function(_SyncClockViewState) _then;
|
||||
|
||||
/// Create a copy of SyncClockViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? deviceId = null,Object? isLoading = null,Object? errorMessage = null,}) {
|
||||
return _then(_SyncClockViewState(
|
||||
deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
||||
as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
@@ -0,0 +1,156 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:navigation/navigation.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
|
||||
import '../../sound/presentation/state/sound_view_model.dart';
|
||||
|
||||
class SyncClockScreen extends ConsumerWidget {
|
||||
|
||||
final NavigationContract navigationContract;
|
||||
|
||||
const SyncClockScreen({
|
||||
super.key,
|
||||
required this.navigationContract,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.read(themePortProvider);
|
||||
|
||||
return LegacyPageLayout(
|
||||
theme: theme,
|
||||
title: context.translate(I18n.syncClock),
|
||||
body: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 18, vertical: 12),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(context.translate(I18n.syncClockMessage)),
|
||||
SizedBox(height: 36),
|
||||
],
|
||||
),
|
||||
),
|
||||
footer: const _SaveSection(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _OptionsSection extends ConsumerWidget {
|
||||
|
||||
const _OptionsSection();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final vm = ref.read(soundViewModelProvider.notifier);
|
||||
|
||||
final soundOption = ref.watch(
|
||||
soundViewModelProvider.select((s)=>s.soundOption)
|
||||
);
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
_SectionButton(
|
||||
title: context.translate(I18n.soundAndVibration),
|
||||
icon: Icons.volume_up_outlined,
|
||||
active: soundOption == 'SOUND_AND_VIBRATION',
|
||||
onPressed: () {vm.setSoundOption('SOUND_AND_VIBRATION');},
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
_SectionButton(
|
||||
title: context.translate(I18n.soundOnly),
|
||||
icon: Icons.volume_up_outlined,
|
||||
active: soundOption == 'SOUND',
|
||||
onPressed: () {vm.setSoundOption('SOUND');},
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
_SectionButton(
|
||||
title: context.translate(I18n.vibrationOnly),
|
||||
icon: Icons.vibration_outlined,
|
||||
active: soundOption == 'VIBRATION',
|
||||
onPressed: () {vm.setSoundOption('VIBRATION');},
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
_SectionButton(
|
||||
title: context.translate(I18n.silent),
|
||||
icon: Icons.volume_mute_outlined,
|
||||
active: soundOption == 'SILENT',
|
||||
onPressed: () {vm.setSoundOption('SILENT');},
|
||||
),
|
||||
]
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SectionButton extends ConsumerWidget {
|
||||
|
||||
final String title;
|
||||
final bool active;
|
||||
final IconData icon;
|
||||
final VoidCallback onPressed;
|
||||
|
||||
const _SectionButton({
|
||||
required this.title,
|
||||
required this.active,
|
||||
required this.icon,
|
||||
required this.onPressed,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.read(themePortProvider);
|
||||
|
||||
return SectionButton(
|
||||
onPressed: onPressed,
|
||||
icon: Icon(icon,
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
size: 36,
|
||||
),
|
||||
iconPadding: 8,
|
||||
body: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(title,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14,
|
||||
color: active
|
||||
? theme.getColorFor(ThemeCode.textPrimary)
|
||||
: theme.getColorFor(ThemeCode.textTertiary),
|
||||
)
|
||||
),
|
||||
Switch(
|
||||
value: active,
|
||||
onChanged: (_){onPressed();},
|
||||
)
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class _SaveSection extends ConsumerWidget {
|
||||
|
||||
const _SaveSection();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.read(themePortProvider);
|
||||
|
||||
final vm = ref.read(soundViewModelProvider.notifier);
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
child: PrimaryButton(
|
||||
onPressed: vm.submit,
|
||||
text: context.translate(I18n.save),
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:navigation/navigation.dart';
|
||||
|
||||
import 'presentation/sync_clock_screen.dart';
|
||||
|
||||
class SyncClockBuilder {
|
||||
const SyncClockBuilder();
|
||||
|
||||
Page<void> buildPage(BuildContext context, GoRouterState state) {
|
||||
final NavigationContract navigationContract = GetIt.I<NavigationContract>();
|
||||
|
||||
return MaterialPage<void>(
|
||||
key: state.pageKey,
|
||||
child: SyncClockScreen(navigationContract: navigationContract),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -66,4 +66,21 @@ class AppRoutes {
|
||||
static const linkedDevices = '$accountSettings/linked_devices';
|
||||
static const appUsers = '$accountSettings/app_users';
|
||||
static const deleteAccount = '$accountSettings/delete_account';
|
||||
|
||||
static const settings = '$controlPanel/settings';
|
||||
static const alarm = '$settings/alarm';
|
||||
static const appStore = '$settings/app_store';
|
||||
static const battery = '$settings/battery';
|
||||
static const blockPhone = '$settings/block_phone';
|
||||
static const disableFunctions = '$settings/disable_functions';
|
||||
static const language = '$settings/language';
|
||||
static const legacyNotifications = '$settings/legacy_notifications';
|
||||
static const remoteManagement = '$settings/remote_management';
|
||||
static const remoteOnOff = '$settings/remote_on_off';
|
||||
static const smsAlert = '$settings/sms_alert';
|
||||
static const sosContacts = '$settings/sos_agenda';
|
||||
static const sound = '$settings/sound';
|
||||
static const syncClock = '$settings/sync_clock';
|
||||
static const timezone = '$settings/timezone';
|
||||
static const wifiSettings = '$settings/wifi_settings';
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ class QuestiaApi {
|
||||
ProgressCallback? onSendProgress,
|
||||
ProgressCallback? onReceiveProgress,
|
||||
}) {
|
||||
print('URI:${String.fromEnvironment('env')}');
|
||||
return _dio.post<T>(
|
||||
path,
|
||||
data: data,
|
||||
|
||||
@@ -550,5 +550,23 @@
|
||||
"ratherNotSay": "I'd rather not say",
|
||||
"personalDataMessage": "*This is the app user's personal data",
|
||||
"newContact": "New contact",
|
||||
"deleteContactMessage": "Are you sure you want to delete this phone from the list?"
|
||||
"deleteContactMessage": "Are you sure you want to delete this phone from the list?",
|
||||
"remoteListening": "Remote Listening",
|
||||
"alarmSettings": "Alarm settings",
|
||||
"setDateTime": "Set time and days:",
|
||||
"addAlarm": "Add new alarm",
|
||||
"alarmsMessage": "*Set up to 3 alarms",
|
||||
"once": "Once",
|
||||
"daily": "Daily",
|
||||
"selectDay": "Select days",
|
||||
"remoteTurnOff": "Apagado Remoto",
|
||||
"remoteTurnOffMessage": "Remote Shutdown",
|
||||
"remoteTurnOffConfirm": "Remotely shut down the device.",
|
||||
"remoteRestart": "Are you sure you want to remotely shut down the device?",
|
||||
"remoteRestartMessage": "Remote Restart",
|
||||
"remoteRestartConfirm": "Remotely restart the device.",
|
||||
"remoteFactoryReset": "Are you sure you want to remotely restart the device?",
|
||||
"remoteFactoryResetMessage": "Restore Default Settings",
|
||||
"remoteFactoryResetConfirm": "Restore your device to factory settings?",
|
||||
"number": "Number {num}:"
|
||||
}
|
||||
@@ -548,5 +548,54 @@
|
||||
"ratherNotSay": "Prefiero no decirlo",
|
||||
"personalDataMessage": "*Estos son los datos personales del usuario de la aplicación",
|
||||
"newContact": "Nuevo contacto",
|
||||
"deleteContactMessage": "¿Estás seguro de que deseas eliminar este número de la lista?"
|
||||
"deleteContactMessage": "¿Estás seguro de que deseas eliminar este número de la lista?",
|
||||
"settings": "Ajustes",
|
||||
"alarm": "Alarmas",
|
||||
"appStore": "App Store",
|
||||
"battery": "Ahorro Nocturno de Batería",
|
||||
"blockPhone": "Bloqueo de números",
|
||||
"disableFunctions": "Deshabilitar Funciones",
|
||||
"language": "Idioma",
|
||||
"legacyNotifications": "Notificaciones",
|
||||
"remoteManagement": "Programación remota",
|
||||
"remoteOnOff": "Encendido y Apagado Programado",
|
||||
"smsAlert": "Alertas SMS",
|
||||
"sosContacts": "Agenda SOS",
|
||||
"sound": "Sonidos",
|
||||
"syncClock": "Sincronización de tiempo",
|
||||
"timezone": "Cambio de horario y zona",
|
||||
"wifiSettings": "Configuración WiFi",
|
||||
"alarmSettings": "Ajustes de alarma",
|
||||
"setDateTime": "Establece la hora y días:",
|
||||
"addAlarm": "Agregar nueva alarma",
|
||||
"alarmsMessage": "*Configura hasta 3 alarmas",
|
||||
"once": "Una vez",
|
||||
"daily": "Diario",
|
||||
"selectDay": "Selección de días",
|
||||
"remoteTurnOff": "Apagado Remoto",
|
||||
"remoteTurnOffMessage": "Apaga el dispositivo de forma remota",
|
||||
"remoteTurnOffConfirm": "¿Seguro que deseas apagar el dispositivo en remoto?",
|
||||
"remoteRestart": "Reinicio Remoto",
|
||||
"remoteRestartMessage": "Reinicia el dispositivo de forma remota",
|
||||
"remoteRestartConfirm": "¿Seguro que deseas reiniciar el dispositivo en remoto?",
|
||||
"remoteFactoryReset": "Restaurar Configuración Predeterminada",
|
||||
"remoteFactoryResetMessage": "Restaura tu dispositivo a la configuración de fábrica",
|
||||
"remoteFactoryResetConfirm": "¿Seguro que quieres restablecer este dispositivo a la configuración de fábrica?",
|
||||
"number": "Número {num}:",
|
||||
"settings": "Ajustes",
|
||||
"alarm": "Alarmas",
|
||||
"appStore": "App Store",
|
||||
"blockPhone": "Bloqueo de números",
|
||||
"timezone": "Cambio de horario y zona",
|
||||
"language": "Idioma",
|
||||
"battery": "Ahorro nocturno de batería",
|
||||
"remoteManagement": "programación remota",
|
||||
"legacyNotifications": "notificaciones",
|
||||
"smsAlert": "Alertas SMS",
|
||||
"sosContacts": "Agenda SOS",
|
||||
"sound": "Sonidos",
|
||||
"wifiSettings": "Cofiguración WiFi",
|
||||
"remoteOnOff": "Encendido y apagado programado",
|
||||
"disableFunctions": "Deshabilitar funciones",
|
||||
"syncClock": "Sincronización de tiempo"
|
||||
}
|
||||
@@ -669,4 +669,42 @@ class I18n {
|
||||
static const String personalDataMessage = 'personalDataMessage';
|
||||
static const String newContact = 'newContact';
|
||||
static const String deleteContactMessage = 'deleteContactMessage';
|
||||
static const String alarmSettings = 'alarmSettings';
|
||||
static const String setDateTime = 'setDateTime';
|
||||
static const String addAlarm = 'addAlarm';
|
||||
static const String alarmsMessage = 'alarmsMessage';
|
||||
static const String once = 'once';
|
||||
static const String daily = 'daily';
|
||||
static const String selectDay = 'selectDay';
|
||||
static const String remoteTurnOff = 'remoteTurnOff';
|
||||
static const String remoteTurnOffMessage = 'remoteTurnOffMessage';
|
||||
static const String remoteTurnOffConfirm = 'remoteTurnOffConfirm';
|
||||
static const String remoteRestart = 'remoteRestart';
|
||||
static const String remoteRestartMessage = 'remoteRestartMessage';
|
||||
static const String remoteRestartConfirm = 'remoteRestartConfirm';
|
||||
static const String remoteFactoryReset = 'remoteFactoryReset';
|
||||
static const String remoteFactoryResetMessage = 'remoteFactoryResetMessage';
|
||||
static const String remoteFactoryResetConfirm = 'remoteFactoryResetConfirm';
|
||||
static const String soundAndVibration = 'soundAndVibration';
|
||||
static const String soundOnly = 'soundOnly';
|
||||
static const String vibrationOnly = 'vibrationOnly';
|
||||
static const String silent = 'silent';
|
||||
static const String number = 'number';
|
||||
static const String syncClockMessage = 'syncClockMessage';
|
||||
static const String settings = 'settings';
|
||||
static const String alarm = 'alarm';
|
||||
static const String appStore = 'appStore';
|
||||
static const String blockPhone = 'blockPhone';
|
||||
static const String timezone = 'timezone';
|
||||
static const String language = 'language';
|
||||
static const String battery = 'battery';
|
||||
static const String remoteManagement = 'remoteManagement';
|
||||
static const String legacyNotifications = 'legacyNotifications';
|
||||
static const String smsAlert = 'smsAlert';
|
||||
static const String sosContacts = 'sosContacts';
|
||||
static const String sound = 'sound';
|
||||
static const String wifiSettings = 'wifiSettings';
|
||||
static const String remoteOnOff = 'remoteOnOff';
|
||||
static const String disableFunctions = 'disableFunctions';
|
||||
static const String syncClock = 'syncClock';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user