From dd53db6795e11f8a94c7f06c8b93652b15b280c0 Mon Sep 17 00:00:00 2001 From: aitorarana Date: Fri, 20 Mar 2026 09:33:57 +0100 Subject: [PATCH] set language --- modules/legacy/melos_legacy.iml | 3 + .../features/language/language_builder.dart | 6 +- .../presentation/language_screen.dart | 100 +++++- .../state/language_view_model.dart | 85 +++++ .../state/language_view_state.dart | 17 + .../state/language_view_state.freezed.dart | 307 ++++++++++++++++++ .../models/send_command_request_model.dart | 2 + .../antelop/antelop/maven-metadata.xml | 2 +- .../antelop/antelop/maven-metadata.xml.md5 | 2 +- .../antelop/antelop/maven-metadata.xml.sha1 | 2 +- 10 files changed, 519 insertions(+), 7 deletions(-) create mode 100644 modules/legacy/modules/settings/lib/src/features/language/presentation/state/language_view_model.dart create mode 100644 modules/legacy/modules/settings/lib/src/features/language/presentation/state/language_view_state.dart create mode 100644 modules/legacy/modules/settings/lib/src/features/language/presentation/state/language_view_state.freezed.dart diff --git a/modules/legacy/melos_legacy.iml b/modules/legacy/melos_legacy.iml index cecc6de8..a37321b0 100644 --- a/modules/legacy/melos_legacy.iml +++ b/modules/legacy/melos_legacy.iml @@ -32,6 +32,9 @@ + + + diff --git a/modules/legacy/modules/settings/lib/src/features/language/language_builder.dart b/modules/legacy/modules/settings/lib/src/features/language/language_builder.dart index e5fa9769..acf3f718 100644 --- a/modules/legacy/modules/settings/lib/src/features/language/language_builder.dart +++ b/modules/legacy/modules/settings/lib/src/features/language/language_builder.dart @@ -1,5 +1,7 @@ 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/language_screen.dart'; @@ -7,9 +9,11 @@ class LanguageBuilder { const LanguageBuilder(); Page buildPage(BuildContext context, GoRouterState state) { + final navigationContract = GetIt.I(); + return MaterialPage( key: state.pageKey, - child: const LanguageScreen(), + child: LanguageScreen(navigationContract: navigationContract), ); } } diff --git a/modules/legacy/modules/settings/lib/src/features/language/presentation/language_screen.dart b/modules/legacy/modules/settings/lib/src/features/language/presentation/language_screen.dart index b31c20dd..fcd9d2e0 100644 --- a/modules/legacy/modules/settings/lib/src/features/language/presentation/language_screen.dart +++ b/modules/legacy/modules/settings/lib/src/features/language/presentation/language_screen.dart @@ -2,21 +2,115 @@ 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/language/presentation/state/language_view_model.dart'; import 'package:sf_localizations/sf_localizations.dart'; class LanguageScreen extends ConsumerWidget { - const LanguageScreen({super.key}); + + final NavigationContract navigationContract; + + const LanguageScreen({super.key, required this.navigationContract}); @override Widget build(BuildContext context, WidgetRef ref) { final theme = ref.watch(themePortProvider); + final vm = ref.read(languageViewModelProvider.notifier); + final language = ref.watch( + languageViewModelProvider.select((s)=>s.language) + ); + + const Map languageOptions = { + 'es': 'espaƱol', + 'en': 'english', + }; + final languageCodes = languageOptions.keys.toList(growable: false); + final languageLabels = languageOptions.values.toList(growable: false); + + ref.listen(languageViewModelProvider.select((s) => s.errorMessage), ( + _, + errorMessage, + ) { + if (errorMessage.isNotEmpty) { + showTopSnackbar( + context, + message: errorMessage, + type: MessageType.error, + ); + } + }); + + ref.listen(languageViewModelProvider.select((s) => s.isComplete), ( + _, + isComplete, + ) { + if (isComplete) navigationContract.goBack(); + }); + return LegacyPageLayout( theme: theme, title: context.translate(I18n.language), - body: const Center( - child: Text('Coming soon'), + body: SingleChildScrollView( + child: RadioGroup( + groupValue: language, + onChanged: (value) {vm.selectLanguage(value.toString());}, + child: Column( + children: List.generate(languageOptions.length, (int i) { + return _Option( + value: languageCodes.elementAt(i), + label: languageLabels.elementAt(i), + ); + }), + ) + ), ), + footer: const _SaveSection(), ); } } + +class _Option extends ConsumerWidget { + + final String value; + final String label; + + const _Option({ + required this.value, + required this.label, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final theme = ref.read(themePortProvider); + + return RadioListTile( + title: Text(label), + value: value, + activeColor: theme.getColorFor(ThemeCode.legacyPrimary), + controlAffinity: ListTileControlAffinity.trailing, + ); + } +} + +class _SaveSection extends ConsumerWidget { + + const _SaveSection(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final theme = ref.read(themePortProvider); + + final vm = ref.read(languageViewModelProvider.notifier); + + return Padding( + padding: EdgeInsets.symmetric(horizontal: 24, vertical: 10), + child: PrimaryButton( + onPressed: vm.submit, + text: context.translate(I18n.save), + color: theme.getColorFor(ThemeCode.legacyPrimary) + ), + ); + } + +} \ No newline at end of file diff --git a/modules/legacy/modules/settings/lib/src/features/language/presentation/state/language_view_model.dart b/modules/legacy/modules/settings/lib/src/features/language/presentation/state/language_view_model.dart new file mode 100644 index 00000000..a4f9d50a --- /dev/null +++ b/modules/legacy/modules/settings/lib/src/features/language/presentation/state/language_view_model.dart @@ -0,0 +1,85 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:legacy_shared/legacy_shared.dart'; +import 'language_view_state.dart'; + +final languageViewModelProvider = +NotifierProvider.autoDispose( + LanguageViewModel.new, +); + +class LanguageViewModel extends Notifier { + late final CommandsRepository _commandsRepository; + + @override + LanguageViewState build() { + _commandsRepository = ref.read(commandsRepositoryProvider); + + Future.microtask(() => load()); + return const LanguageViewState(isLoading: true); + } + + Future load() async { + state = state.copyWith(isLoading: true, errorMessage: ''); + try { + final device = ref.read(selectedDeviceProvider); + + state = state.copyWith( + isLoading: false, + device: device, + language: device?.settings['language'], + ); + } catch (e) { + if (!ref.mounted) return; + state = state.copyWith(isLoading: false, errorMessage: e.toString()); + } + } + + void selectLanguage(String value) { + if (value == state.language) return; + + state = state.copyWith( + language: value + ); + } + + Future submit() async { + if (state.device == null) { + state = state.copyWith( + errorMessage: 'errorMessageNoDevice', + ); + return; + } + + if (state.language == state.device!.settings['language']) { + state = state.copyWith( + isComplete: true, + ); + return; + } + + try { + state = state.copyWith( + isLoading: true, + isComplete: false, + ); + + final request = SendCommandRequestModel( + device: state.device!.identificator, + command: DeviceCommand.setLanguage, + data: {'language': state.language}, + ); + + await _commandsRepository.send(request: request); + + state = state.copyWith( + isLoading: false, + isComplete: true, + ); + } catch (e) { + state = state.copyWith( + isLoading: false, + errorMessage: e.toString(), + ); + } + } +} \ No newline at end of file diff --git a/modules/legacy/modules/settings/lib/src/features/language/presentation/state/language_view_state.dart b/modules/legacy/modules/settings/lib/src/features/language/presentation/state/language_view_state.dart new file mode 100644 index 00000000..f1fd123d --- /dev/null +++ b/modules/legacy/modules/settings/lib/src/features/language/presentation/state/language_view_state.dart @@ -0,0 +1,17 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:sf_shared/sf_shared.dart'; + +part 'language_view_state.freezed.dart'; + +@freezed +abstract class LanguageViewState with _$LanguageViewState { + const LanguageViewState._(); + + const factory LanguageViewState({ + @Default(false) bool isLoading, + @Default(false) bool isComplete, + DeviceEntity? device, + @Default('es') String language, + @Default('') String errorMessage, + }) = _LanguageViewState; +} diff --git a/modules/legacy/modules/settings/lib/src/features/language/presentation/state/language_view_state.freezed.dart b/modules/legacy/modules/settings/lib/src/features/language/presentation/state/language_view_state.freezed.dart new file mode 100644 index 00000000..469fc12a --- /dev/null +++ b/modules/legacy/modules/settings/lib/src/features/language/presentation/state/language_view_state.freezed.dart @@ -0,0 +1,307 @@ +// 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 'language_view_state.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; +/// @nodoc +mixin _$LanguageViewState { + + bool get isLoading; bool get isComplete; DeviceEntity? get device; String get language; String get errorMessage; +/// Create a copy of LanguageViewState +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$LanguageViewStateCopyWith get copyWith => _$LanguageViewStateCopyWithImpl(this as LanguageViewState, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is LanguageViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isComplete, isComplete) || other.isComplete == isComplete)&&(identical(other.device, device) || other.device == device)&&(identical(other.language, language) || other.language == language)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)); +} + + +@override +int get hashCode => Object.hash(runtimeType,isLoading,isComplete,device,language,errorMessage); + +@override +String toString() { + return 'LanguageViewState(isLoading: $isLoading, isComplete: $isComplete, device: $device, language: $language, errorMessage: $errorMessage)'; +} + + +} + +/// @nodoc +abstract mixin class $LanguageViewStateCopyWith<$Res> { + factory $LanguageViewStateCopyWith(LanguageViewState value, $Res Function(LanguageViewState) _then) = _$LanguageViewStateCopyWithImpl; +@useResult +$Res call({ + bool isLoading, bool isComplete, DeviceEntity? device, String language, String errorMessage +}); + + +$DeviceEntityCopyWith<$Res>? get device; + +} +/// @nodoc +class _$LanguageViewStateCopyWithImpl<$Res> + implements $LanguageViewStateCopyWith<$Res> { + _$LanguageViewStateCopyWithImpl(this._self, this._then); + + final LanguageViewState _self; + final $Res Function(LanguageViewState) _then; + +/// Create a copy of LanguageViewState +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? isLoading = null,Object? isComplete = null,Object? device = freezed,Object? language = null,Object? errorMessage = null,}) { + return _then(_self.copyWith( +isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable +as bool,isComplete: null == isComplete ? _self.isComplete : isComplete // ignore: cast_nullable_to_non_nullable +as bool,device: freezed == device ? _self.device : device // ignore: cast_nullable_to_non_nullable +as DeviceEntity?,language: null == language ? _self.language : language // ignore: cast_nullable_to_non_nullable +as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable +as String, + )); +} +/// Create a copy of LanguageViewState +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$DeviceEntityCopyWith<$Res>? get device { + if (_self.device == null) { + return null; + } + + return $DeviceEntityCopyWith<$Res>(_self.device!, (value) { + return _then(_self.copyWith(device: value)); + }); +} +} + + +/// Adds pattern-matching-related methods to [LanguageViewState]. +extension LanguageViewStatePatterns on LanguageViewState { +/// 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 Function( _LanguageViewState value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _LanguageViewState() 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 Function( _LanguageViewState value) $default,){ +final _that = this; +switch (_that) { +case _LanguageViewState(): +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? Function( _LanguageViewState value)? $default,){ +final _that = this; +switch (_that) { +case _LanguageViewState() 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 Function( bool isLoading, bool isComplete, DeviceEntity? device, String language, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _LanguageViewState() when $default != null: +return $default(_that.isLoading,_that.isComplete,_that.device,_that.language,_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 Function( bool isLoading, bool isComplete, DeviceEntity? device, String language, String errorMessage) $default,) {final _that = this; +switch (_that) { +case _LanguageViewState(): +return $default(_that.isLoading,_that.isComplete,_that.device,_that.language,_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? Function( bool isLoading, bool isComplete, DeviceEntity? device, String language, String errorMessage)? $default,) {final _that = this; +switch (_that) { +case _LanguageViewState() when $default != null: +return $default(_that.isLoading,_that.isComplete,_that.device,_that.language,_that.errorMessage);case _: + return null; + +} +} + +} + +/// @nodoc + + +class _LanguageViewState extends LanguageViewState { + const _LanguageViewState({this.isLoading = false, this.isComplete = false, this.device, this.language = 'es', this.errorMessage = ''}): super._(); + + +@override@JsonKey() final bool isLoading; +@override@JsonKey() final bool isComplete; +@override final DeviceEntity? device; +@override@JsonKey() final String language; +@override@JsonKey() final String errorMessage; + +/// Create a copy of LanguageViewState +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$LanguageViewStateCopyWith<_LanguageViewState> get copyWith => __$LanguageViewStateCopyWithImpl<_LanguageViewState>(this, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _LanguageViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isComplete, isComplete) || other.isComplete == isComplete)&&(identical(other.device, device) || other.device == device)&&(identical(other.language, language) || other.language == language)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)); +} + + +@override +int get hashCode => Object.hash(runtimeType,isLoading,isComplete,device,language,errorMessage); + +@override +String toString() { + return 'LanguageViewState(isLoading: $isLoading, isComplete: $isComplete, device: $device, language: $language, errorMessage: $errorMessage)'; +} + + +} + +/// @nodoc +abstract mixin class _$LanguageViewStateCopyWith<$Res> implements $LanguageViewStateCopyWith<$Res> { + factory _$LanguageViewStateCopyWith(_LanguageViewState value, $Res Function(_LanguageViewState) _then) = __$LanguageViewStateCopyWithImpl; +@override @useResult +$Res call({ + bool isLoading, bool isComplete, DeviceEntity? device, String language, String errorMessage +}); + + +@override $DeviceEntityCopyWith<$Res>? get device; + +} +/// @nodoc +class __$LanguageViewStateCopyWithImpl<$Res> + implements _$LanguageViewStateCopyWith<$Res> { + __$LanguageViewStateCopyWithImpl(this._self, this._then); + + final _LanguageViewState _self; + final $Res Function(_LanguageViewState) _then; + +/// Create a copy of LanguageViewState +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? isLoading = null,Object? isComplete = null,Object? device = freezed,Object? language = null,Object? errorMessage = null,}) { + return _then(_LanguageViewState( +isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable +as bool,isComplete: null == isComplete ? _self.isComplete : isComplete // ignore: cast_nullable_to_non_nullable +as bool,device: freezed == device ? _self.device : device // ignore: cast_nullable_to_non_nullable +as DeviceEntity?,language: null == language ? _self.language : language // ignore: cast_nullable_to_non_nullable +as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable +as String, + )); +} + +/// Create a copy of LanguageViewState +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$DeviceEntityCopyWith<$Res>? get device { + if (_self.device == null) { + return null; + } + + return $DeviceEntityCopyWith<$Res>(_self.device!, (value) { + return _then(_self.copyWith(device: value)); + }); +} +} + +// dart format on diff --git a/modules/legacy/packages/legacy_shared/lib/src/data/models/send_command_request_model.dart b/modules/legacy/packages/legacy_shared/lib/src/data/models/send_command_request_model.dart index 358627ae..1f9a33dc 100644 --- a/modules/legacy/packages/legacy_shared/lib/src/data/models/send_command_request_model.dart +++ b/modules/legacy/packages/legacy_shared/lib/src/data/models/send_command_request_model.dart @@ -12,6 +12,8 @@ enum DeviceCommand { restart, @JsonValue('REWARDS') rewards, + @JsonValue('SET_LANGUAGE') + setLanguage, @JsonValue('SHUTDOWN') shutdown, @JsonValue('SOUND') diff --git a/packages/flutter_treezor_entrust_sdk_bridge/android/build/com/entrust/antelop/antelop/maven-metadata.xml b/packages/flutter_treezor_entrust_sdk_bridge/android/build/com/entrust/antelop/antelop/maven-metadata.xml index a264b49f..4cf4310c 100644 --- a/packages/flutter_treezor_entrust_sdk_bridge/android/build/com/entrust/antelop/antelop/maven-metadata.xml +++ b/packages/flutter_treezor_entrust_sdk_bridge/android/build/com/entrust/antelop/antelop/maven-metadata.xml @@ -7,6 +7,6 @@ 2.6.4 - 20260318000000 + 20260320000000 diff --git a/packages/flutter_treezor_entrust_sdk_bridge/android/build/com/entrust/antelop/antelop/maven-metadata.xml.md5 b/packages/flutter_treezor_entrust_sdk_bridge/android/build/com/entrust/antelop/antelop/maven-metadata.xml.md5 index 66e064a1..31b6c765 100644 --- a/packages/flutter_treezor_entrust_sdk_bridge/android/build/com/entrust/antelop/antelop/maven-metadata.xml.md5 +++ b/packages/flutter_treezor_entrust_sdk_bridge/android/build/com/entrust/antelop/antelop/maven-metadata.xml.md5 @@ -1 +1 @@ -f9e85f64806f37132f0c0cc4ef8a67ec \ No newline at end of file +20c099fa5d73eb3667d91872fabb23b6 \ No newline at end of file diff --git a/packages/flutter_treezor_entrust_sdk_bridge/android/build/com/entrust/antelop/antelop/maven-metadata.xml.sha1 b/packages/flutter_treezor_entrust_sdk_bridge/android/build/com/entrust/antelop/antelop/maven-metadata.xml.sha1 index d0a4a597..0b5ad7e2 100644 --- a/packages/flutter_treezor_entrust_sdk_bridge/android/build/com/entrust/antelop/antelop/maven-metadata.xml.sha1 +++ b/packages/flutter_treezor_entrust_sdk_bridge/android/build/com/entrust/antelop/antelop/maven-metadata.xml.sha1 @@ -1 +1 @@ -b7a72907f1f917f7b7d0cd57cb170901965b4113 \ No newline at end of file +67c2ba6eea196d22403b194e8407fcca966416f6 \ No newline at end of file