mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
Merge branch 'cake-tech:dev' into dev
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cw_core/db/sqlite_debug.dart';
|
||||
import 'package:cw_core/root_dir.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
Database? db;
|
||||
|
||||
@@ -22,7 +24,25 @@ Future<void> _addColumnIfNotExists(
|
||||
}
|
||||
}
|
||||
|
||||
Future<File> sqliteDebugMarkerFile() async {
|
||||
final appDir = await getAppDir();
|
||||
final dbDebugMarker = p.join(appDir.path, ".sqlite_db_debug");
|
||||
return File(dbDebugMarker);
|
||||
}
|
||||
|
||||
Future<void> initDb({String? pathOverride}) async {
|
||||
final dbDebugMarker = await sqliteDebugMarkerFile();
|
||||
try {
|
||||
if (dbDebugMarker.existsSync()) {
|
||||
throw Exception("Debug marker is present");
|
||||
}
|
||||
await _initDb(pathOverride: pathOverride);
|
||||
} catch (e, s) {
|
||||
await handleSqliteError(e, s);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _initDb({String? pathOverride}) async {
|
||||
if (Platform.isLinux || Platform.isWindows) {
|
||||
databaseFactory = databaseFactoryFfi;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:cw_core/db/sqlite.dart';
|
||||
import 'package:cw_core/root_dir.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
Future<void> handleSqliteError(final dynamic error, final StackTrace stackTrace) async {
|
||||
try {
|
||||
final f = await sqliteDebugMarkerFile();
|
||||
f.createSync();
|
||||
} catch (_) {}
|
||||
runApp(
|
||||
SqliteErrorHandlerApp(
|
||||
error: error,
|
||||
stackTrace: stackTrace,
|
||||
),
|
||||
);
|
||||
await Future.delayed(Duration(hours: 24));
|
||||
}
|
||||
|
||||
class SqliteErrorHandlerApp extends StatelessWidget {
|
||||
const SqliteErrorHandlerApp({
|
||||
super.key,
|
||||
required this.error,
|
||||
required this.stackTrace,
|
||||
});
|
||||
|
||||
final dynamic error;
|
||||
final StackTrace? stackTrace;
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
title: 'Cake Wallet Crash',
|
||||
themeMode: ThemeMode.light,
|
||||
home: SqliteErrorHandler(
|
||||
error: error,
|
||||
stackTrace: stackTrace,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SqliteErrorHandler extends StatefulWidget {
|
||||
SqliteErrorHandler({
|
||||
super.key,
|
||||
required this.error,
|
||||
required this.stackTrace,
|
||||
});
|
||||
|
||||
final dynamic error;
|
||||
final StackTrace? stackTrace;
|
||||
|
||||
@override
|
||||
State<SqliteErrorHandler> createState() => _SqliteErrorHandlerState();
|
||||
}
|
||||
|
||||
class _SqliteErrorHandlerState extends State<SqliteErrorHandler> {
|
||||
String log = "";
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Sqlite Recovery Screen"),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _buildNotice(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _text(final dynamic element) {
|
||||
return SelectableText(
|
||||
element.toString(),
|
||||
);
|
||||
}
|
||||
|
||||
void _dbgRemoveDebugMarker() async {
|
||||
final f = await sqliteDebugMarkerFile();
|
||||
f.delete();
|
||||
}
|
||||
|
||||
void _corruptTheDb() async {
|
||||
final dbFile = File("${(await getAppDir()).path}/cake.db");
|
||||
await writeBytesToFile(dbFile);
|
||||
}
|
||||
|
||||
void _dumpDb() async {
|
||||
try {
|
||||
final dbFile = File("${(await getAppDir()).path}/cake.db");
|
||||
final jsonText = JsonEncoder.withIndent(" ").convert(await dumpCustomDb(dbFile.path));
|
||||
setState(() {
|
||||
log = jsonText;
|
||||
});
|
||||
} catch (e, s) {
|
||||
setState(() {
|
||||
log = """
|
||||
e: $e
|
||||
|
||||
s: $s
|
||||
""";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _removeDb() async {
|
||||
try {
|
||||
File("${(await getAppDir()).path}/cake.db.old").deleteSync();
|
||||
} catch (_) {}
|
||||
final dbFile = File("${(await getAppDir()).path}/cake.db");
|
||||
dbFile.renameSync("${(await getAppDir()).path}/cake.db.old");
|
||||
}
|
||||
|
||||
List<Widget> _debugOptions() {
|
||||
return [
|
||||
_debugButton("Remove debug marker", _dbgRemoveDebugMarker),
|
||||
if (kDebugMode || kProfileMode) _debugButton("[DO NOT PRESS] Corrupt the DB", _corruptTheDb),
|
||||
_debugButton("Remove DB", _removeDb),
|
||||
_debugButton("Dump DB", _dumpDb),
|
||||
];
|
||||
}
|
||||
|
||||
Widget _debugButton(String text, VoidCallback callback) {
|
||||
return ElevatedButton(
|
||||
onPressed: callback,
|
||||
child: Text(text),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildNotice() {
|
||||
return [
|
||||
_text("""Critical error occured in SQLite
|
||||
This is very rare, usually caused by the device killing the app before it finishes writing file.
|
||||
Here you can recover the database or recreate it to regain access to your wallets.
|
||||
During this process some of the metadata may get lost.
|
||||
Please contact support@cakewallet.com if you need any help."""),
|
||||
Divider(),
|
||||
..._debugOptions(),
|
||||
if (log.isNotEmpty) ...[
|
||||
Divider(),
|
||||
SelectableText(
|
||||
log,
|
||||
style: TextStyle(
|
||||
fontSize: 8,
|
||||
),
|
||||
),
|
||||
],
|
||||
Divider(),
|
||||
_text(widget.error),
|
||||
Divider(),
|
||||
_text(widget.stackTrace),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> writeBytesToFile(File file) async {
|
||||
const int seek = 5;
|
||||
const int count = 50000;
|
||||
final raf = await file.open(mode: FileMode.writeOnlyAppend);
|
||||
try {
|
||||
await raf.setPosition(seek);
|
||||
final zeros = Uint8List.fromList(List.generate(count, (_) => 0x42));
|
||||
await raf.writeFrom(zeros);
|
||||
} finally {
|
||||
await raf.close();
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,17 @@ const _BOLT_PREFIXES = ["lnbcrt", "lntbs", "lnbc", "lntb"];
|
||||
const _LUD17_PREFIXES = ['lnurlw', 'lnurlc', 'lnurlp', 'keyauth'];
|
||||
|
||||
bool isBolt11ZeroInvoice(String invoice) {
|
||||
final request = Bech32Codec().decode(invoice.replaceFirst("lightning:", ""), invoice.length);
|
||||
try {
|
||||
final request = Bech32Codec().decode(invoice.replaceFirst("lightning:", ""), invoice.length);
|
||||
|
||||
final prefix = _BOLT_PREFIXES
|
||||
.firstWhere((prefix) => request.hrp.startsWith(prefix), orElse: () => "");
|
||||
|
||||
return request.hrp.length == prefix.length;
|
||||
final prefix = _BOLT_PREFIXES
|
||||
.firstWhere((prefix) => request.hrp.startsWith(prefix), orElse: () => "");
|
||||
|
||||
return request.hrp.length == prefix.length;
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the amount of a Bolt 11 Invoice in
|
||||
|
||||
+3
-3
@@ -1569,10 +1569,10 @@ Future<void> setup({
|
||||
unspentCoinsListViewModel:
|
||||
getIt.get<UnspentCoinsListViewModel>(param1: coinTypeToSpendFrom)));
|
||||
|
||||
getIt.registerFactoryParam<NewCoinControlPage, UnspentCoinType?, void>(
|
||||
(coinTypeToSpendFrom, _) => NewCoinControlPage(
|
||||
getIt.registerFactoryParam<NewCoinControlPage, UnspentCoinType?, bool?>(
|
||||
(coinTypeToSpendFrom, canEdit) => NewCoinControlPage(
|
||||
unspentCoinsListViewModel:
|
||||
getIt.get<UnspentCoinsListViewModel>(param1: coinTypeToSpendFrom))
|
||||
getIt.get<UnspentCoinsListViewModel>(param1: coinTypeToSpendFrom), canEdit: canEdit ?? true,)
|
||||
|
||||
);
|
||||
|
||||
|
||||
@@ -4,15 +4,25 @@ import 'package:cake_wallet/new-ui/widgets/modal_header.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart';
|
||||
import 'package:cw_core/unspent_coin_type.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import "package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart";
|
||||
|
||||
class CoinControlPageArgs {
|
||||
final bool canEdit;
|
||||
final UnspentCoinType? coinTypeToSpendFrom;
|
||||
|
||||
const CoinControlPageArgs({required this.canEdit, this.coinTypeToSpendFrom});
|
||||
}
|
||||
|
||||
class NewCoinControlPage extends StatefulWidget {
|
||||
const NewCoinControlPage({super.key, required this.unspentCoinsListViewModel});
|
||||
const NewCoinControlPage(
|
||||
{super.key, required this.unspentCoinsListViewModel, required this.canEdit});
|
||||
|
||||
final UnspentCoinsListViewModel unspentCoinsListViewModel;
|
||||
final bool canEdit;
|
||||
|
||||
@override
|
||||
State<NewCoinControlPage> createState() => _NewCoinControlPageState();
|
||||
@@ -31,9 +41,9 @@ class _NewCoinControlPageState extends State<NewCoinControlPage> {
|
||||
Widget build(BuildContext context) {
|
||||
return PopScope(
|
||||
canPop: !widget.unspentCoinsListViewModel.isSavingItems,
|
||||
onPopInvokedWithResult: (didPop, result) async{
|
||||
onPopInvokedWithResult: (didPop, result) async {
|
||||
await widget.unspentCoinsListViewModel.dispose();
|
||||
if(!didPop && mounted) Navigator.of(context).pop();
|
||||
if (!didPop && mounted) Navigator.of(context).pop();
|
||||
},
|
||||
child: Material(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
|
||||
@@ -94,9 +104,9 @@ class _NewCoinControlPageState extends State<NewCoinControlPage> {
|
||||
child: ModalHeader(
|
||||
iconPath: "assets/new-ui/settings_row_icons/coin-control.svg",
|
||||
title: "Coin Control",
|
||||
message: S.of(context).coin_control_desc),
|
||||
message: widget.canEdit ? S.of(context).coin_control_desc : S.of(context).coin_control_desc_no_edit),
|
||||
),
|
||||
if (widget.unspentCoinsListViewModel.items.isNotEmpty)
|
||||
if (widget.unspentCoinsListViewModel.items.isNotEmpty && widget.canEdit)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Row(
|
||||
@@ -124,7 +134,7 @@ class _NewCoinControlPageState extends State<NewCoinControlPage> {
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
) else SizedBox(height: 24,),
|
||||
if (widget.unspentCoinsListViewModel.nonFrozenItems.isEmpty &&
|
||||
widget.unspentCoinsListViewModel.frozenItems.isEmpty) ...[
|
||||
SizedBox(height: 12),
|
||||
@@ -144,6 +154,7 @@ class _NewCoinControlPageState extends State<NewCoinControlPage> {
|
||||
: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18.0),
|
||||
child: CoinControlListSection(
|
||||
canEdit: widget.canEdit,
|
||||
items: widget.unspentCoinsListViewModel.nonFrozenItems,
|
||||
unspentCoinsListViewModel:
|
||||
widget.unspentCoinsListViewModel),
|
||||
@@ -170,6 +181,7 @@ class _NewCoinControlPageState extends State<NewCoinControlPage> {
|
||||
.onSurfaceVariant),
|
||||
),
|
||||
CoinControlListSection(
|
||||
canEdit: widget.canEdit,
|
||||
items: widget
|
||||
.unspentCoinsListViewModel.frozenItems,
|
||||
unspentCoinsListViewModel:
|
||||
@@ -194,9 +206,10 @@ class _NewCoinControlPageState extends State<NewCoinControlPage> {
|
||||
|
||||
class CoinControlListSection extends StatelessWidget {
|
||||
const CoinControlListSection(
|
||||
{super.key, required this.items, required this.unspentCoinsListViewModel});
|
||||
{super.key, required this.items, required this.unspentCoinsListViewModel, required this.canEdit});
|
||||
|
||||
final List<UnspentCoinsItem> items;
|
||||
final bool canEdit;
|
||||
final UnspentCoinsListViewModel unspentCoinsListViewModel;
|
||||
|
||||
@override
|
||||
@@ -231,6 +244,7 @@ class CoinControlListSection extends StatelessWidget {
|
||||
isLoading: item.isBeingSaved,
|
||||
isFirst: index == 0,
|
||||
isLast: index == items.length - 1,
|
||||
hasCheckbox: canEdit,
|
||||
onCheckBoxTap: item.isFrozen
|
||||
? null
|
||||
: () async {
|
||||
|
||||
@@ -524,7 +524,7 @@ class _NewSendPageState extends State<NewSendPage> {
|
||||
label: "Coin Control",
|
||||
onTap: () {
|
||||
showCupertinoModalBottomSheet(enableDrag: false, useRootNavigator: true, isDismissible: false, context: context, builder: (context){
|
||||
return NewCoinControlPage(unspentCoinsListViewModel: widget.sendViewModel.unspentCoinsListViewModel,);
|
||||
return NewCoinControlPage(unspentCoinsListViewModel: widget.sendViewModel.unspentCoinsListViewModel,canEdit: true);
|
||||
});
|
||||
}
|
||||
),
|
||||
|
||||
@@ -17,7 +17,7 @@ class CoinControlListItem extends StatelessWidget {
|
||||
required this.isFirst,
|
||||
required this.isLast,
|
||||
required this.isLoading,
|
||||
this.onCheckBoxTap,
|
||||
this.onCheckBoxTap, required this.hasCheckbox,
|
||||
});
|
||||
|
||||
final String note;
|
||||
@@ -31,6 +31,7 @@ class CoinControlListItem extends StatelessWidget {
|
||||
final bool isFirst;
|
||||
final bool isLast;
|
||||
final bool isLoading;
|
||||
final bool hasCheckbox;
|
||||
final Function()? onCheckBoxTap;
|
||||
|
||||
@override
|
||||
@@ -123,6 +124,10 @@ class CoinControlListItem extends StatelessWidget {
|
||||
return CakeImageWidget(imageUrl:"assets/new-ui/frozen.svg");
|
||||
}
|
||||
|
||||
if(!hasCheckbox) {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
|
||||
return NewSimpleCheckbox(value: isSending, onChanged: (value){onCheckBoxTap?.call();});
|
||||
|
||||
}
|
||||
|
||||
@@ -163,17 +163,17 @@ class CoinActionRow extends StatelessWidget {
|
||||
|
||||
late final PaymentRequest req;
|
||||
var unspentCoinType = UnspentCoinType.any;
|
||||
// if (false && SendViewModelBase.isNonZeroAmountLightningInvoice(code)) {
|
||||
// unspentCoinType = UnspentCoinType.lightning;
|
||||
// final amount = CryptoCurrency.btcln.formatAmount(BigInt.from(getBolt11Amount(code) ?? 0));
|
||||
// req = PaymentRequest(code, amount, "", "", "");
|
||||
// } else if (false && SendViewModelBase.isLnurlInvoice(code)) {
|
||||
// unspentCoinType = UnspentCoinType.lightning;
|
||||
// final amount = CryptoCurrency.btcln.formatAmount(BigInt.from(await LNURL.getPayRequestAmount(code) ?? 0));
|
||||
// req = PaymentRequest(code, amount, "", "", "");
|
||||
// } else if (false &&OpenCryptoPayService.isOpenCryptoPayQR(code)) {
|
||||
// req = PaymentRequest(code, "", "", "", "");
|
||||
// } else
|
||||
if (SendViewModelBase.isNonZeroAmountLightningInvoice(code)) {
|
||||
unspentCoinType = UnspentCoinType.lightning;
|
||||
final amount = CryptoCurrency.btcln.formatAmount(BigInt.from(getBolt11Amount(code) ?? 0));
|
||||
req = PaymentRequest(code, amount, "", "", "");
|
||||
} else if (SendViewModelBase.isLnurlInvoice(code)) {
|
||||
unspentCoinType = UnspentCoinType.lightning;
|
||||
final amount = CryptoCurrency.btcln.formatAmount(BigInt.from(await LNURL.getPayRequestAmount(code) ?? 0));
|
||||
req = PaymentRequest(code, amount, "", "", "");
|
||||
} else if (OpenCryptoPayService.isOpenCryptoPayQR(code)) {
|
||||
req = PaymentRequest(code, "", "", "", "");
|
||||
} else
|
||||
if (Uri.tryParse(code)?.scheme == "wc") {
|
||||
if (!isWalletConnectCompatibleChain(walletType)) {
|
||||
showPopUp<void>(
|
||||
|
||||
@@ -102,7 +102,7 @@ class SwapOptionsPage extends StatelessWidget {
|
||||
label: "Coin Control",
|
||||
onTap: () {
|
||||
showCupertinoModalBottomSheet(enableDrag: false, useRootNavigator: true, isDismissible: false, context: context, builder: (context){
|
||||
return NewCoinControlPage(unspentCoinsListViewModel: exchangeViewModel.unspentCoinsListViewModel,);
|
||||
return NewCoinControlPage(unspentCoinsListViewModel: exchangeViewModel.unspentCoinsListViewModel,canEdit: true,);
|
||||
});
|
||||
}),
|
||||
ListItemSelector(
|
||||
|
||||
+3
-6
@@ -759,12 +759,9 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||
);
|
||||
|
||||
case Routes.unspentCoinsList:
|
||||
final coinTypeToSpendFrom = settings.arguments as UnspentCoinType?;
|
||||
return handleRouteWithPlatformAwareness(
|
||||
(context) => FeatureFlag.hasNewUi
|
||||
? getIt.get<NewCoinControlPage>(param1: coinTypeToSpendFrom)
|
||||
: getIt.get<UnspentCoinsListPage>(param1: coinTypeToSpendFrom),
|
||||
);
|
||||
final args = settings.arguments as CoinControlPageArgs?;
|
||||
return handleRouteWithPlatformAwareness((context) =>
|
||||
getIt.get<NewCoinControlPage>(param1: args?.coinTypeToSpendFrom, param2: args?.canEdit));
|
||||
|
||||
case Routes.unspentCoinsDetails:
|
||||
final args = settings.arguments as List;
|
||||
|
||||
@@ -135,69 +135,73 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 18, right: 18, bottom: 12),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
),
|
||||
child: ListView.separated(
|
||||
key: const ValueKey('new_wallet_type_scrollable_key'),
|
||||
itemCount: filteredTypes.length,
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (context, index) {
|
||||
final item = filteredTypes[index];
|
||||
|
||||
return GestureDetector(
|
||||
key: ValueKey('new_wallet_type_${item.name}_button_key'),
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
onTypeSelected(item);
|
||||
},
|
||||
child: SizedBox(
|
||||
height: 48,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
CakeImageWidget(
|
||||
height: 24,
|
||||
width: 24,
|
||||
imageUrl: getCryptoCurrencyIconForWalletListItem(item),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(walletTypeToDisplayName(item)),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
walletTypeToDisplayTicker(item),
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 18, right: 18, bottom: 12),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
),
|
||||
child: ListView.separated(
|
||||
key: const ValueKey('new_wallet_type_scrollable_key'),
|
||||
itemCount: filteredTypes.length,
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
padding: EdgeInsets.zero,
|
||||
itemBuilder: (context, index) {
|
||||
final item = filteredTypes[index];
|
||||
|
||||
return GestureDetector(
|
||||
key: ValueKey('new_wallet_type_${item.name}_button_key'),
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
onTypeSelected(item);
|
||||
},
|
||||
child: SizedBox(
|
||||
height: 48,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
CakeImageWidget(
|
||||
height: 24,
|
||||
width: 24,
|
||||
imageUrl: getCryptoCurrencyIconForWalletListItem(item),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
CakeImageWidget(
|
||||
imageUrl: "assets/new-ui/arrow_forward.svg",
|
||||
height: 16,
|
||||
colorFilter: ColorFilter.mode(
|
||||
Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
BlendMode.srcIn,
|
||||
const SizedBox(width: 12),
|
||||
Text(walletTypeToDisplayName(item)),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
walletTypeToDisplayTicker(item),
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
CakeImageWidget(
|
||||
imageUrl: "assets/new-ui/arrow_forward.svg",
|
||||
height: 16,
|
||||
colorFilter: ColorFilter.mode(
|
||||
Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) => Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) => Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart';
|
||||
import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_selector.dart';
|
||||
@@ -14,11 +16,13 @@ import 'package:cake_wallet/utils/feature_flag.dart';
|
||||
import 'package:cake_wallet/utils/share_util.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart';
|
||||
import 'package:cw_core/root_dir.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/transaction_priority.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_core/db/sqlite.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
class OtherSettingsPage extends BasePage {
|
||||
OtherSettingsPage(this._otherSettingsViewModel) {
|
||||
@@ -211,6 +215,14 @@ class OtherSettingsPage extends BasePage {
|
||||
),
|
||||
);
|
||||
}),
|
||||
ListItemRegularRow(
|
||||
keyValue: '[dev] fake corrupt sqlite db',
|
||||
label: '[dev] fake corrupt sqlite db',
|
||||
onTap: () async {
|
||||
final dbDebugMarker = await sqliteDebugMarkerFile();
|
||||
dbDebugMarker.create();
|
||||
}
|
||||
),
|
||||
]
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart';
|
||||
import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_toggle.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/new-ui/pages/coin_control_page.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/modal_header.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/modal_page_wrapper.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
|
||||
@@ -95,7 +96,7 @@ class PrivacyPage extends BasePage {
|
||||
keyValue: "coin_control",
|
||||
label: "Coin Control",
|
||||
onTap: () =>
|
||||
Navigator.of(context).pushNamed(Routes.unspentCoinsList)),
|
||||
Navigator.of(context).pushNamed(Routes.unspentCoinsList, arguments: CoinControlPageArgs(canEdit: false, coinTypeToSpendFrom: null))),
|
||||
],
|
||||
}
|
||||
),
|
||||
|
||||
@@ -208,6 +208,7 @@
|
||||
"close": "Close",
|
||||
"coin_control": "Coin Control (optional)",
|
||||
"coin_control_desc": "Filter outputs for this transaction. The changes you make here will only reflect on the current transaction.",
|
||||
"coin_control_desc_no_edit": "Freeze outputs if needed. The changes will be persistent across all transactions. You can also filter outputs for one transaction on the Send or Swap screens.",
|
||||
"coin_control_load_failed": "Failed to load unspent coins",
|
||||
"cold_or_recover_wallet": "Add an air-gapped cold wallet or recover a paper wallet.",
|
||||
"collection_address": "Collection Address",
|
||||
|
||||
Reference in New Issue
Block a user