3rd-party-disclaimer (#2372)

* feat: enhance dEuro Savings UI and functionality with fiat support and tooltips

* feat: add "Reinvest Interest" feature and improve dEuro Savings functionality

- Implemented the "Reinvest Interest" feature for dEuro Savings.
- Updated localized strings for multiple languages.
- Enhanced error handling for insufficient gas fees.
- Incorporated loading state and tooltips in Savings Card and Interest Card.
- Improved UI responsiveness and button state management.

* feat: localize deuro tooltips

* feat: enhance dEuro Savings with reinvestment feature and localized strings

- Added "Reinvest Interest" functionality for dEuro Savings.
- Updated localized strings across multiple languages.
- Improved modal bottom sheet handling for savings add/remove operations.
- Enhanced tooltips and UI responsiveness.

* feat: improve dEuro Savings page with info chips and localized strings

* Update res/values/strings_de.arb

* fix: update dEuro action type reset in transaction handling

* feat: add third-party disclaimer support and UI enhancements

* feat: add dEuro disclaimer functionality

---------

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
Konstantin Ullrich
2025-07-11 03:29:54 +03:00
committed by GitHub
co-authored by Omar Hatem
parent 3ba2e4283e
commit 342269a496
10 changed files with 189 additions and 14 deletions
@@ -34,10 +34,10 @@ class DisclaimerBodyState extends State<DisclaimerPageBody> {
bool _checked = false;
String _fileText = '';
Future<void> getFileLines() async {
_fileText = await rootBundle.loadString(
isMoneroOnly ? 'assets/text/Monerocom_Terms_of_Use.txt' : 'assets/text/Terms_of_Use.txt');
setState(() {});
void getFileLines() {
final fileName =
isMoneroOnly ? 'assets/text/Monerocom_Terms_of_Use.txt' : 'assets/text/Terms_of_Use.txt';
rootBundle.loadString(fileName).then((text) => setState(() => _fileText = text));
}
@override
@@ -48,8 +48,8 @@ class DisclaimerBodyState extends State<DisclaimerPageBody> {
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => widget.isReadOnly,
return PopScope(
canPop: widget.isReadOnly,
child: Container(
color: Theme.of(context).colorScheme.surface,
child: Column(
@@ -121,7 +121,7 @@ class DisclaimerBodyState extends State<DisclaimerPageBody> {
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Theme.of(context).colorScheme.surface.withOpacity(0.0),
Theme.of(context).colorScheme.surface.withAlpha(0),
Theme.of(context).colorScheme.surface,
],
begin: FractionalOffset.topCenter,
@@ -144,11 +144,7 @@ class DisclaimerBodyState extends State<DisclaimerPageBody> {
EdgeInsets.only(left: 24.0, top: 10.0, right: 24.0, bottom: 10.0),
child: InkWell(
key: ValueKey('disclaimer_check_key'),
onTap: () {
setState(() {
_checked = !_checked;
});
},
onTap: () => setState(() =>_checked = !_checked),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
@@ -0,0 +1,97 @@
import 'dart:ui';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class ThirdPartyDisclaimerPage extends BasePage {
ThirdPartyDisclaimerPage({this.title = "Disclaimer"});
@override
final String title;
final String filePath = 'assets/text/disclaimer_deuro.txt';
@override
Widget body(BuildContext context) => _ThirdPartyDisclaimerPageBody(filePath: filePath);
}
class _ThirdPartyDisclaimerPageBody extends StatefulWidget {
_ThirdPartyDisclaimerPageBody({required this.filePath});
final String filePath;
@override
_ThirdPartyDisclaimerPageBodyState createState() => _ThirdPartyDisclaimerPageBodyState();
}
class _ThirdPartyDisclaimerPageBodyState extends State<_ThirdPartyDisclaimerPageBody> {
String _fileText = '';
void getFileLines() =>
rootBundle.loadString(widget.filePath).then((text) => setState(() => _fileText = text));
@override
void initState() {
super.initState();
getFileLines();
}
@override
Widget build(BuildContext context) => Container(
color: Theme.of(context).colorScheme.surface,
child: Column(
children: <Widget>[
SizedBox(height: 10.0),
Expanded(
child: Stack(
children: <Widget>[
SingleChildScrollView(
padding: EdgeInsets.only(left: 24.0, right: 24.0),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: Text(
_fileText,
style: Theme.of(context).textTheme.bodySmall,
))
],
),
SizedBox(
height: 16.0,
),
],
),
),
Container(
alignment: Alignment.bottomCenter,
child: Container(
height: 12,
child: ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 0.7, sigmaY: 0.7),
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Theme.of(context).colorScheme.surface.withAlpha(0),
Theme.of(context).colorScheme.surface,
],
begin: FractionalOffset.topCenter,
end: FractionalOffset.bottomCenter,
),
),
),
),
),
),
)
],
),
),
],
),
);
}
@@ -139,6 +139,7 @@ class DEuroSavingsPage extends BasePage {
void _setReactions(BuildContext context, DEuroViewModel dEuroViewModel) {
if (_isReactionsSet) return;
if (_dEuroViewModel.isFistTime) _showWelcomeTooltip(context);
reaction((_) => dEuroViewModel.transaction, (PendingTransaction? tx) async {
if (tx == null) return;
@@ -315,7 +316,7 @@ class DEuroSavingsPage extends BasePage {
Future<void> _showWelcomeTooltip(BuildContext context) async {
if (!context.mounted) return;
return showModalBottomSheet<void>(
await showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
builder: (BuildContext bottomSheetContext) => InfoBottomSheet(
@@ -333,8 +334,11 @@ class DEuroSavingsPage extends BasePage {
leftButtonText: S.of(context).learn_more,
leftActionButtonKey: ValueKey('deuro_page_tooltip_dialog_welcome_learn_more_button_key'),
actionLeftButton: () => Navigator.of(bottomSheetContext).pop(), // ToDo
showDisclaimerText: _dEuroViewModel.isFistTime,
),
);
if (_dEuroViewModel.isFistTime) _dEuroViewModel.acceptDisclaimer();
}
Future<void> _showNoEthTooltip(BuildContext context) async {
@@ -1,6 +1,8 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/themes/core/material_base_theme.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
@@ -29,6 +31,7 @@ class InfoBottomSheet extends BaseBottomSheet {
final String? content;
final bool isTwoAction;
final bool showDontAskMeCheckbox;
final bool showDisclaimerText;
final Function(bool)? onCheckboxChanged;
final String? actionButtonText;
final VoidCallback? actionButton;
@@ -52,6 +55,7 @@ class InfoBottomSheet extends BaseBottomSheet {
this.content,
this.isTwoAction = false,
this.showDontAskMeCheckbox = false,
this.showDisclaimerText = false,
this.height = 200,
this.onCheckboxChanged,
this.actionButtonText,
@@ -106,9 +110,40 @@ class InfoBottomSheet extends BaseBottomSheet {
],
),
),
if (showDisclaimerText)
Padding(
padding: const EdgeInsets.only(top: 20, bottom: 10),
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
children: [
TextSpan(
text: 'By continuing you agree to this ',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
fontWeight: FontWeight.w500,
),
),
TextSpan(
text: 'disclaimer',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
decoration: TextDecoration.underline,
fontWeight: FontWeight.w700,
),
recognizer: TapGestureRecognizer()
..onTap = () => Navigator.pushNamed(context, Routes.readThirdPartyDisclaimer),
),
],
),
),
),
if (showDontAskMeCheckbox)
Padding(
padding: const EdgeInsets.only(left: 34.0),
padding: const EdgeInsets.only(left: 34),
child: Row(
children: [
SimpleCheckbox(onChanged: onCheckboxChanged),