diff --git a/apps/mobile_app/lib/navigation/app_router.dart b/apps/mobile_app/lib/navigation/app_router.dart index b74445aa..0c86c309 100644 --- a/apps/mobile_app/lib/navigation/app_router.dart +++ b/apps/mobile_app/lib/navigation/app_router.dart @@ -25,7 +25,7 @@ late final GoRouter appRouter; void configureAppRouter() { appRouter = GoRouter( navigatorKey: rootNavigatorKey, - initialLocation: AppRoutes.legacyOnboarding, + initialLocation: AppRoutes.controlPanel, debugLogDiagnostics: true, routes: [ GoRoute( @@ -108,6 +108,11 @@ void configureAppRouter() { name: 'locate_device', pageBuilder: LocateDeviceBuilder().buildPage, ), + GoRoute( + path: 'rewards', + name: 'rewards', + pageBuilder: RewardsBuilder().buildPage, + ), ], ), ], diff --git a/modules/legacy/modules/device_management/lib/device_management.dart b/modules/legacy/modules/device_management/lib/device_management.dart index ed03eb98..d83a3543 100644 --- a/modules/legacy/modules/device_management/lib/device_management.dart +++ b/modules/legacy/modules/device_management/lib/device_management.dart @@ -4,3 +4,4 @@ export 'src/features/device_management/device_management_builder.dart'; export 'src/features/contacts/contacts_builder.dart'; export 'src/features/remote_connection/remote_connection_builder.dart'; export 'src/features/locate_device/locate_device_builder.dart'; +export 'src/features/rewards/rewards_builder.dart'; \ No newline at end of file diff --git a/modules/legacy/modules/device_management/lib/src/features/device_management/device_management_screen.dart b/modules/legacy/modules/device_management/lib/src/features/device_management/device_management_screen.dart index 609c9ae5..63d0f55d 100644 --- a/modules/legacy/modules/device_management/lib/src/features/device_management/device_management_screen.dart +++ b/modules/legacy/modules/device_management/lib/src/features/device_management/device_management_screen.dart @@ -82,7 +82,7 @@ class DeviceManagementScreen extends ConsumerWidget { SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)), AppMenuButton( color: theme.getColorFor(ThemeCode.legacyPrimary), - onPressed: (){}, + onPressed: (){navigationContract.pushTo(AppRoutes.rewards);}, icon: SFIcons.rewardsCircle, negativeIcon: true, text: context.translate(I18n.rewards) diff --git a/modules/legacy/modules/device_management/lib/src/features/rewards/presentation/rewards_screen.dart b/modules/legacy/modules/device_management/lib/src/features/rewards/presentation/rewards_screen.dart new file mode 100644 index 00000000..252c8c3f --- /dev/null +++ b/modules/legacy/modules/device_management/lib/src/features/rewards/presentation/rewards_screen.dart @@ -0,0 +1,125 @@ +import 'package:design_system/design_system.dart'; +import 'package:device_management/src/features/rewards/presentation/state/rewards_view_model.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'; + +class RewardsScreen extends ConsumerWidget { + + final NavigationContract navigationContract; + + const RewardsScreen({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.rewards), + body: Column( + children: [ + Center( + child: Icon(SFIcons.rewardsCircle, + size: 180, + color: theme.getColorFor(ThemeCode.legacyPrimary), + ) + ), + SizedBox(height: 32), + Text( + context.translate(I18n.rewardsMessage), + textAlign: TextAlign.start, + ), + SizedBox(height: 12), + const _CounterSection(), + ], + ), + footer: _SaveSection(), + ); + } +} + +class _CounterSection extends ConsumerWidget { + + const _CounterSection(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + + final theme = ref.read(themePortProvider); + + final state = ref.watch(rewardsViewModelProvider); + final vm = ref.read(rewardsViewModelProvider.notifier); + + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconButton( + onPressed: vm.decreaseAmount, + icon: Icon(Icons.remove), + color: theme.getColorFor(ThemeCode.legacyPrimary), + ), + SizedBox( + width: 240, + child: TextField( + controller: vm.amountController, + decoration: InputDecoration( + border: OutlineInputBorder( + borderSide: BorderSide( + color: theme.getColorFor(ThemeCode.legacyPrimary), + ), + borderRadius: BorderRadius.all(Radius.circular(32)), + ), + ), + style: TextStyle(color: theme.getColorFor(ThemeCode.legacyPrimary)), + textAlign: TextAlign.center, + keyboardType: TextInputType.number, + ) + ), + IconButton( + onPressed: vm.increaseAmount, + icon: Icon(Icons.add), + color: theme.getColorFor(ThemeCode.legacyPrimary), + ), + ], + ); + } +} + +class _SaveSection extends ConsumerWidget{ + + const _SaveSection(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final theme = ref.read(themePortProvider); + + final vm = ref.read(rewardsViewModelProvider.notifier); + + return Padding( + padding: EdgeInsets.symmetric(horizontal: 24, vertical: 10), + child: PrimaryButton( + onPressed: () async { + await vm.submit(); + + final errorMessage = ref.read( + rewardsViewModelProvider.select((s)=>s.errorMessage) + ); + + if (errorMessage.isNotEmpty) { + showTopSnackbar( + context, + message: errorMessage, + type: MessageType.error, + ); + } + }, + text: context.translate(I18n.sendRewards), + color: theme.getColorFor(ThemeCode.legacyPrimary) + ) + ); + } +} \ No newline at end of file diff --git a/modules/legacy/modules/device_management/lib/src/features/rewards/presentation/state/rewards_view_model.dart b/modules/legacy/modules/device_management/lib/src/features/rewards/presentation/state/rewards_view_model.dart new file mode 100644 index 00000000..d235392f --- /dev/null +++ b/modules/legacy/modules/device_management/lib/src/features/rewards/presentation/state/rewards_view_model.dart @@ -0,0 +1,109 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:legacy_shared/legacy_shared.dart'; + +import 'rewards_view_state.dart'; + + +final rewardsViewModelProvider = +NotifierProvider.autoDispose( + RewardsViewModel.new, +); + +class RewardsViewModel extends Notifier { + + static final _min = 1; + + late final TextEditingController amountController; + late final SendCommandUseCase _sendCommandUseCase; + + @override + RewardsViewState build() { + + _sendCommandUseCase = ref.read(sendCommandUseCaseProvider); + + amountController = TextEditingController(); + amountController.addListener(_onAmountChanged); + amountController.text = '1'; + + ref.onDispose(disposeControllers); + + return const RewardsViewState(); + } + + void _onAmountChanged() { + final raw = amountController.text; + + if (raw.isEmpty) return; + + final amount = int.tryParse(raw); + + if (amount == null) return; + if (amount == state.amount) return; + if (amount < _min) return; + + state = state.copyWith( + amount: amount, + errorMessage: '', + ); + } + + void increaseAmount() { + final amount = state.amount + 1; + + state = state.copyWith( + amount: amount, + errorMessage: '', + ); + amountController.text = amount.toString(); + } + + void decreaseAmount() { + if (state.amount == _min) return; + + final amount = state.amount - 1; + + state = state.copyWith( + amount: amount, + errorMessage: '', + ); + amountController.text = amount.toString(); + } + + Future submit() async { + + try { + state = state.copyWith( + isLoading: true, + isComplete: false, + ); + + final device = ref.read(selectedDeviceProvider); + final request = SendCommandRequestModel( + device: device!.identificator.toString(), + command: 'REWARDS', + data: { + 'rewards': state.amount + } + ); + + await _sendCommandUseCase.send(request: request); + + state = state.copyWith( + isLoading: false, + isComplete: true + ); + } catch(e) { + state = state.copyWith( + errorMessage: e.toString(), + isLoading: false, + isComplete: false + ); + } + } + + void disposeControllers() { + amountController.removeListener(_onAmountChanged); + amountController.dispose(); + } +} diff --git a/modules/legacy/modules/device_management/lib/src/features/rewards/presentation/state/rewards_view_state.dart b/modules/legacy/modules/device_management/lib/src/features/rewards/presentation/state/rewards_view_state.dart new file mode 100644 index 00000000..92946970 --- /dev/null +++ b/modules/legacy/modules/device_management/lib/src/features/rewards/presentation/state/rewards_view_state.dart @@ -0,0 +1,15 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'rewards_view_state.freezed.dart'; + +@freezed +abstract class RewardsViewState with _$RewardsViewState { + const factory RewardsViewState({ + @Default(1) int amount, + @Default(false) bool isLoading, + @Default(false) bool isComplete, + @Default('') String errorMessage, + }) = _RewardsViewState; + + +} \ No newline at end of file diff --git a/modules/legacy/modules/device_management/lib/src/features/rewards/presentation/state/rewards_view_state.freezed.dart b/modules/legacy/modules/device_management/lib/src/features/rewards/presentation/state/rewards_view_state.freezed.dart new file mode 100644 index 00000000..c23f95dc --- /dev/null +++ b/modules/legacy/modules/device_management/lib/src/features/rewards/presentation/state/rewards_view_state.freezed.dart @@ -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 'rewards_view_state.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; +/// @nodoc +mixin _$RewardsViewState { + + int get amount; bool get isLoading; bool get isComplete; String get errorMessage; +/// Create a copy of RewardsViewState +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$RewardsViewStateCopyWith get copyWith => _$RewardsViewStateCopyWithImpl(this as RewardsViewState, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is RewardsViewState&&(identical(other.amount, amount) || other.amount == amount)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isComplete, isComplete) || other.isComplete == isComplete)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)); +} + + +@override +int get hashCode => Object.hash(runtimeType,amount,isLoading,isComplete,errorMessage); + +@override +String toString() { + return 'RewardsViewState(amount: $amount, isLoading: $isLoading, isComplete: $isComplete, errorMessage: $errorMessage)'; +} + + +} + +/// @nodoc +abstract mixin class $RewardsViewStateCopyWith<$Res> { + factory $RewardsViewStateCopyWith(RewardsViewState value, $Res Function(RewardsViewState) _then) = _$RewardsViewStateCopyWithImpl; +@useResult +$Res call({ + int amount, bool isLoading, bool isComplete, String errorMessage +}); + + + + +} +/// @nodoc +class _$RewardsViewStateCopyWithImpl<$Res> + implements $RewardsViewStateCopyWith<$Res> { + _$RewardsViewStateCopyWithImpl(this._self, this._then); + + final RewardsViewState _self; + final $Res Function(RewardsViewState) _then; + +/// Create a copy of RewardsViewState +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? amount = null,Object? isLoading = null,Object? isComplete = null,Object? errorMessage = null,}) { + return _then(_self.copyWith( +amount: null == amount ? _self.amount : amount // ignore: cast_nullable_to_non_nullable +as int,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable +as bool,isComplete: null == isComplete ? _self.isComplete : isComplete // ignore: cast_nullable_to_non_nullable +as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable +as String, + )); +} + +} + + +/// Adds pattern-matching-related methods to [RewardsViewState]. +extension RewardsViewStatePatterns on RewardsViewState { +/// 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( _RewardsViewState value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _RewardsViewState() 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( _RewardsViewState value) $default,){ +final _that = this; +switch (_that) { +case _RewardsViewState(): +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( _RewardsViewState value)? $default,){ +final _that = this; +switch (_that) { +case _RewardsViewState() 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( int amount, bool isLoading, bool isComplete, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _RewardsViewState() when $default != null: +return $default(_that.amount,_that.isLoading,_that.isComplete,_that.errorMessage);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( int amount, bool isLoading, bool isComplete, String errorMessage) $default,) {final _that = this; +switch (_that) { +case _RewardsViewState(): +return $default(_that.amount,_that.isLoading,_that.isComplete,_that.errorMessage);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( int amount, bool isLoading, bool isComplete, String errorMessage)? $default,) {final _that = this; +switch (_that) { +case _RewardsViewState() when $default != null: +return $default(_that.amount,_that.isLoading,_that.isComplete,_that.errorMessage);case _: + return null; + +} +} + +} + +/// @nodoc + + +class _RewardsViewState implements RewardsViewState { + const _RewardsViewState({this.amount = 0, this.isLoading = false, this.isComplete = false, this.errorMessage = ''}); + + +@override@JsonKey() final int amount; +@override@JsonKey() final bool isLoading; +@override@JsonKey() final bool isComplete; +@override@JsonKey() final String errorMessage; + +/// Create a copy of RewardsViewState +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$RewardsViewStateCopyWith<_RewardsViewState> get copyWith => __$RewardsViewStateCopyWithImpl<_RewardsViewState>(this, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _RewardsViewState&&(identical(other.amount, amount) || other.amount == amount)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isComplete, isComplete) || other.isComplete == isComplete)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)); +} + + +@override +int get hashCode => Object.hash(runtimeType,amount,isLoading,isComplete,errorMessage); + +@override +String toString() { + return 'RewardsViewState(amount: $amount, isLoading: $isLoading, isComplete: $isComplete, errorMessage: $errorMessage)'; +} + + +} + +/// @nodoc +abstract mixin class _$RewardsViewStateCopyWith<$Res> implements $RewardsViewStateCopyWith<$Res> { + factory _$RewardsViewStateCopyWith(_RewardsViewState value, $Res Function(_RewardsViewState) _then) = __$RewardsViewStateCopyWithImpl; +@override @useResult +$Res call({ + int amount, bool isLoading, bool isComplete, String errorMessage +}); + + + + +} +/// @nodoc +class __$RewardsViewStateCopyWithImpl<$Res> + implements _$RewardsViewStateCopyWith<$Res> { + __$RewardsViewStateCopyWithImpl(this._self, this._then); + + final _RewardsViewState _self; + final $Res Function(_RewardsViewState) _then; + +/// Create a copy of RewardsViewState +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? amount = null,Object? isLoading = null,Object? isComplete = null,Object? errorMessage = null,}) { + return _then(_RewardsViewState( +amount: null == amount ? _self.amount : amount // ignore: cast_nullable_to_non_nullable +as int,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable +as bool,isComplete: null == isComplete ? _self.isComplete : isComplete // ignore: cast_nullable_to_non_nullable +as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable +as String, + )); +} + + +} + +// dart format on diff --git a/modules/legacy/modules/device_management/lib/src/features/rewards/rewards_builder.dart b/modules/legacy/modules/device_management/lib/src/features/rewards/rewards_builder.dart new file mode 100644 index 00000000..63cff1a2 --- /dev/null +++ b/modules/legacy/modules/device_management/lib/src/features/rewards/rewards_builder.dart @@ -0,0 +1,18 @@ +import 'package:device_management/src/features/rewards/presentation/rewards_screen.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:get_it/get_it.dart'; +import 'package:navigation/navigation.dart'; + +class RewardsBuilder { + const RewardsBuilder(); + + Page buildPage(BuildContext context, GoRouterState state) { + final NavigationContract navigationContract = GetIt.I(); + + return MaterialPage( + key: state.pageKey, + child: RewardsScreen(navigationContract: navigationContract), + ); + } +} 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 62f97c42..4376f9ee 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 - 20260309000000 + 20260310000000 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 07888a04..3f1b5e45 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 @@ -efba28f7c4340264bc1e42e5d11102a8 \ No newline at end of file +4d748a03a80705124d1ffe8143732218 \ 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 b515de4c..614622ee 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 @@ -33fe0a028f582b89ab719f8dafc0490e05af4ff5 \ No newline at end of file +626b5ba9e1320d252d7286b888d11b14552a2a38 \ No newline at end of file diff --git a/packages/navigation/lib/app_routes.dart b/packages/navigation/lib/app_routes.dart index 1377da97..c3c6a4a2 100644 --- a/packages/navigation/lib/app_routes.dart +++ b/packages/navigation/lib/app_routes.dart @@ -50,6 +50,7 @@ class AppRoutes { static const contacts = '$deviceManagement/contacts'; static const remoteConnection = '$deviceManagement/remote_connection'; static const locateDevice = '$deviceManagement/locate_device'; + static const rewards = '$deviceManagement/rewards'; static const legacyLogin = '$legacy/login'; static const legacySignup = '$legacy/signup'; diff --git a/packages/sf_localizations/assets/l10n/en.json b/packages/sf_localizations/assets/l10n/en.json index 019d19c0..edb5589b 100755 --- a/packages/sf_localizations/assets/l10n/en.json +++ b/packages/sf_localizations/assets/l10n/en.json @@ -489,7 +489,6 @@ "deleteAccount": "Delete account", "logOut": "Log out", "loginEmail": "(Login email)", - "loginSuccess": "Login successful", "userNameLabel": "User name", "userPhoneLabel": "User phone number", "contactEmailLabel": "Contact email", @@ -498,7 +497,6 @@ "editDeviceTitle": "Edit Device", "name": "Name", "deleteDeviceDialog": "Are you sure you want to delete this device from the list?", - "cancel": "Cancel", "delete": "Delete", "userAccount": "Account: {email}", "userRole": "Role: {role}", @@ -544,5 +542,7 @@ "deviceSetup_weightHint": "30", "deviceSetup_heightLabel": "Height (cm)", "deviceSetup_heightHint": "120", - "activationKeyLabel": "Activation key" + "activationKeyLabel": "Activation key", + "rewardsMessage": "Send rewards", + "sendRewards": "Send rewards!" } \ No newline at end of file diff --git a/packages/sf_localizations/assets/l10n/es.json b/packages/sf_localizations/assets/l10n/es.json index de986f81..5a721878 100644 --- a/packages/sf_localizations/assets/l10n/es.json +++ b/packages/sf_localizations/assets/l10n/es.json @@ -540,5 +540,7 @@ "deviceSetup_weightHint": "30", "deviceSetup_heightLabel": "Altura (cm)", "deviceSetup_heightHint": "120", - "activationKeyLabel": "Clave de activación" + "activationKeyLabel": "Clave de activación", + "rewardsMessage": "Envía una recompensa", + "sendRewards": "¡Enviar recompensas!" } \ No newline at end of file diff --git a/packages/sf_localizations/lib/src/generated/i18n.dart b/packages/sf_localizations/lib/src/generated/i18n.dart index 44a53793..efd602ae 100755 --- a/packages/sf_localizations/lib/src/generated/i18n.dart +++ b/packages/sf_localizations/lib/src/generated/i18n.dart @@ -661,4 +661,6 @@ class I18n { static const String deviceSetup_heightLabel = 'deviceSetup_heightLabel'; static const String deviceSetup_heightHint = 'deviceSetup_heightHint'; static const String activationKeyLabel = 'activationKeyLabel'; + static const String rewardsMessage = 'rewardsMessage'; + static const String sendRewards = 'sendRewards'; }