Fix Scan TAddrs

This commit is contained in:
Hanh 2023-03-13 17:53:42 +10:00
parent a6580baa48
commit 9b16a6b7be
17 changed files with 553 additions and 423 deletions

1
intl.sh Executable file
View File

@ -0,0 +1 @@
flutter pub global run intl_utils:generate

View File

@ -265,11 +265,33 @@ class AccountManagerState extends State<AccountManagerPage> {
} }
_onFullRestore() { _onFullRestore() {
Navigator.of(this.context).pushNamed('/fullRestore'); Navigator.of(context).pushNamed('/fullRestore');
} }
_onScanSubAccounts() { _onScanSubAccounts() async {
Navigator.of(this.context).pushNamed('/scantaddr'); final s = S.of(context);
final nav = Navigator.of(context);
final gapController = TextEditingController(text: '10');
final confirmed = await showDialog<bool>(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
title: Text(s.scanTransparentAddresses),
content: SingleChildScrollView(
child: Column(mainAxisSize: MainAxisSize.min, children: [
TextFormField(
decoration: InputDecoration(labelText: s.gapLimit),
controller: gapController,
keyboardType: TextInputType.number)
])),
actions: confirmButtons(context, () {
nav.pop(true);
}))) ??
false;
if (confirmed) {
final gapLimit = int.parse(gapController.text);
Navigator.of(this.context).pushNamed('/scantaddr', arguments: gapLimit);
}
} }
} }

View File

@ -397,6 +397,10 @@ class MessageLookup extends MessageLookupByLibrary {
"scanQrCode": MessageLookupByLibrary.simpleMessage("Scan QR Code"), "scanQrCode": MessageLookupByLibrary.simpleMessage("Scan QR Code"),
"scanStartingMomentarily": "scanStartingMomentarily":
MessageLookupByLibrary.simpleMessage("Scan starting momentarily"), MessageLookupByLibrary.simpleMessage("Scan starting momentarily"),
"scanTransparentAddresses":
MessageLookupByLibrary.simpleMessage("Scan Transparent Addresses"),
"scanningAddresses":
MessageLookupByLibrary.simpleMessage("Scanning addresses"),
"secondary": MessageLookupByLibrary.simpleMessage("Secondary"), "secondary": MessageLookupByLibrary.simpleMessage("Secondary"),
"secretKey": MessageLookupByLibrary.simpleMessage("Secret Key"), "secretKey": MessageLookupByLibrary.simpleMessage("Secret Key"),
"secretShare": MessageLookupByLibrary.simpleMessage("Secret Share"), "secretShare": MessageLookupByLibrary.simpleMessage("Secret Share"),

View File

@ -404,6 +404,10 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("Escanear Código QR"), MessageLookupByLibrary.simpleMessage("Escanear Código QR"),
"scanStartingMomentarily": MessageLookupByLibrary.simpleMessage( "scanStartingMomentarily": MessageLookupByLibrary.simpleMessage(
"Escaneo iniciado momentáneamente"), "Escaneo iniciado momentáneamente"),
"scanTransparentAddresses":
MessageLookupByLibrary.simpleMessage("Scan Transparent Addresses"),
"scanningAddresses":
MessageLookupByLibrary.simpleMessage("Scanning addresses"),
"secondary": MessageLookupByLibrary.simpleMessage("Secundario"), "secondary": MessageLookupByLibrary.simpleMessage("Secundario"),
"secretKey": MessageLookupByLibrary.simpleMessage("Clave secreta"), "secretKey": MessageLookupByLibrary.simpleMessage("Clave secreta"),
"secretShare": MessageLookupByLibrary.simpleMessage("Clave secreta"), "secretShare": MessageLookupByLibrary.simpleMessage("Clave secreta"),

View File

@ -408,6 +408,10 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("Scanner le QR Code"), MessageLookupByLibrary.simpleMessage("Scanner le QR Code"),
"scanStartingMomentarily": MessageLookupByLibrary.simpleMessage( "scanStartingMomentarily": MessageLookupByLibrary.simpleMessage(
"Le scan démarre momentanément"), "Le scan démarre momentanément"),
"scanTransparentAddresses":
MessageLookupByLibrary.simpleMessage("Scan Transparent Addresses"),
"scanningAddresses":
MessageLookupByLibrary.simpleMessage("Scanning addresses"),
"secondary": MessageLookupByLibrary.simpleMessage("Secondaire"), "secondary": MessageLookupByLibrary.simpleMessage("Secondaire"),
"secretKey": MessageLookupByLibrary.simpleMessage("Clé secrète"), "secretKey": MessageLookupByLibrary.simpleMessage("Clé secrète"),
"secretShare": MessageLookupByLibrary.simpleMessage("Secret Share"), "secretShare": MessageLookupByLibrary.simpleMessage("Secret Share"),

View File

@ -3121,6 +3121,26 @@ class S {
args: [], args: [],
); );
} }
/// `Scan Transparent Addresses`
String get scanTransparentAddresses {
return Intl.message(
'Scan Transparent Addresses',
name: 'scanTransparentAddresses',
desc: '',
args: [],
);
}
/// `Scanning addresses`
String get scanningAddresses {
return Intl.message(
'Scanning addresses',
name: 'scanningAddresses',
desc: '',
args: [],
);
}
} }
class AppLocalizationDelegate extends LocalizationsDelegate<S> { class AppLocalizationDelegate extends LocalizationsDelegate<S> {

View File

@ -305,5 +305,7 @@
"invalidPassword": "Invalid Password", "invalidPassword": "Invalid Password",
"databaseRestored": "Database Restored", "databaseRestored": "Database Restored",
"never": "Never", "never": "Never",
"always": "Always" "always": "Always",
"scanTransparentAddresses": "Scan Transparent Addresses",
"scanningAddresses": "Scanning addresses"
} }

View File

@ -303,5 +303,7 @@
"invalidPassword": "Invalid Password", "invalidPassword": "Invalid Password",
"databaseRestored": "Database Restored", "databaseRestored": "Database Restored",
"never": "Never", "never": "Never",
"always": "Always" "always": "Always",
"scanTransparentAddresses": "Scan Transparent Addresses",
"scanningAddresses": "Scanning addresses"
} }

View File

@ -304,5 +304,7 @@
"invalidPassword": "Mot de Passe incorrect", "invalidPassword": "Mot de Passe incorrect",
"databaseRestored": "BD Récupèrée", "databaseRestored": "BD Récupèrée",
"never": "Jamais", "never": "Jamais",
"always": "Toujours" "always": "Toujours",
"scanTransparentAddresses": "Scan Transparent Addresses",
"scanningAddresses": "Scanning addresses"
} }

View File

@ -93,8 +93,8 @@ void handleUri(BuildContext context, Uri uri) {
final coin = coinDef.coin; final coin = coinDef.coin;
final id = WarpApi.getActiveAccountId(coin); final id = WarpApi.getActiveAccountId(coin);
active.setActiveAccount(coin, id); active.setActiveAccount(coin, id);
Navigator.of(context).pushNamed( Navigator.of(context)
'/send', arguments: SendPageArgs(uri: uri.toString())); .pushNamed('/send', arguments: SendPageArgs(uri: uri.toString()));
} }
Future<void> registerURLHandler(BuildContext context) async { Future<void> registerURLHandler(BuildContext context) async {
@ -128,7 +128,7 @@ void handleQuickAction(BuildContext context, String type) {
} }
class LoadProgress extends StatefulWidget { class LoadProgress extends StatefulWidget {
LoadProgress({Key? key}): super(key: key); LoadProgress({Key? key}) : super(key: key);
@override @override
LoadProgressState createState() => LoadProgressState(); LoadProgressState createState() => LoadProgressState();
@ -165,11 +165,13 @@ class LoadProgressState extends State<LoadProgress> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
final textTheme = theme.textTheme; final textTheme = theme.textTheme;
return Scaffold(body: Container( return Scaffold(
body: Container(
alignment: Alignment.center, alignment: Alignment.center,
child: SizedBox(height: 240, width: 200, child: child: SizedBox(
Column( height: 240,
children: [ width: 200,
child: Column(children: [
Image.asset('assets/icon.png', height: 64), Image.asset('assets/icon.png', height: 64),
Padding(padding: EdgeInsets.all(16)), Padding(padding: EdgeInsets.all(16)),
Text(S.of(context).loading, style: textTheme.headlineMedium), Text(S.of(context).loading, style: textTheme.headlineMedium),
@ -177,9 +179,7 @@ class LoadProgressState extends State<LoadProgress> {
LinearProgressIndicator(value: _value), LinearProgressIndicator(value: _value),
Padding(padding: EdgeInsets.all(8)), Padding(padding: EdgeInsets.all(8)),
Text(_message, style: textTheme.labelMedium), Text(_message, style: textTheme.labelMedium),
] ]))));
)
)));
} }
void setValue(double v, String message) { void setValue(double v, String message) {
@ -221,7 +221,8 @@ void main() async {
size: size, size: size,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
skipTaskbar: false, skipTaskbar: false,
titleBarStyle: Platform.isMacOS ? TitleBarStyle.hidden : TitleBarStyle.normal, titleBarStyle:
Platform.isMacOS ? TitleBarStyle.hidden : TitleBarStyle.normal,
); );
windowManager.waitUntilReadyToShow(windowOptions, () async { windowManager.waitUntilReadyToShow(windowOptions, () async {
await windowManager.show(); await windowManager.show();
@ -240,16 +241,15 @@ void main() async {
ledColor: Colors.white, ledColor: Colors.white,
) )
], ],
debug: true debug: true);
);
final home = ZWalletApp(); final home = ZWalletApp();
runApp(FutureBuilder( runApp(FutureBuilder(
future: settings.restore(), future: settings.restore(),
builder: (context, snapshot) { builder: (context, snapshot) {
return snapshot.connectionState == ConnectionState.waiting return snapshot.connectionState == ConnectionState.waiting
? MaterialApp(home: Container()) : ? MaterialApp(home: Container())
Observer(builder: (context) { : Observer(builder: (context) {
final theme = settings.themeData.copyWith( final theme = settings.themeData.copyWith(
useMaterial3: true, useMaterial3: true,
dataTableTheme: DataTableThemeData( dataTableTheme: DataTableThemeData(
@ -274,11 +274,11 @@ void main() async {
'/welcome': (context) => WelcomePage(), '/welcome': (context) => WelcomePage(),
'/account': (context) => HomePage(), '/account': (context) => HomePage(),
'/add_account': (context) => AddAccountPage(), '/add_account': (context) => AddAccountPage(),
'/add_first_account': (context) => AddAccountPage(firstAccount: true), '/add_first_account': (context) =>
AddAccountPage(firstAccount: true),
'/send': (context) => '/send': (context) =>
SendPage(routeSettings.arguments as SendPageArgs?), SendPage(routeSettings.arguments as SendPageArgs?),
'/receive': (context) => '/receive': (context) => PaymentURIPage(),
PaymentURIPage(),
'/accounts': (context) => AccountManagerPage(), '/accounts': (context) => AccountManagerPage(),
'/settings': (context) => SettingsPage(), '/settings': (context) => SettingsPage(),
'/tx': (context) => '/tx': (context) =>
@ -292,25 +292,33 @@ void main() async {
}, },
'/pools': (context) => PoolsPage(), '/pools': (context) => PoolsPage(),
'/multipay': (context) => MultiPayPage(), '/multipay': (context) => MultiPayPage(),
'/edit_theme': (context) => '/edit_theme': (context) => ThemeEditorPage(
ThemeEditorPage(onSaved: settings.updateCustomThemeColors), onSaved: settings.updateCustomThemeColors),
'/reset': (context) => ResetPage(), '/reset': (context) => ResetPage(),
'/fullBackup': (context) => FullBackupPage(), '/fullBackup': (context) => FullBackupPage(),
'/fullRestore': (context) => FullRestorePage(), '/fullRestore': (context) => FullRestorePage(),
'/scantaddr': (context) => ScanTAddrPage(), '/scantaddr': (context) =>
'/qroffline': (context) => QrOffline(routeSettings.arguments as String), ScanTAddrPage(routeSettings.arguments as int),
'/showRawTx': (context) => ShowRawTx(routeSettings.arguments as String), '/qroffline': (context) =>
QrOffline(routeSettings.arguments as String),
'/showRawTx': (context) =>
ShowRawTx(routeSettings.arguments as String),
'/keytool': (context) => KeyToolPage(), '/keytool': (context) => KeyToolPage(),
'/dev': (context) => DevPage(), '/dev': (context) => DevPage(),
'/txplan': (context) => TxPlanPage.fromPlan(routeSettings.arguments as String, false), '/txplan': (context) => TxPlanPage.fromPlan(
'/sign': (context) => TxPlanPage.fromPlan(routeSettings.arguments as String, true), routeSettings.arguments as String, false),
'/sign': (context) => TxPlanPage.fromPlan(
routeSettings.arguments as String, true),
'/syncstats': (context) => SyncChartPage(), '/syncstats': (context) => SyncChartPage(),
'/scanner': (context) { '/scanner': (context) {
final args = routeSettings.arguments as Map<String, dynamic>; final args =
return QRScanner(args['onScan'], completed: args['completed'], multi: args['multi']); routeSettings.arguments as Map<String, dynamic>;
return QRScanner(args['onScan'],
completed: args['completed'], multi: args['multi']);
}, },
}; };
return MaterialPageRoute(builder: routes[routeSettings.name]!); return MaterialPageRoute(
builder: routes[routeSettings.name]!);
}, },
); );
}); });
@ -346,11 +354,14 @@ class ZWalletAppState extends State<ZWalletApp> {
} }
// Only after at least the action method is set, the notification events are delivered // Only after at least the action method is set, the notification events are delivered
AwesomeNotifications().setListeners( AwesomeNotifications().setListeners(
onActionReceivedMethod: NotificationController.onActionReceivedMethod, onActionReceivedMethod:
onNotificationCreatedMethod: NotificationController.onNotificationCreatedMethod, NotificationController.onActionReceivedMethod,
onNotificationDisplayedMethod: NotificationController.onNotificationDisplayedMethod, onNotificationCreatedMethod:
onDismissActionReceivedMethod: NotificationController.onDismissActionReceivedMethod NotificationController.onNotificationCreatedMethod,
); onNotificationDisplayedMethod:
NotificationController.onNotificationDisplayedMethod,
onDismissActionReceivedMethod:
NotificationController.onDismissActionReceivedMethod);
}); });
} }
} }
@ -367,15 +378,14 @@ class ZWalletAppState extends State<ZWalletApp> {
final dbPath = await getDbPath(); final dbPath = await getDbPath();
for (var coin in coins) { for (var coin in coins) {
coin.init(dbPath); coin.init(dbPath);
final server = settings.servers.firstWhere((s) => s.coin == coin.coin); final server =
settings.servers.firstWhere((s) => s.coin == coin.coin);
final url = server.getLWDUrl(); final url = server.getLWDUrl();
if (url != null) if (url != null) WarpApi.updateLWD(coin.coin, url);
WarpApi.updateLWD(coin.coin, url);
} }
if (exportDb) { if (exportDb) {
for (var coin in coins) for (var coin in coins) await coin.export(context, dbPath);
await coin.export(context, dbPath);
prefs.setBool('export_db', false); prefs.setBool('export_db', false);
} }
if (recover) { if (recover) {
@ -394,24 +404,24 @@ class ZWalletAppState extends State<ZWalletApp> {
if (WarpApi.decryptDb(c.dbFullPath, '')) break; // not encrypted if (WarpApi.decryptDb(c.dbFullPath, '')) break; // not encrypted
final reset = await getDbPasswd(context, c.dbFullPath); final reset = await getDbPasswd(context, c.dbFullPath);
if (reset) { // user didn't input the passwd and wants to reset if (reset) {
// user didn't input the passwd and wants to reset
await clearApp(context); await clearApp(context);
} } else
else break; break;
} while(true); } while (true);
for (var c in coins) { for (var c in coins) {
_setProgress(0.2 * (c.coin+1), 'Initializing ${c.ticker}'); _setProgress(0.2 * (c.coin + 1), 'Initializing ${c.ticker}');
await compute(_initWallet, { 'coin': c, 'passwd': settings.dbPasswd }); await compute(_initWallet, {'coin': c, 'passwd': settings.dbPasswd});
} }
_setProgress(0.7, 'Restoring Active Account'); _setProgress(0.7, 'Restoring Active Account');
if (recover) { if (recover) {
final aid = getAvailableId(coins[0].coin); final aid = getAvailableId(coins[0].coin);
if (aid != null) if (aid != null) active.setActiveAccount(aid.coin, aid.id);
active.setActiveAccount(aid.coin, aid.id); } else
} await active.restore();
else await active.restore();
_setProgress(0.8, 'Register URL Protocol Handlers'); _setProgress(0.8, 'Register URL Protocol Handlers');
await registerURLHandler(this.context); await registerURLHandler(this.context);
@ -434,10 +444,12 @@ class ZWalletAppState extends State<ZWalletApp> {
for (var c in settings.coins) { for (var c in settings.coins) {
final coin = c.coin; final coin = c.coin;
final ticker = c.def.ticker; final ticker = c.def.ticker;
shortcuts.add(ShortcutItem(type: '$coin.receive', shortcuts.add(ShortcutItem(
type: '$coin.receive',
localizedTitle: s.receive(ticker), localizedTitle: s.receive(ticker),
icon: 'receive')); icon: 'receive'));
shortcuts.add(ShortcutItem(type: '$coin.send', shortcuts.add(ShortcutItem(
type: '$coin.send',
localizedTitle: s.sendCointicker(ticker), localizedTitle: s.sendCointicker(ticker),
icon: 'send')); icon: 'send'));
} }
@ -468,8 +480,7 @@ class ZWalletAppState extends State<ZWalletApp> {
WarpApi.initWallet(c.coin, c.dbFullPath); WarpApi.initWallet(c.coin, c.dbFullPath);
try { try {
WarpApi.migrateData(c.coin); WarpApi.migrateData(c.coin);
} } catch (e) {} // do not fail on network exception
catch (e) {} // do not fail on network exception
} }
@override @override
@ -489,10 +500,10 @@ class ZWalletAppState extends State<ZWalletApp> {
} }
final GlobalKey<ScaffoldMessengerState> rootScaffoldMessengerKey = final GlobalKey<ScaffoldMessengerState> rootScaffoldMessengerKey =
GlobalKey<ScaffoldMessengerState>(); GlobalKey<ScaffoldMessengerState>();
List<ElevatedButton> confirmButtons(BuildContext context, List<ElevatedButton> confirmButtons(
VoidCallback? onPressed, BuildContext context, VoidCallback? onPressed,
{String? okLabel, Icon? okIcon, cancelValue}) { {String? okLabel, Icon? okIcon, cancelValue}) {
final s = S.of(context); final s = S.of(context);
final navigator = Navigator.of(context); final navigator = Navigator.of(context);
@ -511,7 +522,8 @@ List<ElevatedButton> confirmButtons(BuildContext context,
]; ];
} }
List<TimeSeriesPoint<V>> sampleDaily<T, Y, V>(List<T> timeseries, List<TimeSeriesPoint<V>> sampleDaily<T, Y, V>(
List<T> timeseries,
int start, int start,
int end, int end,
int Function(T) getDay, int Function(T) getDay,
@ -550,54 +562,62 @@ void showQR(BuildContext context, String text, String title) {
return AlertDialog( return AlertDialog(
content: Container( content: Container(
width: double.maxFinite, width: double.maxFinite,
child: SingleChildScrollView(child: Column(children: [ child: SingleChildScrollView(
child: Column(children: [
LayoutBuilder(builder: (context, constraints) { LayoutBuilder(builder: (context, constraints) {
final size = getScreenSize(context) * 0.5; final size = getScreenSize(context) * 0.5;
return QrImage(data: text, backgroundColor: Colors.white, size: size); return QrImage(
data: text, backgroundColor: Colors.white, size: size);
}), }),
Padding(padding: EdgeInsets.all(8)), Padding(padding: EdgeInsets.all(8)),
Text(title, style: Theme Text(title, style: Theme.of(context).textTheme.titleSmall),
.of(context)
.textTheme
.titleSmall),
Padding(padding: EdgeInsets.all(4)), Padding(padding: EdgeInsets.all(4)),
ButtonBar(children: [ ButtonBar(children: [
ElevatedButton.icon(onPressed: () { ElevatedButton.icon(
onPressed: () {
Clipboard.setData(ClipboardData(text: text)); Clipboard.setData(ClipboardData(text: text));
showSnackBar(s.textCopiedToClipboard(title)); showSnackBar(s.textCopiedToClipboard(title));
Navigator.of(context).pop(); Navigator.of(context).pop();
}, icon: Icon(Icons.copy), label: Text(s.copy)), },
ElevatedButton.icon(onPressed: () => saveQRImage(text, title), icon: Icon(Icons.copy),
icon: Icon(Icons.save), label: Text(s.save)) label: Text(s.copy)),
ElevatedButton.icon(
onPressed: () => saveQRImage(text, title),
icon: Icon(Icons.save),
label: Text(s.save))
]) ])
]))) ]))));
); }); });
} }
Future<void> saveQRImage(String data, String title) async { Future<void> saveQRImage(String data, String title) async {
final code = QrCode.fromData(data: data, errorCorrectLevel: QrErrorCorrectLevel.L); final code =
QrCode.fromData(data: data, errorCorrectLevel: QrErrorCorrectLevel.L);
code.make(); code.make();
final painter = QrPainter.withQr(qr: code, emptyColor: Colors.white, gapless: true); final painter =
QrPainter.withQr(qr: code, emptyColor: Colors.white, gapless: true);
final recorder = PictureRecorder(); final recorder = PictureRecorder();
final canvas = Canvas(recorder); final canvas = Canvas(recorder);
final size = code.moduleCount * 32; final size = code.moduleCount * 32;
final whitePaint = Paint()..color = Colors.white..style = PaintingStyle.fill; final whitePaint = Paint()
..color = Colors.white
..style = PaintingStyle.fill;
canvas.drawRect(Rect.fromLTWH(0, 0, size + 256, size + 256), whitePaint); canvas.drawRect(Rect.fromLTWH(0, 0, size + 256, size + 256), whitePaint);
canvas.translate(128, 128); canvas.translate(128, 128);
painter.paint(canvas, Size(size.toDouble(), size.toDouble())); painter.paint(canvas, Size(size.toDouble(), size.toDouble()));
final image = await recorder.endRecording().toImage(size + 256, size + 256); final image = await recorder.endRecording().toImage(size + 256, size + 256);
final ByteData? byteData = await image.toByteData(format: ImageByteFormat.png); final ByteData? byteData =
await image.toByteData(format: ImageByteFormat.png);
final Uint8List pngBytes = byteData!.buffer.asUint8List(); final Uint8List pngBytes = byteData!.buffer.asUint8List();
await saveFileBinary(pngBytes, 'qr.png', title); await saveFileBinary(pngBytes, 'qr.png', title);
} }
Future<bool> showMessageBox(BuildContext context, String title, String content, Future<bool> showMessageBox(
String label) async { BuildContext context, String title, String content, String label) async {
final confirm = await showDialog<bool>( final confirm = await showDialog<bool>(
context: context, context: context,
barrierDismissible: false, barrierDismissible: false,
builder: (context) => builder: (context) => AlertDialog(
AlertDialog(
title: Text(title), title: Text(title),
content: Text(content), content: Text(content),
actions: confirmButtons(context, () { actions: confirmButtons(context, () {
@ -608,9 +628,7 @@ Future<bool> showMessageBox(BuildContext context, String title, String content,
} }
double getScreenSize(BuildContext context) { double getScreenSize(BuildContext context) {
final size = MediaQuery final size = MediaQuery.of(context).size;
.of(context)
.size;
return min(size.height, size.width); return min(size.height, size.width);
} }
@ -650,8 +668,7 @@ int stringToAmount(String? s) {
bool checkNumber(String s) { bool checkNumber(String s) {
try { try {
NumberFormat.currency().parse(s); NumberFormat.currency().parse(s);
} } on FormatException {
on FormatException {
return false; return false;
} }
return true; return true;
@ -664,8 +681,7 @@ Future<String?> scanCode(BuildContext context) async {
if (!isMobile()) { if (!isMobile()) {
final code = await pickDecodeQRImage(); final code = await pickDecodeQRImage();
return code; return code;
} } else {
else {
final f = Completer(); final f = Completer();
Navigator.of(context).pushNamed('/scanner', arguments: { Navigator.of(context).pushNamed('/scanner', arguments: {
'onScan': (String code) { 'onScan': (String code) {
@ -679,19 +695,21 @@ Future<String?> scanCode(BuildContext context) async {
} }
String centerTrim(String v) => String centerTrim(String v) =>
v.length >= 16 ? v.substring(0, 8) + "..." + v.length >= 16 ? v.substring(0, 8) + "..." + v.substring(v.length - 16) : v;
v.substring(v.length - 16) : v;
String trailing(String v, int n) { String trailing(String v, int n) {
final len = min(n, v.length); final len = min(n, v.length);
return v.substring(v.length - len); return v.substring(v.length - len);
} }
void showSnackBar(String msg, { bool autoClose = false, bool quick = false }) { void showSnackBar(String msg, {bool autoClose = false, bool quick = false}) {
final duration = quick ? Duration(seconds: 1) : Duration(seconds: 4); final duration = quick ? Duration(seconds: 1) : Duration(seconds: 4);
final snackBar = SnackBar(content: SelectableText(msg), final snackBar = SnackBar(
content: SelectableText(msg),
duration: autoClose ? duration : Duration(minutes: 1), duration: autoClose ? duration : Duration(minutes: 1),
action: SnackBarAction(label: S.current.close, onPressed: () { action: SnackBarAction(
label: S.current.close,
onPressed: () {
rootScaffoldMessengerKey.currentState?.hideCurrentSnackBar(); rootScaffoldMessengerKey.currentState?.hideCurrentSnackBar();
})); }));
rootScaffoldMessengerKey.currentState?.showSnackBar(snackBar); rootScaffoldMessengerKey.currentState?.showSnackBar(snackBar);
@ -700,8 +718,7 @@ void showSnackBar(String msg, { bool autoClose = false, bool quick = false }) {
void showBalanceNotification(int prevBalances, int curBalances) { void showBalanceNotification(int prevBalances, int curBalances) {
final s = S.current; final s = S.current;
if (syncStatus.isRescan) return; if (syncStatus.isRescan) return;
if (Platform.isAndroid && if (Platform.isAndroid && prevBalances != curBalances) {
prevBalances != curBalances) {
final amount = (prevBalances - curBalances).abs(); final amount = (prevBalances - curBalances).abs();
final amountStr = amountToString(amount, MAX_PRECISION); final amountStr = amountToString(amount, MAX_PRECISION);
final ticker = active.coinDef.ticker; final ticker = active.coinDef.ticker;
@ -714,8 +731,7 @@ void showBalanceNotification(int prevBalances, int curBalances) {
body: s.received(amountStr, ticker), body: s.received(amountStr, ticker),
actionType: ActionType.Default, actionType: ActionType.Default,
); );
} } else {
else {
content = NotificationContent( content = NotificationContent(
id: 1, id: 1,
channelKey: APP_NAME, channelKey: APP_NAME,
@ -724,25 +740,14 @@ void showBalanceNotification(int prevBalances, int curBalances) {
actionType: ActionType.Default, actionType: ActionType.Default,
); );
} }
AwesomeNotifications().createNotification( AwesomeNotifications().createNotification(content: content);
content: content
);
} }
} }
enum DeviceWidth { enum DeviceWidth { xs, sm, md, lg, xl }
xs,
sm,
md,
lg,
xl
}
DeviceWidth getWidth(BuildContext context) { DeviceWidth getWidth(BuildContext context) {
final width = MediaQuery final width = MediaQuery.of(context).size.width;
.of(context)
.size
.width;
if (width < 600) return DeviceWidth.xs; if (width < 600) return DeviceWidth.xs;
if (width < 960) return DeviceWidth.sm; if (width < 960) return DeviceWidth.sm;
if (width < 1280) return DeviceWidth.md; if (width < 1280) return DeviceWidth.md;
@ -773,11 +778,13 @@ String humanizeDateTime(DateTime datetime) {
return dateString; return dateString;
} }
String decimalFormat(double x, int decimalDigits, { String symbol = '' }) => String decimalFormat(double x, int decimalDigits, {String symbol = ''}) =>
NumberFormat.currency(decimalDigits: decimalDigits, symbol: symbol).format( NumberFormat.currency(decimalDigits: decimalDigits, symbol: symbol)
x).trimRight(); .format(x)
.trimRight();
String amountToString(int amount, int decimalDigits) => decimalFormat(amount / ZECUNIT, decimalDigits); String amountToString(int amount, int decimalDigits) =>
decimalFormat(amount / ZECUNIT, decimalDigits);
DecodedPaymentURI decodeAddress(BuildContext context, String? v) { DecodedPaymentURI decodeAddress(BuildContext context, String? v) {
final s = S.of(context); final s = S.of(context);
@ -789,8 +796,7 @@ DecodedPaymentURI decodeAddress(BuildContext context, String? v) {
final json = WarpApi.parsePaymentURI(v); final json = WarpApi.parsePaymentURI(v);
final payment = DecodedPaymentURI.fromJson(jsonDecode(json)); final payment = DecodedPaymentURI.fromJson(jsonDecode(json));
return payment; return payment;
} } on String catch (e) {
on String catch (e) {
throw e; throw e;
} }
} }
@ -802,8 +808,7 @@ Future<bool> authenticate(BuildContext context, String reason) async {
final bool didAuthenticate; final bool didAuthenticate;
if (Platform.isAndroid && !await localAuth.canCheckBiometrics) { if (Platform.isAndroid && !await localAuth.canCheckBiometrics) {
didAuthenticate = await KeyGuardmanager.authStatus == "true"; didAuthenticate = await KeyGuardmanager.authStatus == "true";
} } else {
else {
didAuthenticate = await localAuth.authenticate( didAuthenticate = await localAuth.authenticate(
localizedReason: reason, options: AuthenticationOptions()); localizedReason: reason, options: AuthenticationOptions());
} }
@ -814,8 +819,7 @@ Future<bool> authenticate(BuildContext context, String reason) async {
await showDialog( await showDialog(
context: context, context: context,
barrierDismissible: true, barrierDismissible: true,
builder: (context) => builder: (context) => AlertDialog(
AlertDialog(
title: Text(S.of(context).noAuthenticationMethod), title: Text(S.of(context).noAuthenticationMethod),
content: Text(e.message ?? ""))); content: Text(e.message ?? "")));
} }
@ -827,8 +831,7 @@ Future<void> shieldTAddr(BuildContext context) async {
final txPlan = WarpApi.shieldTAddr( final txPlan = WarpApi.shieldTAddr(
active.coin, active.id, active.tbalance, settings.anchorOffset); active.coin, active.id, active.tbalance, settings.anchorOffset);
Navigator.of(context).pushNamed('/txplan', arguments: txPlan); Navigator.of(context).pushNamed('/txplan', arguments: txPlan);
} } on String catch (msg) {
on String catch (msg) {
showSnackBar(msg); showSnackBar(msg);
} }
} }
@ -851,7 +854,8 @@ Future<void> saveFile(String data, String filename, String title) async {
await saveFileBinary(utf8.encode(data), filename, title); await saveFileBinary(utf8.encode(data), filename, title);
} }
Future<void> saveFileBinary(List<int> data, String filename, String title) async { Future<void> saveFileBinary(
List<int> data, String filename, String title) async {
if (isMobile()) { if (isMobile()) {
final context = navigatorKey.currentContext!; final context = navigatorKey.currentContext!;
Size size = MediaQuery.of(context).size; Size size = MediaQuery.of(context).size;
@ -860,10 +864,12 @@ Future<void> saveFileBinary(List<int> data, String filename, String title) async
final xfile = XFile(path); final xfile = XFile(path);
final file = File(path); final file = File(path);
await file.writeAsBytes(data); await file.writeAsBytes(data);
await Share.shareXFiles([xfile], subject: title, sharePositionOrigin: Rect.fromLTWH(0, 0, size.width, size.height / 2)); await Share.shareXFiles([xfile],
} subject: title,
else { sharePositionOrigin: Rect.fromLTWH(0, 0, size.width, size.height / 2));
final fn = await FilePicker.platform.saveFile(dialogTitle: title, fileName: filename); } else {
final fn = await FilePicker.platform
.saveFile(dialogTitle: title, fileName: filename);
if (fn != null) { if (fn != null) {
final file = File(fn); final file = File(fn);
await file.writeAsBytes(data); await file.writeAsBytes(data);
@ -872,15 +878,17 @@ Future<void> saveFileBinary(List<int> data, String filename, String title) async
} }
Future<void> exportFile(BuildContext context, String path, String title) async { Future<void> exportFile(BuildContext context, String path, String title) async {
final confirmed = await showMessageBox(context, title, "Exporting $path", S.of(context).ok); final confirmed =
await showMessageBox(context, title, "Exporting $path", S.of(context).ok);
if (!confirmed) return; if (!confirmed) return;
if (isMobile()) { if (isMobile()) {
final context = navigatorKey.currentContext!; final context = navigatorKey.currentContext!;
Size size = MediaQuery.of(context).size; Size size = MediaQuery.of(context).size;
final xfile = XFile(path); final xfile = XFile(path);
await Share.shareXFiles([xfile], subject: title, sharePositionOrigin: Rect.fromLTWH(0, 0, size.width, size.height / 2)); await Share.shareXFiles([xfile],
} subject: title,
else { sharePositionOrigin: Rect.fromLTWH(0, 0, size.width, size.height / 2));
} else {
final fn = await FilePicker.platform.saveFile(); final fn = await FilePicker.platform.saveFile();
if (fn != null) { if (fn != null) {
final file = File(path); final file = File(path);
@ -895,7 +903,8 @@ Future<File> getRecoveryFile() async {
return f; return f;
} }
Future<bool> showConfirmDialog(BuildContext context, String title, String body) async { Future<bool> showConfirmDialog(
BuildContext context, String title, String body) async {
final s = S.of(context); final s = S.of(context);
final confirmation = await showDialog<bool>( final confirmation = await showDialog<bool>(
context: context, context: context,
@ -906,7 +915,8 @@ Future<bool> showConfirmDialog(BuildContext context, String title, String body)
actions: confirmButtons(context, () { actions: confirmButtons(context, () {
Navigator.of(context).pop(true); Navigator.of(context).pop(true);
}, okLabel: s.ok, cancelValue: false)), }, okLabel: s.ok, cancelValue: false)),
) ?? false; ) ??
false;
return confirmation; return confirmation;
} }
@ -925,9 +935,12 @@ void cancelScan(BuildContext context) {
Future<String> getDataPath() async { Future<String> getDataPath() async {
String? home; String? home;
if (Platform.isAndroid) home = (await getApplicationDocumentsDirectory()).parent.path; if (Platform.isAndroid)
home = (await getApplicationDocumentsDirectory()).parent.path;
if (Platform.isWindows) home = Platform.environment['LOCALAPPDATA']; if (Platform.isWindows) home = Platform.environment['LOCALAPPDATA'];
if (Platform.isLinux) home = Platform.environment['XDG_DATA_HOME'] ?? Platform.environment['HOME']; if (Platform.isLinux)
home =
Platform.environment['XDG_DATA_HOME'] ?? Platform.environment['HOME'];
if (Platform.isMacOS) home = Platform.environment['HOME']; if (Platform.isMacOS) home = Platform.environment['HOME'];
final h = home ?? ""; final h = home ?? "";
return h; return h;
@ -953,27 +966,27 @@ Future<String> getDbPath() async {
bool isMobile() => Platform.isAndroid || Platform.isIOS; bool isMobile() => Platform.isAndroid || Platform.isIOS;
class NotificationController { class NotificationController {
/// Use this method to detect when a new notification or a schedule is created /// Use this method to detect when a new notification or a schedule is created
@pragma("vm:entry-point") @pragma("vm:entry-point")
static Future <void> onNotificationCreatedMethod(ReceivedNotification receivedNotification) async { static Future<void> onNotificationCreatedMethod(
} ReceivedNotification receivedNotification) async {}
/// Use this method to detect every time that a new notification is displayed /// Use this method to detect every time that a new notification is displayed
@pragma("vm:entry-point") @pragma("vm:entry-point")
static Future <void> onNotificationDisplayedMethod(ReceivedNotification receivedNotification) async { static Future<void> onNotificationDisplayedMethod(
ReceivedNotification receivedNotification) async {
FlutterRingtonePlayer.playNotification(); FlutterRingtonePlayer.playNotification();
} }
/// Use this method to detect if the user dismissed a notification /// Use this method to detect if the user dismissed a notification
@pragma("vm:entry-point") @pragma("vm:entry-point")
static Future <void> onDismissActionReceivedMethod(ReceivedAction receivedAction) async { static Future<void> onDismissActionReceivedMethod(
} ReceivedAction receivedAction) async {}
/// Use this method to detect when the user taps on a notification or action button /// Use this method to detect when the user taps on a notification or action button
@pragma("vm:entry-point") @pragma("vm:entry-point")
static Future <void> onActionReceivedMethod(ReceivedAction receivedAction) async { static Future<void> onActionReceivedMethod(
} ReceivedAction receivedAction) async {}
} }
void resetApp() { void resetApp() {
@ -999,17 +1012,22 @@ Future<bool> getDbPasswd(BuildContext context, String dbPath) async {
return AlertDialog( return AlertDialog(
content: Container( content: Container(
width: double.maxFinite, width: double.maxFinite,
child: SingleChildScrollView(child: Form(key: formKey, child: Column(children: [ child: SingleChildScrollView(
child: Form(
key: formKey,
child: Column(children: [
TextFormField( TextFormField(
decoration: InputDecoration(labelText: s.databasePassword), decoration: InputDecoration(
labelText: s.databasePassword),
controller: passwdController, controller: passwdController,
validator: checkPasswd, validator: checkPasswd,
onSaved: (v) { settings.dbPasswd = v!; }, onSaved: (v) {
settings.dbPasswd = v!;
},
obscureText: true, obscureText: true,
), ),
])))), ])))),
actions: actions: <ElevatedButton>[
<ElevatedButton>[
ElevatedButton.icon( ElevatedButton.icon(
icon: Icon(Icons.lock_reset), icon: Icon(Icons.lock_reset),
label: Text(s.reset), label: Text(s.reset),
@ -1025,7 +1043,8 @@ Future<bool> getDbPasswd(BuildContext context, String dbPath) async {
} }
}) })
]); ]);
}) ?? false; }) ??
false;
return reset; return reset;
} }

View File

@ -5,6 +5,9 @@ import 'generated/l10n.dart';
import 'main.dart'; import 'main.dart';
class ScanTAddrPage extends StatefulWidget { class ScanTAddrPage extends StatefulWidget {
final int gapLimit;
ScanTAddrPage(this.gapLimit);
@override @override
ScanTAddrPageState createState() => ScanTAddrPageState(); ScanTAddrPageState createState() => ScanTAddrPageState();
} }
@ -16,7 +19,8 @@ class ScanTAddrPageState extends State<ScanTAddrPage> {
void initState() { void initState() {
super.initState(); super.initState();
Future(() async { Future(() async {
final _addresses = await WarpApi.scanTransparentAccounts(active.coin, active.id, settings.gapLimit); final _addresses = await WarpApi.scanTransparentAccounts(
active.coin, active.id, widget.gapLimit);
setState(() { setState(() {
addresses = _addresses; addresses = _addresses;
}); });
@ -25,23 +29,25 @@ class ScanTAddrPageState extends State<ScanTAddrPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = S.of(context);
final _addresses = addresses; final _addresses = addresses;
return Scaffold( return Scaffold(
appBar: AppBar(title: Text('Scan Transparent Addresses')), appBar: AppBar(title: Text(s.scanTransparentAddresses)),
body: (_addresses == null) ? body: (_addresses == null)
Center(child: Text('Scanning addresses')) ? Center(child: Text(s.scanningAddresses))
: Column( : Column(children: [
children: [ Expanded(
Expanded(child: ListView.builder( child: ListView.builder(
itemCount: _addresses.length, itemCount: _addresses.length,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
final a = _addresses[index]; final a = _addresses[index];
return ListTile(title: Text(a.address!), subtitle: Text(amountToString(a.balance, MAX_PRECISION))); return ListTile(
title: Text(a.address!),
subtitle: Text(
amountToString(a.balance, MAX_PRECISION)));
})), })),
ButtonBar(children: confirmButtons(this.context, onPressed)) ButtonBar(children: confirmButtons(this.context, onPressed))
] ]));
)
);
} }
onPressed() async { onPressed() async {
@ -54,4 +60,3 @@ class ScanTAddrPageState extends State<ScanTAddrPage> {
Navigator.of(context).pop(); Navigator.of(context).pop();
} }
} }

View File

@ -36,7 +36,6 @@ class SettingsState extends State<SettingsPage>
var _anchorController = var _anchorController =
TextEditingController(text: "${settings.anchorOffset}"); TextEditingController(text: "${settings.anchorOffset}");
var _memoController = TextEditingController(); var _memoController = TextEditingController();
var _gapLimitController = TextEditingController(text: "${settings.gapLimit}");
var _currency = settings.currency; var _currency = settings.currency;
var _needAuth = false; var _needAuth = false;
var _messageView = settings.messageView; var _messageView = settings.messageView;
@ -50,7 +49,6 @@ class SettingsState extends State<SettingsPage>
final simpleMode = settings.simpleMode; final simpleMode = settings.simpleMode;
_memoController.text = settings.memoSignature ?? s.sendFrom(APP_NAME); _memoController.text = settings.memoSignature ?? s.sendFrom(APP_NAME);
final hasUA = active.coinDef.supportsUA; final hasUA = active.coinDef.supportsUA;
final uaType = settings.uaType; final uaType = settings.uaType;
List<String> uaList = []; List<String> uaList = [];
@ -153,7 +151,6 @@ class SettingsState extends State<SettingsPage>
}, },
onSaved: _onProtectOpen)), onSaved: _onProtectOpen)),
]), ]),
if (!simpleMode) if (!simpleMode)
FormBuilderCheckbox( FormBuilderCheckbox(
name: 'use_millis', name: 'use_millis',
@ -191,7 +188,8 @@ class SettingsState extends State<SettingsPage>
FormBuilderCheckboxGroup<String>( FormBuilderCheckboxGroup<String>(
orientation: OptionsOrientation.horizontal, orientation: OptionsOrientation.horizontal,
name: 'ua', name: 'ua',
decoration: InputDecoration(labelText: 'Main Address Type'), decoration: InputDecoration(
labelText: 'Main Address Type'),
initialValue: uaList, initialValue: uaList,
onSaved: _onUAType, onSaved: _onUAType,
validator: _checkUA, validator: _checkUA,
@ -209,13 +207,17 @@ class SettingsState extends State<SettingsPage>
FormBuilderRadioGroup<int>( FormBuilderRadioGroup<int>(
orientation: OptionsOrientation.horizontal, orientation: OptionsOrientation.horizontal,
name: 'auto_hide', name: 'auto_hide',
decoration: InputDecoration(labelText: s.autoHideBalance), decoration:
InputDecoration(labelText: s.autoHideBalance),
initialValue: settings.autoHide, initialValue: settings.autoHide,
onSaved: _onAutoHide, onSaved: _onAutoHide,
options: [ options: [
FormBuilderFieldOption(child: Text(s.never), value: 0), FormBuilderFieldOption(
FormBuilderFieldOption(child: Text(s.auto), value: 1), child: Text(s.never), value: 0),
FormBuilderFieldOption(child: Text(s.always), value: 2), FormBuilderFieldOption(
child: Text(s.auto), value: 1),
FormBuilderFieldOption(
child: Text(s.always), value: 2),
]), ]),
DropdownButtonFormField<int>( DropdownButtonFormField<int>(
decoration: decoration:
@ -235,36 +237,55 @@ class SettingsState extends State<SettingsPage>
title: Text(s.includeReplyTo), title: Text(s.includeReplyTo),
initialValue: settings.includeReplyTo, initialValue: settings.includeReplyTo,
onSaved: _onIncludeReplyTo), onSaved: _onIncludeReplyTo),
if (!simpleMode) Row(children: [ if (!simpleMode)
Expanded(child: DropdownButtonFormField<ViewStyle>( Row(children: [
decoration: InputDecoration(labelText: s.messages), Expanded(
child: DropdownButtonFormField<ViewStyle>(
decoration: InputDecoration(
labelText: s.messages),
value: _messageView, value: _messageView,
items: ViewStyle.values.map((v) => DropdownMenuItem( items: ViewStyle.values
child: Text(v.name), value: v)).toList(), .map((v) => DropdownMenuItem(
child: Text(v.name), value: v))
.toList(),
onChanged: (v) { onChanged: (v) {
setState(() { _messageView = v!; }); setState(() {
_messageView = v!;
});
}, },
onSaved: (_) { onSaved: (_) {
settings.setMessageView(_messageView); settings.setMessageView(_messageView);
})), })),
Expanded(child: DropdownButtonFormField<ViewStyle>( Expanded(
decoration: InputDecoration(labelText: s.notes), child: DropdownButtonFormField<ViewStyle>(
decoration:
InputDecoration(labelText: s.notes),
value: _noteView, value: _noteView,
items: ViewStyle.values.map((v) => DropdownMenuItem( items: ViewStyle.values
child: Text(v.name), value: v)).toList(), .map((v) => DropdownMenuItem(
child: Text(v.name), value: v))
.toList(),
onChanged: (v) { onChanged: (v) {
setState(() { _noteView = v!; }); setState(() {
_noteView = v!;
});
}, },
onSaved: (_) { onSaved: (_) {
settings.setNoteView(_noteView); settings.setNoteView(_noteView);
})), })),
Expanded(child: DropdownButtonFormField<ViewStyle>( Expanded(
decoration: InputDecoration(labelText: s.transactions), child: DropdownButtonFormField<ViewStyle>(
decoration: InputDecoration(
labelText: s.transactions),
value: _txView, value: _txView,
items: ViewStyle.values.map((v) => DropdownMenuItem( items: ViewStyle.values
child: Text(v.name), value: v)).toList(), .map((v) => DropdownMenuItem(
child: Text(v.name), value: v))
.toList(),
onChanged: (v) { onChanged: (v) {
setState(() { _txView = v!; }); setState(() {
_txView = v!;
});
}, },
onSaved: (_) { onSaved: (_) {
settings.setTxView(_txView); settings.setTxView(_txView);
@ -295,14 +316,6 @@ class SettingsState extends State<SettingsPage>
name: 'memo', name: 'memo',
controller: _memoController, controller: _memoController,
onSaved: _onMemo), onSaved: _onMemo),
if (!simpleMode)
FormBuilderTextField(
decoration:
InputDecoration(labelText: s.gapLimit),
name: 'gap_limit',
keyboardType: TextInputType.number,
controller: _gapLimitController,
onSaved: _onGapLimit),
Padding(padding: EdgeInsets.symmetric(vertical: 8)), Padding(padding: EdgeInsets.symmetric(vertical: 8)),
ButtonBar(children: confirmButtons(context, _onSave)) ButtonBar(children: confirmButtons(context, _onSave))
])))))); ]))))));
@ -345,9 +358,15 @@ class SettingsState extends State<SettingsPage>
int r = 0; int r = 0;
for (var v in vs) { for (var v in vs) {
switch (v) { switch (v) {
case 'T': r |= 1; break; case 'T':
case 'S': r |= 2; break; r |= 1;
case 'O': r |= 4; break; break;
case 'S':
r |= 2;
break;
case 'O':
r |= 4;
break;
} }
} }
return r; return r;
@ -393,10 +412,6 @@ class SettingsState extends State<SettingsPage>
settings.setAnchorOffset(int.parse(v)); settings.setAnchorOffset(int.parse(v));
} }
_onGapLimit(v) {
settings.setGapLimit(int.parse(v));
}
_onGetTx(v) { _onGetTx(v) {
settings.updateGetTx(v); settings.updateGetTx(v);
} }

View File

@ -173,9 +173,6 @@ abstract class _Settings with Store {
@observable @observable
bool qrOffline = true; bool qrOffline = true;
@observable
int gapLimit = 10;
@observable @observable
int primaryColorValue = 0; int primaryColorValue = 0;
@observable @observable
@ -244,7 +241,6 @@ abstract class _Settings with Store {
messageView = ViewStyle.values[(prefs.getInt('message_view') ?? 0)]; messageView = ViewStyle.values[(prefs.getInt('message_view') ?? 0)];
noteView = ViewStyle.values[(prefs.getInt('note_view') ?? 0)]; noteView = ViewStyle.values[(prefs.getInt('note_view') ?? 0)];
txView = ViewStyle.values[(prefs.getInt('tx_view') ?? 0)]; txView = ViewStyle.values[(prefs.getInt('tx_view') ?? 0)];
gapLimit = prefs.getInt('gap_limit') ?? 10;
qrOffline = prefs.getBool('qr_offline') ?? true; qrOffline = prefs.getBool('qr_offline') ?? true;
primaryColorValue = prefs.getInt('primary') ?? Colors.blue.value; primaryColorValue = prefs.getInt('primary') ?? Colors.blue.value;
@ -347,13 +343,6 @@ abstract class _Settings with Store {
WarpApi.useGPU(v); WarpApi.useGPU(v);
} }
@action
Future<void> setGapLimit(int _gapLimit) async {
final prefs = await SharedPreferences.getInstance();
gapLimit = _gapLimit;
prefs.setInt('gap_limit', gapLimit);
}
@action @action
Future<void> setTheme(String thm) async { Future<void> setTheme(String thm) async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();

@ -1 +1 @@
Subproject commit 09f2c1093027be54cc316a758a687f9a3f58ad08 Subproject commit ee7672569560ba1367f6449dd7a12347d42402f0

View File

@ -179,9 +179,7 @@ class WarpApi {
} }
static bool validAddress(int coin, String address) { static bool validAddress(int coin, String address) {
return warp_api_lib.valid_address( return warp_api_lib.valid_address(coin, toNative(address)) != 0;
coin, toNative(address)) !=
0;
} }
static String getDiversifiedAddress(int uaType, int time) { static String getDiversifiedAddress(int uaType, int time) {
@ -230,22 +228,34 @@ class WarpApi {
return balance; return balance;
} }
static Future<String> transferPools(int coin, int account, int fromPool, int toPool, static Future<String> transferPools(
int amount, bool includeFee, String memo, int splitAmount, int anchorOffset) async { int coin,
final txId = await compute(transferPoolsIsolateFn, TransferPoolsParams(coin, account, fromPool, toPool, amount, int account,
includeFee, memo, splitAmount, anchorOffset)); int fromPool,
int toPool,
int amount,
bool includeFee,
String memo,
int splitAmount,
int anchorOffset) async {
final txId = await compute(
transferPoolsIsolateFn,
TransferPoolsParams(coin, account, fromPool, toPool, amount, includeFee,
memo, splitAmount, anchorOffset));
return txId; return txId;
} }
static String shieldTAddr(int coin, int account, int amount, int anchorOffset) { static String shieldTAddr(
final txPlan = warp_api_lib.shield_taddr(coin, account, amount, anchorOffset); int coin, int account, int amount, int anchorOffset) {
final txPlan =
warp_api_lib.shield_taddr(coin, account, amount, anchorOffset);
return unwrapResultString(txPlan); return unwrapResultString(txPlan);
} }
static Future<List<AddressBalance>> scanTransparentAccounts( static Future<List<AddressBalance>> scanTransparentAccounts(
int coin, int account, int gapLimit) async { int coin, int account, int gapLimit) async {
return await compute(scanTransparentAccountsParamsIsolateFn, return await compute(scanTransparentAccountsParamsIsolateFn,
ScanTransparentAccountsParams(gapLimit)); {'coin': coin, 'account': account, 'gapLimit': gapLimit});
} }
static Future<String> prepareTx(int coin, int account, static Future<String> prepareTx(int coin, int account,
@ -255,25 +265,25 @@ class WarpApi {
int root = RecipientsT(values: rs).pack(builder); int root = RecipientsT(values: rs).pack(builder);
builder.finish(root); builder.finish(root);
return await compute((_) { return await compute((_) {
final res = warp_api_lib.prepare_multi_payment( final res = warp_api_lib.prepare_multi_payment(coin, account,
coin, account, toNativeBytes(builder.buffer), builder.size(), anchorOffset);
toNativeBytes(builder.buffer),
builder.size(),
anchorOffset);
final json = unwrapResultString(res); final json = unwrapResultString(res);
return json; return json;
}, null); }, null);
} }
static TxReport transactionReport(int coin, String plan) { static TxReport transactionReport(int coin, String plan) {
final data = unwrapResultBytes(warp_api_lib.transaction_report(coin, toNative(plan))); final data = unwrapResultBytes(
warp_api_lib.transaction_report(coin, toNative(plan)));
final report = TxReport(data); final report = TxReport(data);
return report; return report;
} }
static Future<String> signAndBroadcast (int coin, int account, String plan) async { static Future<String> signAndBroadcast(
int coin, int account, String plan) async {
return await compute((_) { return await compute((_) {
final txid = warp_api_lib.sign_and_broadcast(coin, account, plan.toNativeUtf8().cast<Int8>()); final txid = warp_api_lib.sign_and_broadcast(
coin, account, plan.toNativeUtf8().cast<Int8>());
return unwrapResultString(txid); return unwrapResultString(txid);
}, null); }, null);
} }
@ -285,8 +295,8 @@ class WarpApi {
f(progress); f(progress);
}); });
return await compute( return await compute(signOnlyIsolateFn,
signOnlyIsolateFn, SignOnlyParams(coin, account, tx, receivePort.sendPort)); SignOnlyParams(coin, account, tx, receivePort.sendPort));
} }
static String broadcast(String txStr) { static String broadcast(String txStr) {
@ -298,9 +308,11 @@ class WarpApi {
return warp_api_lib.is_valid_tkey(toNative(key)) != 0; return warp_api_lib.is_valid_tkey(toNative(key)) != 0;
} }
static Future<String> sweepTransparent(int latestHeight, String sk, int pool, int confirmations) async { static Future<String> sweepTransparent(
int latestHeight, String sk, int pool, int confirmations) async {
return await compute((_) { return await compute((_) {
final txid = warp_api_lib.sweep_tkey(latestHeight, toNative(sk), pool, confirmations); final txid = warp_api_lib.sweep_tkey(
latestHeight, toNative(sk), pool, confirmations);
return unwrapResultString(txid); return unwrapResultString(txid);
}, null); }, null);
} }
@ -344,7 +356,8 @@ class WarpApi {
} }
static String commitUnsavedContacts(int anchorOffset) { static String commitUnsavedContacts(int anchorOffset) {
return unwrapResultString(warp_api_lib.commit_unsaved_contacts(anchorOffset)); return unwrapResultString(
warp_api_lib.commit_unsaved_contacts(anchorOffset));
} }
static void markMessageAsRead(int messageId, bool read) { static void markMessageAsRead(int messageId, bool read) {
@ -367,12 +380,10 @@ class WarpApi {
warp_api_lib.delete_account(coin, account); warp_api_lib.delete_account(coin, account);
} }
static String makePaymentURI(int coin, String address, int amount, String memo) { static String makePaymentURI(
int coin, String address, int amount, String memo) {
final uri = warp_api_lib.make_payment_uri( final uri = warp_api_lib.make_payment_uri(
coin, coin, toNative(address), amount, memo.toNativeUtf8().cast<Int8>());
toNative(address),
amount,
memo.toNativeUtf8().cast<Int8>());
return unwrapResultString(uri); return unwrapResultString(uri);
} }
@ -394,7 +405,8 @@ class WarpApi {
} }
static void unzipBackup(String key, String path, String tmpDir) { static void unzipBackup(String key, String path, String tmpDir) {
unwrapResultU8(warp_api_lib.unzip_backup(toNative(key), toNative(path), toNative(tmpDir))); unwrapResultU8(warp_api_lib.unzip_backup(
toNative(key), toNative(path), toNative(tmpDir)));
} }
static List<String> splitData(int id, String data) { static List<String> splitData(int id, String data) {
@ -477,7 +489,8 @@ class WarpApi {
} }
static Balance getBalance(int coin, int id, int confirmedHeight) { static Balance getBalance(int coin, int id, int confirmedHeight) {
final r = unwrapResultBytes(warp_api_lib.get_balances(coin, id, confirmedHeight)); final r =
unwrapResultBytes(warp_api_lib.get_balances(coin, id, confirmedHeight));
final b = Balance(r); final b = Balance(r);
return b; return b;
} }
@ -507,8 +520,10 @@ class WarpApi {
return msgs.messages!; return msgs.messages!;
} }
static PrevNext getPrevNextMessage(int coin, int id, String subject, int height) { static PrevNext getPrevNextMessage(
final r = unwrapResultBytes(warp_api_lib.get_prev_next_message(coin, id, toNative(subject), height)); int coin, int id, String subject, int height) {
final r = unwrapResultBytes(warp_api_lib.get_prev_next_message(
coin, id, toNative(subject), height));
final pn = PrevNext(r); final pn = PrevNext(r);
return pn; return pn;
} }
@ -535,7 +550,8 @@ class WarpApi {
print("templ $t"); print("templ $t");
final data = toNativeBytes(template); final data = toNativeBytes(template);
return unwrapResultU32(warp_api_lib.save_send_template(coin, data, template.length)); return unwrapResultU32(
warp_api_lib.save_send_template(coin, data, template.length));
} }
static void deleteSendTemplate(int coin, int id) { static void deleteSendTemplate(int coin, int id) {
@ -555,13 +571,15 @@ class WarpApi {
} }
static List<Quote> getQuotes(int coin, int timestamp, String currency) { static List<Quote> getQuotes(int coin, int timestamp, String currency) {
final r = unwrapResultBytes(warp_api_lib.get_historical_prices(coin, timestamp, toNative(currency))); final r = unwrapResultBytes(warp_api_lib.get_historical_prices(
coin, timestamp, toNative(currency)));
final quotes = QuoteVec(r); final quotes = QuoteVec(r);
return quotes.values!; return quotes.values!;
} }
static List<Spending> getSpendings(int coin, int id, int timestamp) { static List<Spending> getSpendings(int coin, int id, int timestamp) {
final r = unwrapResultBytes(warp_api_lib.get_spendings(coin, id, timestamp)); final r =
unwrapResultBytes(warp_api_lib.get_spendings(coin, id, timestamp));
final quotes = SpendingVec(r); final quotes = SpendingVec(r);
return quotes.values!; return quotes.values!;
} }
@ -585,17 +603,18 @@ class WarpApi {
} }
static void cloneDbWithPasswd(int coin, String tempPath, String passwd) { static void cloneDbWithPasswd(int coin, String tempPath, String passwd) {
warp_api_lib.clone_db_with_passwd(coin, toNative(tempPath), toNative(passwd)); warp_api_lib.clone_db_with_passwd(
coin, toNative(tempPath), toNative(passwd));
} }
static bool decryptDb(String dbPath, String passwd) { static bool decryptDb(String dbPath, String passwd) {
return unwrapResultBool(warp_api_lib.decrypt_db(toNative(dbPath), toNative(passwd))); return unwrapResultBool(
warp_api_lib.decrypt_db(toNative(dbPath), toNative(passwd)));
} }
} }
String signOnlyIsolateFn(SignOnlyParams params) { String signOnlyIsolateFn(SignOnlyParams params) {
final txIdRes = warp_api_lib.sign( final txIdRes = warp_api_lib.sign(params.coin, params.account,
params.coin, params.account,
params.tx.toNativeUtf8().cast<Int8>(), params.port.nativePort); params.tx.toNativeUtf8().cast<Int8>(), params.port.nativePort);
if (txIdRes.error != nullptr) throw convertCString(txIdRes.error); if (txIdRes.error != nullptr) throw convertCString(txIdRes.error);
return convertCString(txIdRes.value); return convertCString(txIdRes.value);
@ -606,8 +625,16 @@ int getLatestHeightIsolateFn(Null n) {
} }
String transferPoolsIsolateFn(TransferPoolsParams params) { String transferPoolsIsolateFn(TransferPoolsParams params) {
final txId = warp_api_lib.transfer_pools(params.coin, params.account, params.fromPool, params.toPool, final txId = warp_api_lib.transfer_pools(
params.amount, params.takeFee ? 1 : 0, toNative(params.memo), params.splitAmount, params.anchorOffset); params.coin,
params.account,
params.fromPool,
params.toPool,
params.amount,
params.takeFee ? 1 : 0,
toNative(params.memo),
params.splitAmount,
params.anchorOffset);
return unwrapResultString(txId); return unwrapResultString(txId);
} }
@ -621,7 +648,8 @@ int syncHistoricalPricesIsolateFn(SyncHistoricalPricesParams params) {
} }
int getTBalanceIsolateFn(GetTBalanceParams params) { int getTBalanceIsolateFn(GetTBalanceParams params) {
return unwrapResultU64(warp_api_lib.get_taddr_balance(params.coin, params.account)); return unwrapResultU64(
warp_api_lib.get_taddr_balance(params.coin, params.account));
} }
int getBlockHeightByTimeIsolateFn(BlockHeightByTimeParams params) { int getBlockHeightByTimeIsolateFn(BlockHeightByTimeParams params) {
@ -629,8 +657,11 @@ int getBlockHeightByTimeIsolateFn(BlockHeightByTimeParams params) {
} }
List<AddressBalance> scanTransparentAccountsParamsIsolateFn( List<AddressBalance> scanTransparentAccountsParamsIsolateFn(
ScanTransparentAccountsParams params) { Map<String, Object> params) {
final r = unwrapResultBytes(warp_api_lib.scan_transparent_accounts(params.gapLimit)); final r = unwrapResultBytes(warp_api_lib.scan_transparent_accounts(
params['coin'] as int,
params['account'] as int,
params['gapLimit'] as int));
final v = AddressBalanceVec(r); final v = AddressBalanceVec(r);
return v.values!; return v.values!;
} }
@ -653,8 +684,8 @@ class PaymentParams {
final int anchorOffset; final int anchorOffset;
final SendPort port; final SendPort port;
PaymentParams( PaymentParams(this.coin, this.account, this.recipientsJson,
this.coin, this.account, this.recipientsJson, this.useTransparent, this.anchorOffset, this.port); this.useTransparent, this.anchorOffset, this.port);
} }
class SignOnlyParams { class SignOnlyParams {
@ -677,8 +708,16 @@ class TransferPoolsParams {
final int splitAmount; final int splitAmount;
final int anchorOffset; final int anchorOffset;
TransferPoolsParams(this.coin, this.account, this.fromPool, this.toPool, this.amount, this.takeFee, this.memo, TransferPoolsParams(
this.splitAmount, this.anchorOffset); this.coin,
this.account,
this.fromPool,
this.toPool,
this.amount,
this.takeFee,
this.memo,
this.splitAmount,
this.anchorOffset);
} }
class SyncHistoricalPricesParams { class SyncHistoricalPricesParams {
@ -700,12 +739,6 @@ class BlockHeightByTimeParams {
BlockHeightByTimeParams(this.time); BlockHeightByTimeParams(this.time);
} }
class ScanTransparentAccountsParams {
final int gapLimit;
ScanTransparentAccountsParams(this.gapLimit);
}
String convertCString(Pointer<Int8> s) { String convertCString(Pointer<Int8> s) {
final str = s.cast<Utf8>().toDartString(); final str = s.cast<Utf8>().toDartString();
warp_api_lib.deallocate_str(s); warp_api_lib.deallocate_str(s);

View File

@ -512,9 +512,13 @@ class NativeLibrary {
_shield_taddr_ptr.asFunction<_dart_shield_taddr>(); _shield_taddr_ptr.asFunction<_dart_shield_taddr>();
CResult______u8 scan_transparent_accounts( CResult______u8 scan_transparent_accounts(
int coin,
int account,
int gap_limit, int gap_limit,
) { ) {
return _scan_transparent_accounts( return _scan_transparent_accounts(
coin,
account,
gap_limit, gap_limit,
); );
} }
@ -1906,10 +1910,14 @@ typedef _dart_shield_taddr = CResult_____c_char Function(
); );
typedef _c_scan_transparent_accounts = CResult______u8 Function( typedef _c_scan_transparent_accounts = CResult______u8 Function(
ffi.Uint8 coin,
ffi.Uint32 account,
ffi.Uint32 gap_limit, ffi.Uint32 gap_limit,
); );
typedef _dart_scan_transparent_accounts = CResult______u8 Function( typedef _dart_scan_transparent_accounts = CResult______u8 Function(
int coin,
int account,
int gap_limit, int gap_limit,
); );

View File

@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.3.6+408 version: 1.3.6+409
environment: environment:
sdk: ">=2.12.0 <3.0.0" sdk: ">=2.12.0 <3.0.0"