mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-17 05:41:29 +02:00
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:
co-authored by
Omar Hatem
parent
3ba2e4283e
commit
342269a496
@@ -0,0 +1,18 @@
|
||||
Last Modified: July 10, 2025
|
||||
|
||||
Third-Party Protocol Disclaimer
|
||||
==============================
|
||||
|
||||
You are now accessing a third-party protocol for savings and earnings in dEURO.
|
||||
|
||||
This protocol is not developed, maintained, or controlled by Cake Wallet. Cake Wallet is not responsible for any third-party protocols or associated third party content accessed through the Services.
|
||||
|
||||
Third party protocols are not investigated, monitored or managed by us. If you decide to access a third-party protocol or to access or use any associated third party content, you do so at your own risk and you should be aware that our terms and policies no longer govern. You should review the applicable third party's terms and policies, including privacy and data gathering practices.
|
||||
|
||||
By proceeding, you acknowledge and agree that:
|
||||
|
||||
- You are using this third-party service at your own risk.
|
||||
- Cake Wallet does not provide any guarantees, warranties, or support for the use of this protocol.
|
||||
- Cake Wallet is not responsible for any loss of funds, technical issues, or changes to the third-party protocol.
|
||||
|
||||
Please do your own research and exercise caution accordingly.
|
||||
@@ -62,6 +62,7 @@ class PreferencesKey {
|
||||
static const mwebNodeUri = 'mwebNodeUri';
|
||||
static const shouldShowReceiveWarning = 'should_show_receive_warning';
|
||||
static const shouldShowYatPopup = 'should_show_yat_popup';
|
||||
static const shouldShowDEuroDisclaimer = 'should_show_deuro_disclaimer';
|
||||
static const shouldShowRepWarning = 'should_show_rep_warning';
|
||||
static const moneroWalletPasswordUpdateV1Base = 'monero_wallet_update_v1';
|
||||
static const syncModeKey = 'sync_mode';
|
||||
|
||||
@@ -43,6 +43,7 @@ import 'package:cake_wallet/src/screens/dev/secure_preferences_page.dart';
|
||||
import 'package:cake_wallet/src/screens/dev/shared_preferences_page.dart';
|
||||
import 'package:cake_wallet/src/screens/dev/background_sync_logs_page.dart';
|
||||
import 'package:cake_wallet/src/screens/disclaimer/disclaimer_page.dart';
|
||||
import 'package:cake_wallet/src/screens/disclaimer/third_party_disclaimer_page.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange/exchange_page.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange_trade/exchange_confirm_page.dart';
|
||||
@@ -418,6 +419,9 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||
case Routes.readDisclaimer:
|
||||
return CupertinoPageRoute<void>(builder: (_) => DisclaimerPage(isReadOnly: true));
|
||||
|
||||
case Routes.readThirdPartyDisclaimer:
|
||||
return CupertinoPageRoute<void>(builder: (_) => ThirdPartyDisclaimerPage());
|
||||
|
||||
case Routes.changeRep:
|
||||
return CupertinoPageRoute<void>(builder: (_) => getIt.get<NanoChangeRepPage>());
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ class Routes {
|
||||
static const walletEdit = '/walletEdit';
|
||||
static const disclaimer = '/disclaimer';
|
||||
static const readDisclaimer = '/read_disclaimer';
|
||||
static const readThirdPartyDisclaimer = '/disclaimer/third_party';
|
||||
static const changeRep = '/change_representative';
|
||||
static const walletList = '/view_model.wallet_list';
|
||||
static const auth = '/auth';
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -87,6 +87,7 @@ abstract class SettingsStoreBase with Store {
|
||||
required Map<WalletType, Node> nodes,
|
||||
required Map<WalletType, Node> powNodes,
|
||||
required this.shouldShowYatPopup,
|
||||
required this.shouldShowDEuroDisclaimer,
|
||||
required this.shouldShowRepWarning,
|
||||
required this.isBitcoinBuyEnabled,
|
||||
required this.actionlistDisplayMode,
|
||||
@@ -251,6 +252,11 @@ abstract class SettingsStoreBase with Store {
|
||||
(bool shouldShowYatPopup) =>
|
||||
sharedPreferences.setBool(PreferencesKey.shouldShowYatPopup, shouldShowYatPopup));
|
||||
|
||||
reaction(
|
||||
(_) => shouldShowDEuroDisclaimer,
|
||||
(bool shouldShowDEuroDisclaimer) =>
|
||||
sharedPreferences.setBool(PreferencesKey.shouldShowDEuroDisclaimer, shouldShowDEuroDisclaimer));
|
||||
|
||||
reaction((_) => shouldShowRepWarning,
|
||||
(bool val) => sharedPreferences.setBool(PreferencesKey.shouldShowRepWarning, val));
|
||||
|
||||
@@ -648,6 +654,9 @@ abstract class SettingsStoreBase with Store {
|
||||
@observable
|
||||
bool shouldShowYatPopup;
|
||||
|
||||
@observable
|
||||
bool shouldShowDEuroDisclaimer;
|
||||
|
||||
@observable
|
||||
bool shouldShowRepWarning;
|
||||
|
||||
@@ -1092,6 +1101,7 @@ abstract class SettingsStoreBase with Store {
|
||||
final packageInfo = await PackageInfo.fromPlatform();
|
||||
final deviceName = await _getDeviceName() ?? '';
|
||||
final shouldShowYatPopup = sharedPreferences.getBool(PreferencesKey.shouldShowYatPopup) ?? true;
|
||||
final shouldShowDEuroDisclaimer = sharedPreferences.getBool(PreferencesKey.shouldShowDEuroDisclaimer) ?? true;
|
||||
final shouldShowRepWarning =
|
||||
sharedPreferences.getBool(PreferencesKey.shouldShowRepWarning) ?? true;
|
||||
|
||||
@@ -1363,6 +1373,7 @@ abstract class SettingsStoreBase with Store {
|
||||
initialSyncMode: savedSyncMode,
|
||||
initialSyncAll: savedSyncAll,
|
||||
shouldShowYatPopup: shouldShowYatPopup,
|
||||
shouldShowDEuroDisclaimer: shouldShowDEuroDisclaimer,
|
||||
shouldShowRepWarning: shouldShowRepWarning,
|
||||
initialBuiltinTor: builtinTor,
|
||||
);
|
||||
@@ -1489,6 +1500,8 @@ abstract class SettingsStoreBase with Store {
|
||||
languageCode = sharedPreferences.getString(PreferencesKey.currentLanguageCode) ?? languageCode;
|
||||
shouldShowYatPopup =
|
||||
sharedPreferences.getBool(PreferencesKey.shouldShowYatPopup) ?? shouldShowYatPopup;
|
||||
shouldShowDEuroDisclaimer =
|
||||
sharedPreferences.getBool(PreferencesKey.shouldShowDEuroDisclaimer) ?? shouldShowDEuroDisclaimer;
|
||||
shouldShowRepWarning =
|
||||
sharedPreferences.getBool(PreferencesKey.shouldShowRepWarning) ?? shouldShowRepWarning;
|
||||
sortBalanceBy = SortBalanceBy
|
||||
|
||||
@@ -38,6 +38,12 @@ abstract class DEuroViewModelBase with Store {
|
||||
@computed
|
||||
bool get isFiatDisabled => balanceViewModel.isFiatDisabled;
|
||||
|
||||
@computed
|
||||
bool get isFistTime => _settingsStore.shouldShowDEuroDisclaimer;
|
||||
|
||||
@action
|
||||
void acceptDisclaimer() => _settingsStore.shouldShowDEuroDisclaimer = false;
|
||||
|
||||
@computed
|
||||
String get pendingTransactionFiatAmountFormatted =>
|
||||
isFiatDisabled ? '' : '$pendingTransactionFiatAmount ${fiat.title}';
|
||||
|
||||
Reference in New Issue
Block a user