data
This commit is contained in:
parent
1bfec064fb
commit
413d9bf43e
|
@ -0,0 +1,3 @@
|
|||
flatc -d -r --gen-object-api data.fbs
|
||||
mv data_fb_generated.dart ../packages/warp_api_ffi/lib
|
||||
mv data_generated.rs ../native/zcash-sync/src/db
|
|
@ -0,0 +1,142 @@
|
|||
namespace fb;
|
||||
|
||||
table Account {
|
||||
id:uint32;
|
||||
name:string;
|
||||
balance:uint64;
|
||||
}
|
||||
|
||||
table AccountVec {
|
||||
accounts:[Account];
|
||||
}
|
||||
|
||||
table Balance {
|
||||
shielded:uint64;
|
||||
unconfirmed_spent:uint64;
|
||||
balance:uint64;
|
||||
under_confirmed:uint64;
|
||||
excluded:uint64;
|
||||
sapling:uint64;
|
||||
orchard:uint64;
|
||||
}
|
||||
|
||||
table Height {
|
||||
height:uint32;
|
||||
timestamp:uint32;
|
||||
}
|
||||
|
||||
table ShieldedNote {
|
||||
id:uint32;
|
||||
height:uint32;
|
||||
value:uint64;
|
||||
timestamp:uint32;
|
||||
orchard:bool;
|
||||
excluded:bool;
|
||||
spent:bool;
|
||||
}
|
||||
|
||||
table ShieldedNoteVec {
|
||||
notes:[ShieldedNote];
|
||||
}
|
||||
|
||||
table ShieldedTx {
|
||||
id:uint32;
|
||||
tx_id:string;
|
||||
height:uint32;
|
||||
short_tx_id:string;
|
||||
timestamp:uint32;
|
||||
name:string;
|
||||
value:uint64;
|
||||
address:string;
|
||||
memo:string;
|
||||
}
|
||||
|
||||
table ShieldedTxVec {
|
||||
txs:[ShieldedTx];
|
||||
}
|
||||
|
||||
table Message {
|
||||
id_msg:uint32;
|
||||
id_tx:uint32;
|
||||
height:uint32;
|
||||
timestamp:uint32;
|
||||
from:string;
|
||||
to:string;
|
||||
subject:string;
|
||||
body:string;
|
||||
read:bool;
|
||||
incoming:bool;
|
||||
}
|
||||
|
||||
table MessageVec {
|
||||
messages:[Message];
|
||||
}
|
||||
|
||||
table PrevNext {
|
||||
prev:uint32;
|
||||
next:uint32;
|
||||
}
|
||||
|
||||
table SendTemplate {
|
||||
id:uint32;
|
||||
title:string;
|
||||
address:string;
|
||||
amount:uint64;
|
||||
fiat_amount:double;
|
||||
fee_included:bool;
|
||||
fiat:string;
|
||||
include_reply_to:bool;
|
||||
subject:string;
|
||||
body:string;
|
||||
}
|
||||
|
||||
table SendTemplateVec {
|
||||
templates:[SendTemplate];
|
||||
}
|
||||
|
||||
table Contact {
|
||||
id:uint32;
|
||||
name:string;
|
||||
address:string;
|
||||
}
|
||||
|
||||
table ContactVec {
|
||||
contacts:[Contact];
|
||||
}
|
||||
|
||||
table TxTimeValue {
|
||||
timestamp:uint32;
|
||||
value:uint64;
|
||||
}
|
||||
|
||||
table TxTimeValueVec {
|
||||
values:[TxTimeValue];
|
||||
}
|
||||
|
||||
table Quote {
|
||||
timestamp:uint32;
|
||||
price:double;
|
||||
}
|
||||
|
||||
table QuoteVec {
|
||||
values:[Quote];
|
||||
}
|
||||
|
||||
table Spending {
|
||||
recipient:string;
|
||||
amount:uint64;
|
||||
}
|
||||
|
||||
table SpendingVec {
|
||||
values:[Spending];
|
||||
}
|
||||
|
||||
table AddressBalance {
|
||||
index:uint32;
|
||||
address:string;
|
||||
balance:uint64;
|
||||
}
|
||||
|
||||
table AddressBalanceVec {
|
||||
values:[AddressBalance];
|
||||
}
|
|
@ -23,10 +23,9 @@ class AccountManagerState extends State<AccountManagerPage> {
|
|||
initState() {
|
||||
super.initState();
|
||||
Future.microtask(() async {
|
||||
await _accounts.refresh();
|
||||
setState(() {});
|
||||
await _accounts.updateTBalance();
|
||||
setState(() {});
|
||||
if (mounted)
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -160,7 +159,7 @@ class AccountManagerState extends State<AccountManagerPage> {
|
|||
|
||||
void _onDismissed(int index, Account account) async {
|
||||
await _accounts.delete(account.coin, account.id);
|
||||
await _accounts.refresh();
|
||||
_accounts.refresh();
|
||||
await active.checkAndUpdate();
|
||||
}
|
||||
|
||||
|
@ -247,7 +246,7 @@ class AccountManagerState extends State<AccountManagerPage> {
|
|||
}
|
||||
|
||||
_refresh() async {
|
||||
await _accounts.refresh();
|
||||
_accounts.refresh();
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:warp_api/data_fb_generated.dart' ;
|
||||
import 'package:warp_api/types.dart';
|
||||
import 'coin/coins.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
@ -19,25 +20,29 @@ class Account {
|
|||
final String name;
|
||||
final int balance;
|
||||
int tbalance = 0;
|
||||
final ShareInfo? share;
|
||||
|
||||
Account(this.coin, this.id, this.name, this.balance, this.tbalance, this.share);
|
||||
Account(this.coin, this.id, this.name, this.balance, this.tbalance);
|
||||
|
||||
String get address {
|
||||
return id != 0 ? WarpApi.getAddress(this.coin, this.id, settings.uaType) : "";
|
||||
}
|
||||
}
|
||||
|
||||
final Account emptyAccount = Account(0, 0, "", 0, 0, null);
|
||||
final Account emptyAccount = Account(0, 0, "", 0, 0);
|
||||
|
||||
class AccountList {
|
||||
List<Account> list = [];
|
||||
|
||||
Future<void> refresh() async {
|
||||
AccountList() {
|
||||
refresh();
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
List<Account> _list = [];
|
||||
for (var coin in coins) {
|
||||
final dbr = DbReader(coin.coin, 0);
|
||||
_list.addAll(await dbr.getAccountList());
|
||||
_list.addAll(WarpApi.getAccountList(coin.coin).map((a) => Account(
|
||||
coin.coin, a.id, a.name!, a.balance, 0)
|
||||
));
|
||||
}
|
||||
list = _list;
|
||||
}
|
||||
|
@ -57,9 +62,8 @@ class AccountList {
|
|||
}
|
||||
|
||||
Future<void> changeAccountName(int coin, int id, String name) async {
|
||||
final dbr = DbReader(coin, id);
|
||||
await dbr.changeAccountName(name);
|
||||
await refresh();
|
||||
WarpApi.updateAccountName(coin, id, name);
|
||||
refresh();
|
||||
}
|
||||
|
||||
void saveActive(int coin, int id) {
|
||||
|
@ -141,13 +145,13 @@ abstract class _ActiveAccount with Store {
|
|||
}
|
||||
|
||||
Future<AccountId?> getAvailableId(AccountId aid) async {
|
||||
final nid = await getAvailableIdForCoin(aid.coin, aid.id);
|
||||
if (nid != null) return nid;
|
||||
final nid = getAvailableIdForCoin(aid.coin, aid.id);
|
||||
if (nid.id != 0) return nid;
|
||||
for (var coin_data in settings.coins) {
|
||||
// look for an account in any other coin
|
||||
if (coin_data.coin != coin) {
|
||||
final nid = await getAvailableIdForCoin(coin_data.coin, coin_data.active);
|
||||
if (nid != null)
|
||||
final nid = getAvailableIdForCoin(coin_data.coin, coin_data.active);
|
||||
if (nid.id != 0)
|
||||
return nid;
|
||||
}
|
||||
}
|
||||
|
@ -158,9 +162,9 @@ abstract class _ActiveAccount with Store {
|
|||
// check that the account still exists
|
||||
// if not, pick any account
|
||||
// if there are none, return 0
|
||||
Future<AccountId?> getAvailableIdForCoin(int coin, int id) async {
|
||||
final dbr = DbReader(coin, id);
|
||||
return dbr.getAvailableAccountId();
|
||||
AccountId getAvailableIdForCoin(int coin, int id) {
|
||||
final newId = WarpApi.getAvailableAccountId(coin, id);
|
||||
return AccountId(coin, newId);
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -178,15 +182,14 @@ abstract class _ActiveAccount with Store {
|
|||
Future<void> refreshAccount() async {
|
||||
final dbr = DbReader(coin, id);
|
||||
coinDef = settings.coins[coin].def;
|
||||
final db = coinDef.db;
|
||||
|
||||
final accounts = AccountList();
|
||||
await accounts.refresh();
|
||||
accounts.refresh();
|
||||
account = accounts.get(coin, id);
|
||||
|
||||
if (id > 0) {
|
||||
taddress = await dbr.getTAddr();
|
||||
canPay = await dbr.getSK() != null;
|
||||
taddress = WarpApi.getTAddr(coin, id);
|
||||
canPay = WarpApi.getSK(coin, id).isNotEmpty;
|
||||
}
|
||||
|
||||
showTAddr = false;
|
||||
|
@ -199,8 +202,7 @@ abstract class _ActiveAccount with Store {
|
|||
|
||||
@action
|
||||
Future<void> refreshTAddr() async {
|
||||
final dbr = DbReader(coin, id);
|
||||
taddress = await dbr.getTAddr();
|
||||
taddress = WarpApi.getTAddr(coin, id);
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -217,11 +219,12 @@ abstract class _ActiveAccount with Store {
|
|||
}
|
||||
|
||||
@action
|
||||
Future<void> updateBalances() async {
|
||||
void updateBalances() {
|
||||
final dbr = DbReader(coin, id);
|
||||
final initialized = balances.initialized;
|
||||
final prevBalance = balances.balance;
|
||||
await dbr.updateBalances(syncStatus.confirmHeight, balances);
|
||||
final b = WarpApi.getBalance(coin, id, syncStatus.confirmHeight);
|
||||
balances.update(b.balance, b.shielded, b.unconfirmedSpent, b.underConfirmed, b.excluded);
|
||||
if (initialized && prevBalance != balances.balance) {
|
||||
showBalanceNotification(prevBalance, balances.balance);
|
||||
}
|
||||
|
@ -238,13 +241,13 @@ abstract class _ActiveAccount with Store {
|
|||
|
||||
@action
|
||||
Future<void> update() async {
|
||||
await updateBalances();
|
||||
updateBalances();
|
||||
updateTBalance();
|
||||
await poolBalances.update();
|
||||
poolBalances.update();
|
||||
|
||||
final dbr = DbReader(coin, id);
|
||||
notes = await dbr.getNotes();
|
||||
txs = await dbr.getTxs();
|
||||
notes = dbr.getNotes();
|
||||
txs = dbr.getTxs();
|
||||
messages = ObservableList.of(await dbr.getMessages());
|
||||
unread = messages.where((m) => !m.read).length;
|
||||
dataEpoch += 1;
|
||||
|
@ -342,26 +345,22 @@ abstract class _ActiveAccount with Store {
|
|||
}
|
||||
|
||||
@action
|
||||
Future<void> excludeNote(Note note) async {
|
||||
await coinDef.db.execute(
|
||||
"UPDATE received_notes SET excluded = ?2 WHERE id_note = ?1",
|
||||
[note.id, note.excluded]);
|
||||
void excludeNote(Note note) {
|
||||
WarpApi.updateExcluded(coin, note.id, note.excluded);
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> invertExcludedNotes() async {
|
||||
await coinDef.db.execute(
|
||||
"UPDATE received_notes SET excluded = NOT(COALESCE(excluded, 0)) WHERE account = ?1",
|
||||
[active.id]);
|
||||
void invertExcludedNotes() {
|
||||
WarpApi.invertExcluded(coin, id);
|
||||
notes = notes.map((n) => n.invertExcluded).toList();
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> fetchChartData() async {
|
||||
final dbr = DbReader(active.coin, active.id);
|
||||
pnls = await dbr.getPNL(active.id);
|
||||
spendings = await dbr.getSpending(active.id);
|
||||
accountBalances = await dbr.getAccountBalanceTimeSeries(active.id, active.balances?.balance ?? 0);
|
||||
void fetchChartData() {
|
||||
final dbr = active.dbReader;
|
||||
pnls = dbr.getPNL(active.id);
|
||||
spendings = dbr.getSpending(active.id);
|
||||
accountBalances = dbr.getAccountBalanceTimeSeries(active.id, active.balances?.balance ?? 0);
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -370,7 +369,6 @@ abstract class _ActiveAccount with Store {
|
|||
WarpApi.markMessageAsRead(messages[index].id, true);
|
||||
messages[index] = messages[index].withRead(true);
|
||||
unread = unread - 1;
|
||||
print("UNREAD $unread");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -383,16 +381,16 @@ abstract class _ActiveAccount with Store {
|
|||
unread = 0;
|
||||
}
|
||||
|
||||
Future<int?> prevInThread(int index) async {
|
||||
int prevInThread(int index) {
|
||||
final message = messages[index];
|
||||
final dbr = DbReader(active.coin, active.id);
|
||||
return await dbr.getPrevMessage(message.subject, message.height, id);
|
||||
final pn = WarpApi.getPrevNextMessage(coin, id, message.subject, message.height);
|
||||
return pn.prev;
|
||||
}
|
||||
|
||||
Future<int?> nextInThread(int index) async {
|
||||
int nextInThread(int index) {
|
||||
final message = messages[index];
|
||||
final dbr = DbReader(active.coin, active.id);
|
||||
return await dbr.getNextMessage(message.subject, message.height, id);
|
||||
final pn = WarpApi.getPrevNextMessage(coin, id, message.subject, message.height);
|
||||
return pn.next;
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -441,11 +439,9 @@ abstract class _PoolBalances with Store {
|
|||
@observable int sapling = 0;
|
||||
@observable int orchard = 0;
|
||||
|
||||
Future<void> update() async {
|
||||
final dbr = DbReader(active.coin, active.id);
|
||||
final saplingBalance = await dbr.getSaplingBalance();
|
||||
final orchardBalance = await dbr.getOrchardBalance();
|
||||
_update(active.tbalance, saplingBalance, orchardBalance);
|
||||
void update() {
|
||||
final b = WarpApi.getBalance(active.coin, active.id, syncStatus.confirmHeight);
|
||||
_update(active.tbalance, b.sapling, b.orchard);
|
||||
}
|
||||
|
||||
@action
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:flutter_form_builder/flutter_form_builder.dart';
|
|||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:flutter_palette/flutter_palette.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:warp_api/data_fb_generated.dart';
|
||||
import 'package:warp_api/warp_api.dart';
|
||||
|
||||
import 'chart.dart';
|
||||
|
@ -229,7 +230,7 @@ class BudgetChartState extends State<BudgetChart> {
|
|||
builder: (context) => Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||
child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
|
||||
HorizontalBarChart(active.spendings.map((s) => s.amount).toList()),
|
||||
HorizontalBarChart(active.spendings.map((s) => s.amount / ZECUNIT).toList()),
|
||||
BudgetTable(active.spendings)
|
||||
])));
|
||||
}
|
||||
|
@ -246,16 +247,10 @@ class BudgetTable extends StatelessWidget {
|
|||
final palette = getPalette(Theme.of(context).primaryColor, spendings.length);
|
||||
final rows = spendings.asMap().entries.map((e) {
|
||||
final style = TextStyle(color: palette[e.key], fontFeatures: [FontFeature.tabularFigures()]);
|
||||
final address = e.value.address;
|
||||
final shortAddress = centerTrim(address);
|
||||
final addressOrContact = e.value.contact ?? shortAddress;
|
||||
final recipient = e.value.recipient!;
|
||||
return DataRow(cells: [
|
||||
DataCell(
|
||||
GestureDetector(onTap: () async {
|
||||
await Clipboard.setData(ClipboardData(text: address));
|
||||
showSnackBar(S.of(context).addressCopiedToClipboard);
|
||||
}, child: Text(addressOrContact, style: style))),
|
||||
DataCell(Text(decimalFormat(e.value.amount, 8), style: style)),
|
||||
DataCell(Text(recipient, style: style)),
|
||||
DataCell(Text(decimalFormat(e.value.amount / ZECUNIT, 8), style: style)),
|
||||
]);
|
||||
}).toList();
|
||||
return DataTable(
|
||||
|
|
|
@ -2,11 +2,7 @@ import 'dart:io';
|
|||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
import 'package:warp_api/warp_api.dart';
|
||||
|
||||
import '../main.dart';
|
||||
|
||||
|
@ -30,7 +26,6 @@ abstract class CoinBase {
|
|||
String get dbName;
|
||||
late String dbDir;
|
||||
late String dbFullPath;
|
||||
late Database db;
|
||||
List<LWInstance> get lwd;
|
||||
bool get supportsUA;
|
||||
bool get supportsMultisig;
|
||||
|
@ -41,21 +36,6 @@ abstract class CoinBase {
|
|||
dbFullPath = _getFullPath(dbDir);
|
||||
}
|
||||
|
||||
bool exists() => File(dbFullPath).existsSync();
|
||||
|
||||
Future<void> open(bool delete_wal) async {
|
||||
print("Opening DB ${dbFullPath}, delete_wal = ${delete_wal}");
|
||||
// schema handled in backend
|
||||
db = await openDatabase(dbFullPath, onConfigure: (db) async {
|
||||
if (delete_wal)
|
||||
await db.rawQuery("PRAGMA journal_mode=DELETE");
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> close() async {
|
||||
await db.close();
|
||||
}
|
||||
|
||||
Future<bool> tryImport(PlatformFile file) async {
|
||||
if (file.name == dbName) {
|
||||
final dest = p.join(settings.tempDir, dbName);
|
||||
|
@ -78,10 +58,10 @@ abstract class CoinBase {
|
|||
|
||||
Future<void> export(BuildContext context, String dbPath) async {
|
||||
final path = _getFullPath(dbPath);
|
||||
db = await openDatabase(path, onConfigure: (db) async {
|
||||
await db.rawQuery("PRAGMA journal_mode=off");
|
||||
});
|
||||
await db.close();
|
||||
// db = await openDatabase(path, onConfigure: (db) async {
|
||||
// await db.rawQuery("PRAGMA journal_mode=off");
|
||||
// });
|
||||
// await db.close();
|
||||
await exportFile(context, path, dbName);
|
||||
}
|
||||
|
||||
|
@ -99,16 +79,3 @@ abstract class CoinBase {
|
|||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> createSchema(Database db, int version) async {
|
||||
final script = await rootBundle.loadString("assets/create_db.sql");
|
||||
final statements = script.split(";");
|
||||
for (var s in statements) {
|
||||
if (s.isNotEmpty) {
|
||||
final sql = s.trim();
|
||||
print(sql);
|
||||
db.execute(sql);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,5 +7,5 @@ CoinBase ycash = YcashCoin();
|
|||
CoinBase zcash = ZcashCoin();
|
||||
CoinBase zcashtest = ZcashTestCoin();
|
||||
|
||||
final coins = [ycash, zcash];
|
||||
final coins = [zcash, ycash];
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
||||
import 'package:warp_api/data_fb_generated.dart';
|
||||
import 'package:warp_api/warp_api.dart';
|
||||
|
||||
import 'main.dart';
|
||||
|
@ -33,9 +34,9 @@ class ContactsState extends State<ContactsTab> {
|
|||
child: Dismissible(
|
||||
key: Key("${c.id}"),
|
||||
child: ListTile(
|
||||
title: Text(c.name,
|
||||
title: Text(c.name!,
|
||||
style: Theme.of(context).textTheme.headline5),
|
||||
subtitle: Text(c.address),
|
||||
subtitle: Text(c.address!),
|
||||
trailing: Icon(Icons.chevron_right),
|
||||
onTap: () { _onContact(c); },
|
||||
onLongPress: () { _editContact(c); },
|
||||
|
@ -51,32 +52,32 @@ class ContactsState extends State<ContactsTab> {
|
|||
);
|
||||
}
|
||||
|
||||
_onContact(Contact c) {
|
||||
_onContact(ContactT c) {
|
||||
Navigator.of(context).pushNamed('/send', arguments: SendPageArgs(contact: c));
|
||||
}
|
||||
|
||||
_editContact(Contact c) async {
|
||||
_editContact(ContactT c) async {
|
||||
final contact = await showContactForm(context, c, true);
|
||||
if (contact != null) {
|
||||
contacts.add(contact);
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _onConfirmDelContact(Contact c) async {
|
||||
Future<bool> _onConfirmDelContact(ContactT c) async {
|
||||
final confirm = await showMessageBox(context, S.of(context).deleteContact,
|
||||
S.of(context).areYouSureYouWantToDeleteThisContact,
|
||||
S.of(context).delete);
|
||||
return confirm;
|
||||
}
|
||||
|
||||
_delContact(Contact c) async {
|
||||
await contacts.remove(c);
|
||||
_delContact(ContactT c) {
|
||||
contacts.remove(c);
|
||||
}
|
||||
|
||||
Future<Contact?> showContactForm(BuildContext context, Contact c, bool edit) async {
|
||||
Future<ContactT?> showContactForm(BuildContext context, ContactT c, bool edit) async {
|
||||
final key = GlobalKey<ContactState>();
|
||||
|
||||
final contact = await showDialog<Contact>(
|
||||
final contact = await showDialog<ContactT>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
contentPadding: EdgeInsets.all(16),
|
||||
|
@ -118,7 +119,7 @@ class NoContact extends StatelessWidget {
|
|||
}
|
||||
|
||||
class ContactForm extends StatefulWidget {
|
||||
final Contact contact;
|
||||
final ContactT contact;
|
||||
|
||||
ContactForm(this.contact, {Key? key}) : super(key: key);
|
||||
|
||||
|
@ -134,8 +135,8 @@ class ContactState extends State<ContactForm> {
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
nameController.text = widget.contact.name;
|
||||
address = widget.contact.address;
|
||||
nameController.text = widget.contact.name ?? "";
|
||||
address = widget.contact.address ?? "";
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -159,7 +160,7 @@ class ContactState extends State<ContactForm> {
|
|||
final state = formKey.currentState!;
|
||||
if (state.validate()) {
|
||||
state.save();
|
||||
final contact = Contact(widget.contact.id, nameController.text, address);
|
||||
final contact = ContactT(id: widget.contact.id, name: nameController.text, address: address);
|
||||
Navigator.of(context).pop(contact);
|
||||
active.update();
|
||||
}
|
||||
|
|
350
lib/db.dart
350
lib/db.dart
|
@ -1,13 +1,9 @@
|
|||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
import 'package:warp_api/types.dart';
|
||||
import 'accounts.dart';
|
||||
import 'package:warp_api/data_fb_generated.dart' hide Quote;
|
||||
import 'store.dart';
|
||||
import 'package:warp_api/warp_api.dart';
|
||||
import 'package:convert/convert.dart';
|
||||
import 'main.dart';
|
||||
|
||||
final DateFormat noteDateFormat = DateFormat("yy-MM-dd HH:mm");
|
||||
|
@ -18,180 +14,47 @@ final DateFormat msgDateFormatFull = DateFormat("yy-MM-dd HH:mm:ss");
|
|||
class DbReader {
|
||||
int coin;
|
||||
int id;
|
||||
Database db;
|
||||
|
||||
DbReader(int coin, int id): this.init(coin, id, settings.coins[coin].def.db);
|
||||
DbReader.init(this.coin, this.id, this.db);
|
||||
DbReader(int coin, int id): this.init(coin, id);
|
||||
DbReader.init(this.coin, this.id);
|
||||
|
||||
Future<bool> hasAccount() async {
|
||||
final List<Map> res = await db.rawQuery(
|
||||
"SELECT 1 FROM accounts",
|
||||
[]);
|
||||
return res.isNotEmpty;
|
||||
}
|
||||
|
||||
Future<List<Account>> getAccountList() async {
|
||||
List<Account> accounts = [];
|
||||
|
||||
final List<Map> res = await db.rawQuery(
|
||||
"WITH notes AS (SELECT a.id_account, a.name, CASE WHEN r.spent IS NULL THEN r.value ELSE 0 END AS nv FROM accounts a LEFT JOIN received_notes r ON a.id_account = r.account),"
|
||||
"accounts2 AS (SELECT id_account, name, COALESCE(sum(nv), 0) AS balance FROM notes GROUP by id_account) "
|
||||
"SELECT a.id_account, a.name, a.balance FROM accounts2 a",
|
||||
[]);
|
||||
for (var r in res) {
|
||||
final int id = r['id_account'];
|
||||
final account = Account(
|
||||
coin,
|
||||
id,
|
||||
r['name'],
|
||||
r['balance'],
|
||||
0,
|
||||
null);
|
||||
accounts.add(account);
|
||||
}
|
||||
return accounts;
|
||||
}
|
||||
|
||||
// check that the account still exists
|
||||
// if not, pick any account
|
||||
// if there are none, return 0
|
||||
Future<AccountId?> getAvailableAccountId() async {
|
||||
final List<Map> res1 = await db.rawQuery(
|
||||
"SELECT 1 FROM accounts WHERE id_account = ?1", [id]);
|
||||
if (res1.isNotEmpty)
|
||||
return AccountId(coin, id);
|
||||
final List<Map> res2 = await db.rawQuery(
|
||||
"SELECT id_account FROM accounts", []);
|
||||
if (res2.isNotEmpty) {
|
||||
final id = res2[0]['id_account'];
|
||||
return AccountId(coin, id);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<String> getTAddr() async {
|
||||
final List<Map> res1 = await db.rawQuery(
|
||||
"SELECT address FROM taddrs WHERE account = ?1", [id]);
|
||||
final taddress = res1.isNotEmpty ? res1[0]['address'] : "";
|
||||
return taddress;
|
||||
}
|
||||
|
||||
Future<String?> getSK() async {
|
||||
final List<Map> res1 = await db.rawQuery(
|
||||
"SELECT sk FROM accounts WHERE id_account = ?1", [id]);
|
||||
final sk = res1.isNotEmpty ? res1[0]['address'] : null;
|
||||
return sk;
|
||||
}
|
||||
|
||||
Future<void> changeAccountName(String name) async {
|
||||
await db.execute("UPDATE accounts SET name = ?2 WHERE id_account = ?1",
|
||||
[id, name]);
|
||||
}
|
||||
|
||||
Future<BlockInfo?> getDbSyncedHeight() async {
|
||||
final rows = await db.rawQuery("SELECT height, timestamp FROM blocks WHERE height = (SELECT MAX(height) FROM blocks)");
|
||||
if (rows.isNotEmpty) {
|
||||
final row = rows.first;
|
||||
final height = row['height'] as int;
|
||||
final timestampEpoch = row['timestamp'] as int;
|
||||
final timestamp = DateTime.fromMillisecondsSinceEpoch(timestampEpoch * 1000);
|
||||
final blockInfo = BlockInfo(height, timestamp);
|
||||
return blockInfo;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> updateBalances(int confirmHeight, Balances balances) async {
|
||||
final balance = Sqflite.firstIntValue(await db.rawQuery(
|
||||
"SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND (spent IS NULL OR spent = 0)",
|
||||
[id])) ?? 0;
|
||||
final shieldedBalance = Sqflite.firstIntValue(await db.rawQuery(
|
||||
"SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND spent IS NULL",
|
||||
[id])) ?? 0;
|
||||
final unconfirmedSpentBalance = Sqflite.firstIntValue(await db.rawQuery(
|
||||
"SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND spent = 0",
|
||||
[id])) ?? 0;
|
||||
final underConfirmedBalance = Sqflite.firstIntValue(await db.rawQuery(
|
||||
"SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND spent IS NULL AND height > ?2",
|
||||
[id, confirmHeight])) ?? 0;
|
||||
final excludedBalance = Sqflite.firstIntValue(await db.rawQuery(
|
||||
"SELECT SUM(value) FROM received_notes WHERE account = ?1 AND spent IS NULL "
|
||||
"AND height <= ?2 AND excluded",
|
||||
[id, confirmHeight])) ?? 0;
|
||||
|
||||
balances.update(balance, shieldedBalance, unconfirmedSpentBalance, underConfirmedBalance, excludedBalance);
|
||||
}
|
||||
|
||||
Future<int> getSaplingBalance() async {
|
||||
return Sqflite.firstIntValue(await db.rawQuery("SELECT SUM(value) FROM received_notes WHERE account = ?1 AND spent IS NULL AND orchard = 0",
|
||||
[id])) ?? 0;
|
||||
}
|
||||
|
||||
Future<int> getOrchardBalance() async {
|
||||
return Sqflite.firstIntValue(await db.rawQuery("SELECT SUM(value) FROM received_notes WHERE account = ?1 AND spent IS NULL AND orchard = 1",
|
||||
[id])) ?? 0;
|
||||
}
|
||||
|
||||
Future<List<Note>> getNotes() async {
|
||||
final List<Map> res = await db.rawQuery(
|
||||
"SELECT n.id_note, n.height, n.value, t.timestamp, n.orchard, n.excluded, n.spent FROM received_notes n, transactions t "
|
||||
"WHERE n.account = ?1 AND (n.spent IS NULL OR n.spent = 0) "
|
||||
"AND n.tx = t.id_tx ORDER BY n.height DESC",
|
||||
[id]);
|
||||
final notes = res.map((row) {
|
||||
final id = row['id_note'];
|
||||
final height = row['height'];
|
||||
final timestamp = DateTime.fromMillisecondsSinceEpoch(row['timestamp'] * 1000);
|
||||
final orchard = row['orchard'] != 0;
|
||||
final excluded = (row['excluded'] ?? 0) != 0;
|
||||
final spent = row['spent'] == 0;
|
||||
List<Note> getNotes() {
|
||||
final ns = WarpApi.getNotes(coin, id);
|
||||
final notes = ns.map((n) {
|
||||
final timestamp = DateTime.fromMillisecondsSinceEpoch(n.timestamp * 1000);
|
||||
return Note(
|
||||
id, height, timestamp, row['value'] / ZECUNIT, orchard, excluded, spent);
|
||||
n.id, n.height, timestamp, n.value / ZECUNIT, n.orchard, n.excluded, n.spent);
|
||||
}).toList();
|
||||
print("NOTES ${notes.length}");
|
||||
return notes;
|
||||
}
|
||||
|
||||
Future<List<Tx>> getTxs() async {
|
||||
final List<Map> res2 = await db.rawQuery(
|
||||
"SELECT id_tx, txid, height, timestamp, t.address, c.name AS cname, a.name AS aname, value, memo FROM transactions t "
|
||||
"LEFT JOIN contacts c ON t.address = c.address "
|
||||
"LEFT JOIN accounts a ON a.address = t.address "
|
||||
"WHERE account = ?1 ORDER BY height DESC",
|
||||
[id]);
|
||||
final txs = res2.map((row) {
|
||||
Uint8List txid = row['txid'];
|
||||
final fullTxId = hex.encode(txid.reversed.toList());
|
||||
final shortTxid = fullTxId.substring(0, 8);
|
||||
final timestamp = DateTime.fromMillisecondsSinceEpoch(row['timestamp'] * 1000);
|
||||
final String? contactName = row['cname'];
|
||||
final String? accountName = row['aname'];
|
||||
final name = contactName ?? accountName;
|
||||
List<Tx> getTxs() {
|
||||
final txs = WarpApi.getTxs(coin, id);
|
||||
final transactions = txs.map((tx) {
|
||||
final timestamp = DateTime.fromMillisecondsSinceEpoch(tx.timestamp * 1000);
|
||||
return Tx(
|
||||
row['id_tx'],
|
||||
row['height'],
|
||||
tx.id,
|
||||
tx.height,
|
||||
timestamp,
|
||||
shortTxid,
|
||||
fullTxId,
|
||||
row['value'] / ZECUNIT,
|
||||
row['address'] ?? "",
|
||||
name,
|
||||
row['memo'] ?? "");
|
||||
tx.shortTxId!,
|
||||
tx.txId!,
|
||||
tx.value / ZECUNIT,
|
||||
tx.address!,
|
||||
tx.name!,
|
||||
tx.memo!);
|
||||
}).toList();
|
||||
print("TXS ${txs.length}");
|
||||
return txs;
|
||||
print("TXS ${transactions.length}");
|
||||
return transactions;
|
||||
}
|
||||
|
||||
Future<List<PnL>> getPNL(int accountId) async {
|
||||
List<PnL> getPNL(int accountId) {
|
||||
final range = _getChartRange();
|
||||
|
||||
final List<Map> res1 = await db.rawQuery(
|
||||
"SELECT timestamp, value FROM transactions WHERE timestamp >= ?2 AND account = ?1",
|
||||
[accountId, range.start ~/ 1000]);
|
||||
final List<Trade> trades = [];
|
||||
for (var row in res1) {
|
||||
final dt = DateTime.fromMillisecondsSinceEpoch(row['timestamp'] * 1000);
|
||||
final qty = row['value'] / ZECUNIT;
|
||||
for (var row in WarpApi.getPnLTxs(coin, id, range.start ~/ 1000)) {
|
||||
final dt = DateTime.fromMillisecondsSinceEpoch(row.timestamp * 1000);
|
||||
final qty = row.value / ZECUNIT;
|
||||
trades.add(Trade(dt, qty));
|
||||
}
|
||||
|
||||
|
@ -204,13 +67,10 @@ class DbReader {
|
|||
(acc, t) => acc + t.qty,
|
||||
0.0);
|
||||
|
||||
final List<Map> res2 = await db.rawQuery(
|
||||
"SELECT timestamp, price FROM historical_prices WHERE timestamp >= ?2 AND currency = ?1",
|
||||
[settings.currency, range.start ~/ 1000]);
|
||||
final List<Quote> quotes = [];
|
||||
for (var row in res2) {
|
||||
final dt = DateTime.fromMillisecondsSinceEpoch(row['timestamp'] * 1000);
|
||||
final price = row['price'];
|
||||
for (var row in WarpApi.getQuotes(coin, range.start ~/ 1000, settings.currency)) {
|
||||
final dt = DateTime.fromMillisecondsSinceEpoch(row.timestamp * 1000);
|
||||
final price = row.price;
|
||||
quotes.add(Quote(dt, price));
|
||||
}
|
||||
|
||||
|
@ -243,33 +103,21 @@ class DbReader {
|
|||
return pnls;
|
||||
}
|
||||
|
||||
Future<List<Spending>> getSpending(int accountId) async {
|
||||
List<Spending> getSpending(int accountId) {
|
||||
final range = _getChartRange();
|
||||
final List<Map> res = await db.rawQuery(
|
||||
"SELECT SUM(value) as v, t.address, c.name FROM transactions t LEFT JOIN contacts c ON t.address = c.address "
|
||||
"WHERE account = ?1 AND timestamp >= ?2 AND value < 0 GROUP BY t.address ORDER BY v ASC LIMIT 5",
|
||||
[accountId, range.start ~/ 1000]);
|
||||
final spendings = res.map((row) {
|
||||
final address = row['address'] ?? "";
|
||||
final value = -row['v'] / ZECUNIT;
|
||||
final contact = row['name'];
|
||||
return Spending(address, value, contact);
|
||||
}).toList();
|
||||
return spendings;
|
||||
return WarpApi.getSpendings(coin, id, range.start ~/ 1000);
|
||||
}
|
||||
|
||||
Future<List<TimeSeriesPoint<double>>> getAccountBalanceTimeSeries(int accountId, int balance) async {
|
||||
List<TimeSeriesPoint<double>> getAccountBalanceTimeSeries(int accountId, int balance) {
|
||||
final range = _getChartRange();
|
||||
final List<Map> res = await db.rawQuery(
|
||||
"SELECT timestamp, value FROM transactions WHERE account = ?1 AND timestamp >= ?2 ORDER BY timestamp DESC",
|
||||
[accountId, range.start ~/ 1000]);
|
||||
final trades = WarpApi.getPnLTxs(coin, id, range.start ~/ 1000);
|
||||
List<AccountBalance> _accountBalances = [];
|
||||
var b = balance;
|
||||
_accountBalances.add(AccountBalance(DateTime.now(), b / ZECUNIT));
|
||||
for (var row in res) {
|
||||
for (var trade in trades) {
|
||||
final timestamp =
|
||||
DateTime.fromMillisecondsSinceEpoch(row['timestamp'] * 1000);
|
||||
final value = row['value'] as int;
|
||||
DateTime.fromMillisecondsSinceEpoch(trade.timestamp * 1000);
|
||||
final value = trade.value;
|
||||
final ab = AccountBalance(timestamp, b / ZECUNIT);
|
||||
_accountBalances.add(ab);
|
||||
b -= value;
|
||||
|
@ -288,63 +136,27 @@ class DbReader {
|
|||
return accountBalances;
|
||||
}
|
||||
|
||||
Future<List<ZMessage>> getMessages() async {
|
||||
final List<Map> res = await db.rawQuery(
|
||||
"SELECT m.id, m.id_tx, m.timestamp, m.sender, m.recipient, m.incoming, c.name as scontact, a.name as saccount, c2.name as rcontact, a2.name as raccount, "
|
||||
"subject, body, height, read FROM messages m "
|
||||
"LEFT JOIN contacts c ON m.sender = c.address "
|
||||
"LEFT JOIN accounts a ON m.sender = a.address "
|
||||
"LEFT JOIN contacts c2 ON m.recipient = c2.address "
|
||||
"LEFT JOIN accounts a2 ON m.recipient = a2.address "
|
||||
"WHERE account = ?1 ORDER BY timestamp DESC",
|
||||
[id]);
|
||||
List<ZMessage> messages = [];
|
||||
for (var row in res) {
|
||||
final id = row['id'];
|
||||
final txId = row['id_tx'] ?? 0;
|
||||
final timestamp = DateTime.fromMillisecondsSinceEpoch(row['timestamp'] * 1000);
|
||||
final height = row['height'];
|
||||
final sender = row['sender'];
|
||||
final from = row['scontact'] ?? row['saccount'] ?? sender;
|
||||
final recipient = row['recipient'];
|
||||
final to = row['rcontact'] ?? row['raccount'] ?? recipient;
|
||||
final subject = row['subject'];
|
||||
final body = row['body'];
|
||||
final read = row['read'] == 1;
|
||||
final incoming = row['incoming'] == 1;
|
||||
messages.add(ZMessage(id, txId, incoming, from, to, subject, body, timestamp, height, read));
|
||||
}
|
||||
return messages;
|
||||
List<ZMessage> getMessages() {
|
||||
final messages = WarpApi.getMessages(coin, id);
|
||||
return messages.map((m) =>
|
||||
ZMessage(m.idMsg, m.idTx, m.incoming, m.from, m.to!, m.subject!, m.body!,
|
||||
DateTime.fromMillisecondsSinceEpoch(m.timestamp * 1000), m.height, m.read)).toList();
|
||||
}
|
||||
|
||||
Future<int?> getPrevMessage(String subject, int height, int account) async {
|
||||
final id = await Sqflite.firstIntValue(await db.rawQuery(
|
||||
"SELECT MAX(id) FROM messages WHERE subject = ?1 AND height < ?2 and account = ?3",
|
||||
[subject, height, account]));
|
||||
return id;
|
||||
}
|
||||
|
||||
Future<int?> getNextMessage(String subject, int height, int account) async {
|
||||
final id = await Sqflite.firstIntValue(await db.rawQuery(
|
||||
"SELECT MIN(id) FROM messages WHERE subject = ?1 AND height > ?2 and account = ?3",
|
||||
[subject, height, account]));
|
||||
return id;
|
||||
}
|
||||
|
||||
Future<List<TAccount>> getTAccounts() async {
|
||||
final List<Map> res = await db.rawQuery(
|
||||
"SELECT aindex, address, value FROM taddr_scan", []);
|
||||
List<TAccount> accounts = [];
|
||||
for (var row in res) {
|
||||
final aindex = row['aindex'];
|
||||
final address = row['address'];
|
||||
final balance = row['value'];
|
||||
final account = TAccount(aindex, address, balance);
|
||||
accounts.add(account);
|
||||
}
|
||||
db.execute("DELETE FROM taddr_scan");
|
||||
return accounts;
|
||||
}
|
||||
// Future<List<TAccount>> getTAccounts() async {
|
||||
// final List<Map> res = await db.rawQuery(
|
||||
// "SELECT aindex, address, value FROM taddr_scan", []);
|
||||
// List<TAccount> accounts = [];
|
||||
// for (var row in res) {
|
||||
// final aindex = row['aindex'];
|
||||
// final address = row['address'];
|
||||
// final balance = row['value'];
|
||||
// final account = TAccount(aindex, address, balance);
|
||||
// accounts.add(account);
|
||||
// }
|
||||
// db.execute("DELETE FROM taddr_scan");
|
||||
// return accounts;
|
||||
// }
|
||||
|
||||
TimeRange _getChartRange() {
|
||||
final now = DateTime.now().toUtc();
|
||||
|
@ -354,48 +166,9 @@ class DbReader {
|
|||
return TimeRange(cutoff, today.millisecondsSinceEpoch);
|
||||
}
|
||||
|
||||
Future<List<SendTemplateId>> loadTemplates() async {
|
||||
final List<Map> res = await db.rawQuery(
|
||||
"SELECT id_send_template, title FROM send_templates", []);
|
||||
List<SendTemplateId> templateIds = [];
|
||||
for (var row in res) {
|
||||
final id = row['id_send_template'];
|
||||
final title = row['title'];
|
||||
final templateId = SendTemplateId(id, title);
|
||||
templateIds.add(templateId);
|
||||
}
|
||||
return templateIds;
|
||||
}
|
||||
|
||||
Future<SendTemplate?> loadTemplate(int id) async {
|
||||
final List<Map> res = await db.rawQuery(
|
||||
"SELECT title, address, amount, fiat_amount, fee_included, fiat, include_reply_to, subject, body "
|
||||
"FROM send_templates WHERE id_send_template = ?1", [id]);
|
||||
for (var row in res) {
|
||||
final title = row['title'];
|
||||
final address = row['address'];
|
||||
final amount = row['amount'];
|
||||
final num fiat_amount = row['fiat_amount'];
|
||||
final fee_included = row['fee_included'] != 0;
|
||||
final fiat = row['fiat'];
|
||||
final include_reply_to = row['include_reply_to'] != 0;
|
||||
final subject = row['subject'];
|
||||
final body = row['body'];
|
||||
final memo = Memo(include_reply_to, subject, body);
|
||||
final template = SendTemplate(id, title, address, amount, fiat_amount.toDouble(), fee_included, fiat, memo);
|
||||
return template;
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<Contact>> getContacts() async {
|
||||
List<Contact> contacts = [];
|
||||
List<Map> res = await db.rawQuery(
|
||||
"SELECT id, name, address FROM contacts WHERE address <> '' ORDER BY name");
|
||||
for (var c in res) {
|
||||
final contact = Contact(c['id'], c['name'], c['address']);
|
||||
contacts.add(contact);
|
||||
}
|
||||
return contacts;
|
||||
List<SendTemplateT> loadTemplates() {
|
||||
final templates = WarpApi.getSendTemplates(coin);
|
||||
return templates;
|
||||
}
|
||||
|
||||
int _chartRangeDays() {
|
||||
|
@ -431,10 +204,3 @@ class ZMessage {
|
|||
|
||||
String fromto() => incoming ? "\u{21e6} ${sender != null ? centerTrim(sender!) : ''}" : "\u{21e8} ${centerTrim(recipient)}";
|
||||
}
|
||||
|
||||
class TAccount {
|
||||
final int aindex;
|
||||
final String address;
|
||||
final int balance;
|
||||
TAccount(this.aindex, this.address, this.balance);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:warp_api/data_fb_generated.dart';
|
||||
import 'package:warp_api/types.dart';
|
||||
import 'package:warp_api/warp_api.dart';
|
||||
import 'package:badges/badges.dart';
|
||||
|
@ -36,20 +37,20 @@ class HomeState extends State<HomePage> {
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
contacts.fetchContacts();
|
||||
Future.microtask(() async {
|
||||
await syncStatus.update();
|
||||
await active.updateBalances();
|
||||
active.updateBalances();
|
||||
await priceStore.updateChart();
|
||||
|
||||
await Future.delayed(Duration(seconds: 3));
|
||||
await syncStatus.sync(false);
|
||||
await contacts.fetchContacts();
|
||||
|
||||
_syncTimer?.cancel();
|
||||
_syncTimer = Timer.periodic(Duration(seconds: 15), (Timer t) async {
|
||||
syncStatus.sync(false);
|
||||
if (active.id != 0) {
|
||||
await active.updateBalances();
|
||||
active.updateBalances();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -398,7 +399,7 @@ class HomeInnerState extends State<HomeInnerPage> with SingleTickerProviderState
|
|||
|
||||
_onAddContact() async {
|
||||
final contact = await contactKey.currentState
|
||||
?.showContactForm(context, Contact.empty(), false);
|
||||
?.showContactForm(context, ContactT(), false);
|
||||
if (contact != null) {
|
||||
contacts.add(contact);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:ffi' show DynamicLibrary, NativePort;
|
||||
import 'dart:ffi' show NativePort;
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'dart:ui';
|
||||
|
@ -23,13 +23,10 @@ import 'package:qr_flutter/qr_flutter.dart';
|
|||
import 'package:rate_my_app/rate_my_app.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||
import 'package:warp_api/warp_api.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:uni_links/uni_links.dart';
|
||||
import 'package:quick_actions/quick_actions.dart';
|
||||
import 'package:sqlite3/open.dart';
|
||||
import 'package:awesome_notifications/awesome_notifications.dart';
|
||||
import 'accounts.dart';
|
||||
import 'animated_qr.dart';
|
||||
|
@ -328,9 +325,6 @@ class ZWalletAppState extends State<ZWalletApp> {
|
|||
);
|
||||
});
|
||||
}
|
||||
else {
|
||||
databaseFactory = createDatabaseFactoryFfi(ffiInit: sqlFfiInit);;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _init() async {
|
||||
|
@ -345,8 +339,6 @@ class ZWalletAppState extends State<ZWalletApp> {
|
|||
final dbPath = await getDbPath();
|
||||
for (var coin in coins) {
|
||||
coin.init(dbPath);
|
||||
await coin.open(true);
|
||||
await coin.close();
|
||||
}
|
||||
|
||||
if (exportDb) {
|
||||
|
@ -357,8 +349,6 @@ class ZWalletAppState extends State<ZWalletApp> {
|
|||
if (recover) {
|
||||
for (var coin in coins) {
|
||||
await coin.importFromTemp();
|
||||
await coin.open(true);
|
||||
await coin.close();
|
||||
}
|
||||
}
|
||||
print("db path $dbPath");
|
||||
|
@ -377,10 +367,6 @@ class ZWalletAppState extends State<ZWalletApp> {
|
|||
}
|
||||
}
|
||||
|
||||
for (var coin in coins) {
|
||||
await coin.open(false);
|
||||
}
|
||||
|
||||
_setProgress(0.7, 'Restoring Active Account');
|
||||
await active.restore();
|
||||
|
||||
|
@ -435,6 +421,7 @@ class ZWalletAppState extends State<ZWalletApp> {
|
|||
}
|
||||
|
||||
void _setProgress(double v, String message) {
|
||||
print(message);
|
||||
progressKey.currentState?.setValue(v, message);
|
||||
}
|
||||
}
|
||||
|
@ -851,23 +838,9 @@ void cancelScan(BuildContext context) {
|
|||
WarpApi.cancelSync();
|
||||
}
|
||||
|
||||
void sqlFfiInit() {
|
||||
open.overrideFor(OperatingSystem.linux, _openOnLinux);
|
||||
}
|
||||
|
||||
DynamicLibrary _openOnLinux() {
|
||||
try {
|
||||
final library = DynamicLibrary.open('libsqlite3.so.0');
|
||||
return library;
|
||||
}
|
||||
catch (e) {
|
||||
print("Failed to load SQLite3: $e");
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> getDataPath() async {
|
||||
String? home;
|
||||
if (Platform.isAndroid) home = (await getApplicationDocumentsDirectory()).parent.path;
|
||||
if (Platform.isWindows) home = Platform.environment['LOCALAPPDATA'];
|
||||
if (Platform.isLinux) home = Platform.environment['XDG_DATA_HOME'];
|
||||
if (Platform.isMacOS) home = Platform.environment['HOME'];
|
||||
|
@ -887,7 +860,7 @@ Future<String> getTempPath() async {
|
|||
}
|
||||
|
||||
Future<String> getDbPath() async {
|
||||
if (isMobile()) return await getDatabasesPath();
|
||||
if (Platform.isIOS) return (await getApplicationDocumentsDirectory()).path;
|
||||
final h = await getDataPath();
|
||||
return "$h/databases";
|
||||
}
|
||||
|
|
|
@ -223,14 +223,14 @@ class MessagePageState extends State<MessagePage> {
|
|||
_prevInThread() async {
|
||||
final id = await active.prevInThread(index);
|
||||
setState(() {
|
||||
if (id != null) index = active.messages.indexWhere((m) => m.id == id);
|
||||
if (id != 0) index = active.messages.indexWhere((m) => m.id == id);
|
||||
});
|
||||
}
|
||||
|
||||
_nextInThread() async {
|
||||
final id = await active.nextInThread(index);
|
||||
setState(() {
|
||||
if (id != null) index = active.messages.indexWhere((m) => m.id == id);
|
||||
if (id != 0) index = active.messages.indexWhere((m) => m.id == id);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:warp_api/data_fb_generated.dart';
|
||||
import 'package:warp_api/warp_api.dart';
|
||||
import 'db.dart';
|
||||
import 'generated/l10n.dart';
|
||||
|
@ -10,34 +11,33 @@ class ScanTAddrPage extends StatefulWidget {
|
|||
}
|
||||
|
||||
class ScanTAddrPageState extends State<ScanTAddrPage> {
|
||||
List<TAccount>? tAccounts = null;
|
||||
List<AddressBalance>? addresses = null;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
Future(() async {
|
||||
await WarpApi.scanTransparentAccounts(active.coin, active.id, settings.gapLimit);
|
||||
final _addresses = await WarpApi.scanTransparentAccounts(active.coin, active.id, settings.gapLimit);
|
||||
final dbr = DbReader(active.coin, active.id);
|
||||
final _accounts = await dbr.getTAccounts();
|
||||
setState(() {
|
||||
tAccounts = _accounts;
|
||||
addresses = _addresses;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _accounts = tAccounts;
|
||||
final _addresses = addresses;
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text('Scan Transparent Addresses')),
|
||||
body: (_accounts == null) ?
|
||||
body: (_addresses == null) ?
|
||||
Center(child: Text('Scanning addresses'))
|
||||
: Column(
|
||||
children: [
|
||||
Expanded(child: ListView.builder(
|
||||
itemCount: _accounts.length,
|
||||
itemCount: _addresses.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final a = _accounts[index];
|
||||
return ListTile(title: Text(a.address), subtitle: Text(amountToString(a.balance, MAX_PRECISION)));
|
||||
final a = _addresses[index];
|
||||
return ListTile(title: Text(a.address!), subtitle: Text(amountToString(a.balance, MAX_PRECISION)));
|
||||
})),
|
||||
ButtonBar(children: confirmButtons(this.context, onPressed))
|
||||
]
|
||||
|
@ -47,10 +47,10 @@ class ScanTAddrPageState extends State<ScanTAddrPage> {
|
|||
|
||||
onPressed() async {
|
||||
final s = S.of(context);
|
||||
final _accounts = tAccounts ?? [];
|
||||
final _accounts = addresses ?? [];
|
||||
for (var account in _accounts) {
|
||||
final name = s.subAccountIndexOf(account.aindex, active.account.name);
|
||||
WarpApi.newSubAccount(name, account.aindex, 1);
|
||||
final name = s.subAccountIndexOf(account.index, active.account.name);
|
||||
WarpApi.newSubAccount(name, account.index, 1);
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
|
108
lib/send.dart
108
lib/send.dart
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
||||
import 'package:warp_api/data_fb_generated.dart' hide Account;
|
||||
import 'accounts.dart';
|
||||
import 'dualmoneyinput.dart';
|
||||
import 'package:warp_api/types.dart';
|
||||
|
@ -48,8 +49,8 @@ class SendState extends State<SendPage> {
|
|||
final _fee = DEFAULT_FEE;
|
||||
var _usedBalance = 0;
|
||||
var _replyTo = settings.includeReplyTo;
|
||||
List<SendTemplateId> _templates = [];
|
||||
SendTemplateId? _template;
|
||||
List<SendTemplateT> _templates = [];
|
||||
SendTemplateT? _template;
|
||||
var _accounts = AccountList();
|
||||
|
||||
void clear() {
|
||||
|
@ -80,7 +81,7 @@ class SendState extends State<SendPage> {
|
|||
}
|
||||
|
||||
if (widget.args?.contact != null)
|
||||
_addressController.text = widget.args!.contact!.address;
|
||||
_addressController.text = widget.args!.contact!.address!;
|
||||
if (widget.args?.address != null)
|
||||
_addressController.text = widget.args!.address!;
|
||||
if (widget.args?.subject != null)
|
||||
|
@ -94,27 +95,21 @@ class SendState extends State<SendPage> {
|
|||
_setPaymentURI(uri);
|
||||
});
|
||||
|
||||
_newBlockAutorunDispose = autorun((_) async {
|
||||
final _ = active.dataEpoch;
|
||||
final balances = active.balances;
|
||||
final sBalance = balances.shieldedBalance;
|
||||
final tBalance = active.tbalance;
|
||||
final excludedBalance = balances.excludedBalance;
|
||||
final underConfirmedBalance = balances.underConfirmedBalance;
|
||||
int? unconfirmedBalance = unconfirmedBalanceStream.value;
|
||||
setState(() {
|
||||
_sBalance = sBalance;
|
||||
_tBalance = tBalance;
|
||||
_excludedBalance = excludedBalance;
|
||||
_underConfirmedBalance = underConfirmedBalance;
|
||||
_unconfirmedBalance = unconfirmedBalance ?? 0;
|
||||
});
|
||||
});
|
||||
active.updateBalances();
|
||||
final balances = active.balances;
|
||||
final sBalance = balances.shieldedBalance;
|
||||
final tBalance = active.tbalance;
|
||||
final excludedBalance = balances.excludedBalance;
|
||||
final underConfirmedBalance = balances.underConfirmedBalance;
|
||||
int? unconfirmedBalance = unconfirmedBalanceStream.value;
|
||||
_sBalance = sBalance;
|
||||
_tBalance = tBalance;
|
||||
_excludedBalance = excludedBalance;
|
||||
_underConfirmedBalance = underConfirmedBalance;
|
||||
_unconfirmedBalance = unconfirmedBalance ?? 0;
|
||||
|
||||
Future.microtask(() async {
|
||||
await _accounts.refresh();
|
||||
await _loadTemplates();
|
||||
});
|
||||
final templateIds = active.dbReader.loadTemplates();
|
||||
_templates = templateIds;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -144,7 +139,7 @@ class SendState extends State<SendPage> {
|
|||
_memoInitialized = true;
|
||||
}
|
||||
|
||||
var templates = _templates.map((t) => DropdownMenuItem(child: Text(t.title), value: t)).toList();
|
||||
var templates = _templates.map((t) => DropdownMenuItem(child: Text(t.title!), value: t)).toList();
|
||||
|
||||
final addReset = _template != null ? IconButton(onPressed: _resetTemplate, icon: Icon(Icons.close)) : IconButton(onPressed: _addTemplate, icon: Icon(Icons.add));
|
||||
|
||||
|
@ -176,7 +171,7 @@ class SendState extends State<SendPage> {
|
|||
_addressController.text = suggestion.name;
|
||||
},
|
||||
suggestionsCallback: (String pattern) {
|
||||
final matchingContacts = contacts.contacts.where((c) => c.name
|
||||
final matchingContacts = contacts.contacts.where((c) => c.name!
|
||||
.toLowerCase()
|
||||
.contains(pattern.toLowerCase())).map((c) => ContactSuggestion(c));
|
||||
final matchingAccounts = _accounts.list
|
||||
|
@ -230,7 +225,7 @@ class SendState extends State<SendPage> {
|
|||
Padding(padding: EdgeInsets.all(8)),
|
||||
Row(children: [
|
||||
Expanded(child:
|
||||
DropdownButtonFormField<SendTemplateId>(
|
||||
DropdownButtonFormField<SendTemplateT>(
|
||||
hint: Text(s.template),
|
||||
items: templates, value: _template, onChanged: (v) {
|
||||
setState(() {
|
||||
|
@ -239,7 +234,7 @@ class SendState extends State<SendPage> {
|
|||
})),
|
||||
addReset,
|
||||
IconButton(onPressed: _template != null ? _openTemplate : null, icon: Icon(Icons.open_in_new)),
|
||||
IconButton(onPressed: _template != null ? () { _saveTemplate(_template!.id, _template!.title, true); } : null, icon: Icon(Icons.save)),
|
||||
IconButton(onPressed: _template != null ? () { _saveTemplate(_template!.id, _template!.title!, true); } : null, icon: Icon(Icons.save)),
|
||||
IconButton(onPressed: _template != null ? _deleteTemplate : null, icon: Icon(Icons.delete)),
|
||||
]),
|
||||
Padding(padding: EdgeInsets.all(8)),
|
||||
|
@ -278,32 +273,35 @@ class SendState extends State<SendPage> {
|
|||
}))) ?? false;
|
||||
if (!confirmed) return;
|
||||
final title = titleController.text;
|
||||
final id = _saveTemplate(0, title, false);
|
||||
if (id != null) {
|
||||
_template = SendTemplateId(id, title);
|
||||
Future.microtask(_loadTemplates);
|
||||
final template = _saveTemplate(0, title, false);
|
||||
if (template != null) {
|
||||
_template = template;
|
||||
_templates.add(template);
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int? _saveTemplate(int id, String title, bool validate) {
|
||||
SendTemplateT? _saveTemplate(int id, String title, bool validate) {
|
||||
final form = _formKey.currentState!;
|
||||
if (validate && !form.validate()) return null;
|
||||
form.save();
|
||||
final memo = Memo(
|
||||
_replyTo, _subjectController.text, _memoController.text);
|
||||
final dualAmountController = amountInput;
|
||||
if (dualAmountController == null) return null;
|
||||
final template = SendTemplate(
|
||||
id,
|
||||
title,
|
||||
_address,
|
||||
stringToAmount(dualAmountController.coinAmountController.text),
|
||||
parseNumber(dualAmountController.fiatAmountController.text),
|
||||
dualAmountController.feeIncluded,
|
||||
dualAmountController.inputInCoin ? null : _fiat,
|
||||
memo);
|
||||
return WarpApi.saveSendTemplate(active.coin, template);
|
||||
var template = SendTemplateT(
|
||||
id: id,
|
||||
title: title,
|
||||
address: _address,
|
||||
amount: stringToAmount(dualAmountController.coinAmountController.text),
|
||||
fiatAmount: parseNumber(dualAmountController.fiatAmountController.text),
|
||||
feeIncluded: dualAmountController.feeIncluded,
|
||||
fiat: dualAmountController.inputInCoin ? null : _fiat,
|
||||
includeReplyTo: _replyTo,
|
||||
subject: _subjectController.text,
|
||||
body: _subjectController.text);
|
||||
final id2 = WarpApi.saveSendTemplate(active.coin, template);
|
||||
template.id = id2;
|
||||
return template;
|
||||
}
|
||||
|
||||
Future<void> _deleteTemplate() async {
|
||||
|
@ -318,19 +316,19 @@ class SendState extends State<SendPage> {
|
|||
Future<void> _openTemplate() async {
|
||||
final tid = _template;
|
||||
if (tid == null) return;
|
||||
final template = await active.dbReader.loadTemplate(tid.id);
|
||||
final template = _template;;
|
||||
if (template == null) return;
|
||||
amountInput?.restore(template.amount, template.fiat_amount, template.fee_included, template.fiat);
|
||||
amountInput?.restore(template.amount, template.fiatAmount, template.feeIncluded, template.fiat);
|
||||
setState(() {
|
||||
_addressController.text = template.address;
|
||||
_replyTo = template.memo.include_reply_to;
|
||||
_subjectController.text = template.memo.subject;
|
||||
_memoController.text = template.memo.body;
|
||||
_addressController.text = template.address!;
|
||||
_replyTo = template.includeReplyTo;
|
||||
_subjectController.text = template.subject!;
|
||||
_memoController.text = template.body!;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _loadTemplates() async {
|
||||
final templateIds = await active.dbReader.loadTemplates();
|
||||
void _loadTemplates() {
|
||||
final templateIds = active.dbReader.loadTemplates();
|
||||
setState(() {
|
||||
_templates = templateIds;
|
||||
});
|
||||
|
@ -523,12 +521,12 @@ abstract class Suggestion {
|
|||
}
|
||||
|
||||
class ContactSuggestion extends Suggestion {
|
||||
final Contact contact;
|
||||
final ContactT contact;
|
||||
|
||||
ContactSuggestion(this.contact);
|
||||
|
||||
String get name => contact.name;
|
||||
String get address => contact.address;
|
||||
String get name => contact.name!;
|
||||
String get address => contact.address!;
|
||||
}
|
||||
|
||||
class AccountSuggestion extends Suggestion {
|
||||
|
|
|
@ -71,19 +71,7 @@ class SettingsState extends State<SettingsPage>
|
|||
FormBuilderFieldOption(
|
||||
child: Text(s.advanced), value: 'advanced'),
|
||||
]),
|
||||
if (!simpleMode)
|
||||
TabBar(
|
||||
controller: _tabController,
|
||||
tabs: [Tab(text: "Zcash"), Tab(text: "Ycash")]),
|
||||
if (!simpleMode)
|
||||
SizedBox(
|
||||
height: 270,
|
||||
child: TabBarView(
|
||||
controller: _tabController,
|
||||
children: [
|
||||
ServerSelect(0),
|
||||
ServerSelect(1)
|
||||
])),
|
||||
ServerSelect(active.coin),
|
||||
FormBuilderRadioGroup(
|
||||
orientation: OptionsOrientation.horizontal,
|
||||
name: 'themes',
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
import 'dart:math';
|
||||
import 'package:YWallet/db.dart';
|
||||
import 'package:YWallet/src/version.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
|
||||
|
@ -9,6 +8,7 @@ import 'package:json_annotation/json_annotation.dart';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:warp_api/data_fb_generated.dart';
|
||||
import 'coin/coins.dart';
|
||||
import 'package:warp_api/warp_api.dart';
|
||||
import 'package:warp_api/types.dart';
|
||||
|
@ -466,7 +466,7 @@ abstract class _Settings with Store {
|
|||
currency = newCurrency;
|
||||
prefs.setString('currency', currency);
|
||||
await priceStore.fetchCoinPrice(active.coin);
|
||||
await active.fetchChartData();
|
||||
active.fetchChartData();
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -646,7 +646,7 @@ abstract class _PriceStore with Store {
|
|||
if (_lastChartUpdateTime == null || now > _lastChartUpdateTime + 5 * 60) {
|
||||
await fetchCoinPrice(active.coin);
|
||||
await WarpApi.syncHistoricalPrices(settings.currency);
|
||||
await active.fetchChartData();
|
||||
active.fetchChartData();
|
||||
lastChartUpdateTime = _lastChartUpdateTime;
|
||||
}
|
||||
}
|
||||
|
@ -729,9 +729,12 @@ abstract class _SyncStatus with Store {
|
|||
WarpApi.skipToLastHeight(coin);
|
||||
}
|
||||
|
||||
Future<BlockInfo?> getDbSyncedHeight() async {
|
||||
final dbr = active.dbReader;
|
||||
return await dbr.getDbSyncedHeight();
|
||||
BlockInfo? getDbSyncedHeight() {
|
||||
final h = WarpApi.getDbHeight(active.coin);
|
||||
if (h == null) return null;
|
||||
final timestamp = DateTime.fromMillisecondsSinceEpoch(h.timestamp * 1000);
|
||||
final blockInfo = BlockInfo(h.height, timestamp);
|
||||
return blockInfo;
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -779,7 +782,7 @@ abstract class _SyncStatus with Store {
|
|||
if (currentSyncedHeight != syncedHeight) {
|
||||
await active.update();
|
||||
await priceStore.updateChart();
|
||||
await contacts.fetchContacts();
|
||||
contacts.fetchContacts();
|
||||
}
|
||||
}
|
||||
else if (res == 1) {
|
||||
|
@ -914,28 +917,27 @@ class ContactStore = _ContactStore with _$ContactStore;
|
|||
|
||||
abstract class _ContactStore with Store {
|
||||
@observable
|
||||
ObservableList<Contact> contacts = ObservableList<Contact>.of([]);
|
||||
ObservableList<ContactT> contacts = ObservableList<ContactT>.of([]);
|
||||
|
||||
@action
|
||||
Future<void> fetchContacts() async {
|
||||
final dbr = active.dbReader;
|
||||
void fetchContacts() {
|
||||
contacts.clear();
|
||||
contacts.addAll(await dbr.getContacts());
|
||||
contacts.addAll(WarpApi.getContacts(active.coin));
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> add(Contact c) async {
|
||||
WarpApi.storeContact(c.id, c.name, c.address, true);
|
||||
void add(ContactT c) {
|
||||
WarpApi.storeContact(c.id, c.name!, c.address!, true);
|
||||
markContactsSaved(active.coin, false);
|
||||
await fetchContacts();
|
||||
fetchContacts();
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> remove(Contact c) async {
|
||||
void remove(ContactT c) {
|
||||
contacts.removeWhere((contact) => contact.id == c.id);
|
||||
WarpApi.storeContact(c.id, c.name, "", true);
|
||||
WarpApi.storeContact(c.id, c.name!, "", true);
|
||||
markContactsSaved(active.coin, false);
|
||||
await fetchContacts();
|
||||
fetchContacts();
|
||||
}
|
||||
|
||||
markContactsSaved(int coin, bool v) {
|
||||
|
@ -1016,14 +1018,6 @@ class Tx extends HasHeight {
|
|||
this.address, this.contact, this.memo);
|
||||
}
|
||||
|
||||
class Spending {
|
||||
final String address;
|
||||
final double amount;
|
||||
final String? contact;
|
||||
|
||||
Spending(this.address, this.amount, this.contact);
|
||||
}
|
||||
|
||||
class AccountBalance {
|
||||
final DateTime time;
|
||||
final double balance;
|
||||
|
@ -1031,16 +1025,6 @@ class AccountBalance {
|
|||
AccountBalance(this.time, this.balance);
|
||||
}
|
||||
|
||||
class Contact {
|
||||
final int id;
|
||||
final String name;
|
||||
final String address;
|
||||
|
||||
Contact(this.id, this.name, this.address);
|
||||
|
||||
factory Contact.empty() => Contact(0, "", "");
|
||||
}
|
||||
|
||||
enum SortOrder {
|
||||
Unsorted,
|
||||
Ascending,
|
||||
|
@ -1136,7 +1120,7 @@ class DecodedPaymentURI {
|
|||
|
||||
class SendPageArgs {
|
||||
final bool isMulti;
|
||||
final Contact? contact;
|
||||
final ContactT? contact;
|
||||
final String? address;
|
||||
final String? subject;
|
||||
final String? uri;
|
||||
|
|
|
@ -12,7 +12,6 @@ import network_info_plus_macos
|
|||
import path_provider_macos
|
||||
import share_plus_macos
|
||||
import shared_preferences_macos
|
||||
import sqflite
|
||||
import url_launcher_macos
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
|
@ -23,6 +22,5 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
}
|
||||
|
|
|
@ -7,9 +7,6 @@ PODS:
|
|||
- FlutterMacOS
|
||||
- ReachabilitySwift
|
||||
- FlutterMacOS (1.0.0)
|
||||
- FMDB (2.7.5):
|
||||
- FMDB/standard (= 2.7.5)
|
||||
- FMDB/standard (2.7.5)
|
||||
- network_info_plus_macos (0.0.1):
|
||||
- FlutterMacOS
|
||||
- path_provider_macos (0.0.1):
|
||||
|
@ -19,9 +16,6 @@ PODS:
|
|||
- FlutterMacOS
|
||||
- shared_preferences_macos (0.0.1):
|
||||
- FlutterMacOS
|
||||
- sqflite (0.0.2):
|
||||
- FlutterMacOS
|
||||
- FMDB (>= 2.7.5)
|
||||
- url_launcher_macos (0.0.1):
|
||||
- FlutterMacOS
|
||||
|
||||
|
@ -34,12 +28,10 @@ DEPENDENCIES:
|
|||
- path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`)
|
||||
- share_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos`)
|
||||
- shared_preferences_macos (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos`)
|
||||
- sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`)
|
||||
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- FMDB
|
||||
- ReachabilitySwift
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
|
@ -59,8 +51,6 @@ EXTERNAL SOURCES:
|
|||
:path: Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos
|
||||
shared_preferences_macos:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos
|
||||
sqflite:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/sqflite/macos
|
||||
url_launcher_macos:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
|
||||
|
||||
|
@ -69,13 +59,11 @@ SPEC CHECKSUMS:
|
|||
awesome_notifications: 428f5c15a700b117418aed09e29c21c5806fcf69
|
||||
connectivity_plus_macos: f6e86fd000e971d361e54b5afcadc8c8fa773308
|
||||
FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811
|
||||
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
||||
network_info_plus_macos: d2b9e6c01c291449b91a584217aa53b113847dbd
|
||||
path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19
|
||||
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
||||
share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4
|
||||
shared_preferences_macos: a64dc611287ed6cbe28fd1297898db1336975727
|
||||
sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea
|
||||
url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3
|
||||
|
||||
PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit cd1d84b25d560644ab8933103a881e8a6b3781c7
|
||||
Subproject commit 4168ee4fa223c6dd88a49b5337309a32c14ebeeb
|
File diff suppressed because it is too large
Load Diff
|
@ -183,49 +183,3 @@ class AGEKeys {
|
|||
|
||||
Map<String, dynamic> toJson() => _$AGEKeysToJson(this);
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class Memo {
|
||||
final bool include_reply_to;
|
||||
final String subject;
|
||||
final String body;
|
||||
Memo(this.include_reply_to, this.subject, this.body);
|
||||
|
||||
factory Memo.fromJson(Map<String, dynamic> json) =>
|
||||
_$MemoFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$MemoToJson(this);
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class SendTemplate {
|
||||
final int id;
|
||||
final String title;
|
||||
final String address;
|
||||
final int amount;
|
||||
final double fiat_amount;
|
||||
final bool fee_included;
|
||||
final String? fiat;
|
||||
final Memo memo;
|
||||
SendTemplate(this.id, this.title, this.address, this.amount, this.fiat_amount, this.fee_included, this.fiat, this.memo);
|
||||
|
||||
factory SendTemplate.fromJson(Map<String, dynamic> json) =>
|
||||
_$SendTemplateFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$SendTemplateToJson(this);
|
||||
}
|
||||
|
||||
class SendTemplateId {
|
||||
final int id;
|
||||
final String title;
|
||||
SendTemplateId(this.id, this.title);
|
||||
|
||||
@override
|
||||
operator ==(o) =>
|
||||
o is SendTemplateId &&
|
||||
o.id == id &&
|
||||
o.title == title;
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(id, title);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,8 @@ import 'dart:isolate';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:ffi/ffi.dart';
|
||||
import './warp_api_generated.dart';
|
||||
import 'warp_api_generated.dart';
|
||||
import 'data_fb_generated.dart';
|
||||
import 'types.dart';
|
||||
|
||||
typedef report_callback = Void Function(Int32);
|
||||
|
@ -27,6 +28,16 @@ Pointer<Int8> toNative(String s) {
|
|||
return s.toNativeUtf8().cast<Int8>();
|
||||
}
|
||||
|
||||
Pointer<Uint8> toNativeBytes(Uint8List bytes) {
|
||||
final len = bytes.length;
|
||||
final ptr = malloc.allocate<Uint8>(bytes.length);
|
||||
final list = ptr.asTypedList(bytes.length);
|
||||
for (var i = 0; i < len; i++) {
|
||||
list[i] = bytes[i];
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
int unwrapResultU8(CResult_u8 r) {
|
||||
if (r.error != nullptr) throw convertCString(r.error);
|
||||
return r.value;
|
||||
|
@ -47,6 +58,11 @@ String unwrapResultString(CResult_____c_char r) {
|
|||
return convertCString(r.value);
|
||||
}
|
||||
|
||||
List<int> unwrapResultBytes(CResult______u8 r) {
|
||||
if (r.error != nullptr) throw convertCString(r.error);
|
||||
return convertBytes(r.value, r.len);
|
||||
}
|
||||
|
||||
class WarpApi {
|
||||
static const MethodChannel _channel = const MethodChannel('warp_api');
|
||||
|
||||
|
@ -218,9 +234,9 @@ class WarpApi {
|
|||
return unwrapResultString(txPlan);
|
||||
}
|
||||
|
||||
static Future<void> scanTransparentAccounts(
|
||||
static Future<List<AddressBalance>> scanTransparentAccounts(
|
||||
int coin, int account, int gapLimit) async {
|
||||
await compute(scanTransparentAccountsParamsIsolateFn,
|
||||
return await compute(scanTransparentAccountsParamsIsolateFn,
|
||||
ScanTransparentAccountsParams(gapLimit));
|
||||
}
|
||||
|
||||
|
@ -261,14 +277,6 @@ class WarpApi {
|
|||
return unwrapResultString(res);
|
||||
}
|
||||
|
||||
static int saveSendTemplate(int coin, SendTemplate template) {
|
||||
final templateAsJson = jsonEncode(template);
|
||||
return unwrapResultU32(warp_api_lib.save_send_template(coin, toNative(templateAsJson)));
|
||||
}
|
||||
|
||||
static void deleteSendTemplate(int coin, int id) {
|
||||
warp_api_lib.delete_send_template(coin, id);
|
||||
}
|
||||
// static String ledgerSign(int coin, String txFilename) {
|
||||
// final res = warp_api_lib.ledger_sign(coin, txFilename.toNativeUtf8().cast<Int8>());
|
||||
// return res.cast<Utf8>().toDartString();
|
||||
|
@ -373,7 +381,6 @@ class WarpApi {
|
|||
static String getTxSummary(String tx) {
|
||||
return unwrapResultString(
|
||||
warp_api_lib.get_tx_summary(tx.toNativeUtf8().cast<Int8>()));
|
||||
// TODO: Free
|
||||
}
|
||||
|
||||
static String getBestServer(List<String> urls) {
|
||||
|
@ -413,6 +420,124 @@ class WarpApi {
|
|||
static void useGPU(bool v) {
|
||||
warp_api_lib.use_gpu(v ? 1 : 0);
|
||||
}
|
||||
|
||||
static List<Account> getAccountList(int coin) {
|
||||
final r = unwrapResultBytes(warp_api_lib.get_account_list(coin));
|
||||
return AccountVec(r).accounts!;
|
||||
}
|
||||
|
||||
static int getAvailableAccountId(int coin, int id) {
|
||||
return unwrapResultU32(warp_api_lib.get_available_account_id(coin, id));
|
||||
}
|
||||
|
||||
static String getTAddr(int coin, int id) {
|
||||
return unwrapResultString(warp_api_lib.get_t_addr(coin, id));
|
||||
}
|
||||
|
||||
static String getSK(int coin, int id) {
|
||||
return unwrapResultString(warp_api_lib.get_sk(coin, id));
|
||||
}
|
||||
|
||||
static void updateAccountName(int coin, int id, String name) {
|
||||
warp_api_lib.update_account_name(coin, id, toNative(name));
|
||||
}
|
||||
|
||||
static Balance getBalance(int coin, int id, int confirmedHeight) {
|
||||
final r = unwrapResultBytes(warp_api_lib.get_balances(coin, id, confirmedHeight));
|
||||
final b = Balance(r);
|
||||
return b;
|
||||
}
|
||||
|
||||
static Height? getDbHeight(int coin) {
|
||||
final r = unwrapResultBytes(warp_api_lib.get_db_height(coin));
|
||||
if (r.isEmpty) return null;
|
||||
final h = Height(r);
|
||||
return h;
|
||||
}
|
||||
|
||||
static List<ShieldedNote> getNotes(int coin, int id) {
|
||||
final r = unwrapResultBytes(warp_api_lib.get_notes(coin, id));
|
||||
final ns = ShieldedNoteVec(r);
|
||||
return ns.notes!;
|
||||
}
|
||||
|
||||
static List<ShieldedTx> getTxs(int coin, int id) {
|
||||
final r = unwrapResultBytes(warp_api_lib.get_txs(coin, id));
|
||||
final txs = ShieldedTxVec(r);
|
||||
return txs.txs!;
|
||||
}
|
||||
|
||||
static List<Message> getMessages(int coin, int id) {
|
||||
final r = unwrapResultBytes(warp_api_lib.get_messages(coin, id));
|
||||
final msgs = MessageVec(r);
|
||||
return msgs.messages!;
|
||||
}
|
||||
|
||||
static PrevNext getPrevNextMessage(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);
|
||||
return pn;
|
||||
}
|
||||
|
||||
static List<SendTemplateT> getSendTemplates(int coin) {
|
||||
final r = unwrapResultBytes(warp_api_lib.get_templates(coin));
|
||||
final templates = SendTemplateVec(r).unpack();
|
||||
return templates.templates!;
|
||||
}
|
||||
|
||||
static int saveSendTemplate(int coin, SendTemplateT t) {
|
||||
final template = SendTemplateObjectBuilder(
|
||||
title: t.title,
|
||||
address: t.address,
|
||||
amount: t.amount,
|
||||
feeIncluded: t.feeIncluded,
|
||||
fiatAmount: t.fiatAmount,
|
||||
fiat: t.fiat,
|
||||
includeReplyTo: t.includeReplyTo,
|
||||
subject: t.subject,
|
||||
body: t.body,
|
||||
).toBytes();
|
||||
print("templ $t");
|
||||
final data = toNativeBytes(template);
|
||||
|
||||
return unwrapResultU32(warp_api_lib.save_send_template(coin, data, template.length));
|
||||
}
|
||||
|
||||
static void deleteSendTemplate(int coin, int id) {
|
||||
warp_api_lib.delete_send_template(coin, id);
|
||||
}
|
||||
|
||||
static List<ContactT> getContacts(int coin) {
|
||||
final r = unwrapResultBytes(warp_api_lib.get_contacts(coin));
|
||||
final contacts = ContactVec(r).unpack();
|
||||
return contacts.contacts!;
|
||||
}
|
||||
|
||||
static List<TxTimeValue> getPnLTxs(int coin, int id, int timestamp) {
|
||||
final r = unwrapResultBytes(warp_api_lib.get_pnl_txs(coin, id, timestamp));
|
||||
final txs = TxTimeValueVec(r);
|
||||
return txs.values!;
|
||||
}
|
||||
|
||||
static List<Quote> getQuotes(int coin, int timestamp, String currency) {
|
||||
final r = unwrapResultBytes(warp_api_lib.get_historical_prices(coin, timestamp, toNative(currency)));
|
||||
final quotes = QuoteVec(r);
|
||||
return quotes.values!;
|
||||
}
|
||||
|
||||
static List<Spending> getSpendings(int coin, int id, int timestamp) {
|
||||
final r = unwrapResultBytes(warp_api_lib.get_spendings(coin, id, timestamp));
|
||||
final quotes = SpendingVec(r);
|
||||
return quotes.values!;
|
||||
}
|
||||
|
||||
static void updateExcluded(int coin, int id, bool excluded) {
|
||||
unwrapResultU8(warp_api_lib.update_excluded(coin, id, excluded ? 1 : 0));
|
||||
}
|
||||
|
||||
static void invertExcluded(int coin, int id) {
|
||||
unwrapResultU8(warp_api_lib.invert_excluded(coin, id));
|
||||
}
|
||||
}
|
||||
|
||||
String signOnlyIsolateFn(SignOnlyParams params) {
|
||||
|
@ -450,9 +575,11 @@ int getBlockHeightByTimeIsolateFn(BlockHeightByTimeParams params) {
|
|||
return unwrapResultU32(warp_api_lib.get_block_by_time(params.time));
|
||||
}
|
||||
|
||||
void scanTransparentAccountsParamsIsolateFn(
|
||||
List<AddressBalance> scanTransparentAccountsParamsIsolateFn(
|
||||
ScanTransparentAccountsParams params) {
|
||||
return warp_api_lib.scan_transparent_accounts(params.gapLimit);
|
||||
final r = unwrapResultBytes(warp_api_lib.scan_transparent_accounts(params.gapLimit));
|
||||
final v = AddressBalanceVec(r);
|
||||
return v.values!;
|
||||
}
|
||||
|
||||
class SyncParams {
|
||||
|
@ -532,6 +659,12 @@ String convertCString(Pointer<Int8> s) {
|
|||
return str;
|
||||
}
|
||||
|
||||
List<int> convertBytes(Pointer<Uint8> s, int len) {
|
||||
final bytes = [...s.asTypedList(len)];
|
||||
warp_api_lib.deallocate_bytes(s, len);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
void mempoolRunIsolateFn(int port) {
|
||||
warp_api_lib.mempool_run(port);
|
||||
}
|
||||
|
|
|
@ -53,6 +53,21 @@ class NativeLibrary {
|
|||
late final _dart_deallocate_str _deallocate_str =
|
||||
_deallocate_str_ptr.asFunction<_dart_deallocate_str>();
|
||||
|
||||
void deallocate_bytes(
|
||||
ffi.Pointer<ffi.Uint8> ptr,
|
||||
int len,
|
||||
) {
|
||||
return _deallocate_bytes(
|
||||
ptr,
|
||||
len,
|
||||
);
|
||||
}
|
||||
|
||||
late final _deallocate_bytes_ptr =
|
||||
_lookup<ffi.NativeFunction<_c_deallocate_bytes>>('deallocate_bytes');
|
||||
late final _dart_deallocate_bytes _deallocate_bytes =
|
||||
_deallocate_bytes_ptr.asFunction<_dart_deallocate_bytes>();
|
||||
|
||||
CResult_u8 init_wallet(
|
||||
int coin,
|
||||
ffi.Pointer<ffi.Int8> db_path,
|
||||
|
@ -494,7 +509,7 @@ class NativeLibrary {
|
|||
late final _dart_shield_taddr _shield_taddr =
|
||||
_shield_taddr_ptr.asFunction<_dart_shield_taddr>();
|
||||
|
||||
void scan_transparent_accounts(
|
||||
CResult______u8 scan_transparent_accounts(
|
||||
int gap_limit,
|
||||
) {
|
||||
return _scan_transparent_accounts(
|
||||
|
@ -892,13 +907,197 @@ class NativeLibrary {
|
|||
late final _dart_derive_zip32 _derive_zip32 =
|
||||
_derive_zip32_ptr.asFunction<_dart_derive_zip32>();
|
||||
|
||||
CResult______u8 get_account_list(
|
||||
int coin,
|
||||
) {
|
||||
return _get_account_list(
|
||||
coin,
|
||||
);
|
||||
}
|
||||
|
||||
late final _get_account_list_ptr =
|
||||
_lookup<ffi.NativeFunction<_c_get_account_list>>('get_account_list');
|
||||
late final _dart_get_account_list _get_account_list =
|
||||
_get_account_list_ptr.asFunction<_dart_get_account_list>();
|
||||
|
||||
CResult_u32 get_available_account_id(
|
||||
int coin,
|
||||
int id,
|
||||
) {
|
||||
return _get_available_account_id(
|
||||
coin,
|
||||
id,
|
||||
);
|
||||
}
|
||||
|
||||
late final _get_available_account_id_ptr =
|
||||
_lookup<ffi.NativeFunction<_c_get_available_account_id>>(
|
||||
'get_available_account_id');
|
||||
late final _dart_get_available_account_id _get_available_account_id =
|
||||
_get_available_account_id_ptr
|
||||
.asFunction<_dart_get_available_account_id>();
|
||||
|
||||
CResult_____c_char get_t_addr(
|
||||
int coin,
|
||||
int id,
|
||||
) {
|
||||
return _get_t_addr(
|
||||
coin,
|
||||
id,
|
||||
);
|
||||
}
|
||||
|
||||
late final _get_t_addr_ptr =
|
||||
_lookup<ffi.NativeFunction<_c_get_t_addr>>('get_t_addr');
|
||||
late final _dart_get_t_addr _get_t_addr =
|
||||
_get_t_addr_ptr.asFunction<_dart_get_t_addr>();
|
||||
|
||||
CResult_____c_char get_sk(
|
||||
int coin,
|
||||
int id,
|
||||
) {
|
||||
return _get_sk(
|
||||
coin,
|
||||
id,
|
||||
);
|
||||
}
|
||||
|
||||
late final _get_sk_ptr = _lookup<ffi.NativeFunction<_c_get_sk>>('get_sk');
|
||||
late final _dart_get_sk _get_sk = _get_sk_ptr.asFunction<_dart_get_sk>();
|
||||
|
||||
CResult_u8 update_account_name(
|
||||
int coin,
|
||||
int id,
|
||||
ffi.Pointer<ffi.Int8> name,
|
||||
) {
|
||||
return _update_account_name(
|
||||
coin,
|
||||
id,
|
||||
name,
|
||||
);
|
||||
}
|
||||
|
||||
late final _update_account_name_ptr =
|
||||
_lookup<ffi.NativeFunction<_c_update_account_name>>(
|
||||
'update_account_name');
|
||||
late final _dart_update_account_name _update_account_name =
|
||||
_update_account_name_ptr.asFunction<_dart_update_account_name>();
|
||||
|
||||
CResult______u8 get_balances(
|
||||
int coin,
|
||||
int id,
|
||||
int confirmed_height,
|
||||
) {
|
||||
return _get_balances(
|
||||
coin,
|
||||
id,
|
||||
confirmed_height,
|
||||
);
|
||||
}
|
||||
|
||||
late final _get_balances_ptr =
|
||||
_lookup<ffi.NativeFunction<_c_get_balances>>('get_balances');
|
||||
late final _dart_get_balances _get_balances =
|
||||
_get_balances_ptr.asFunction<_dart_get_balances>();
|
||||
|
||||
CResult______u8 get_db_height(
|
||||
int coin,
|
||||
) {
|
||||
return _get_db_height(
|
||||
coin,
|
||||
);
|
||||
}
|
||||
|
||||
late final _get_db_height_ptr =
|
||||
_lookup<ffi.NativeFunction<_c_get_db_height>>('get_db_height');
|
||||
late final _dart_get_db_height _get_db_height =
|
||||
_get_db_height_ptr.asFunction<_dart_get_db_height>();
|
||||
|
||||
CResult______u8 get_notes(
|
||||
int coin,
|
||||
int id,
|
||||
) {
|
||||
return _get_notes(
|
||||
coin,
|
||||
id,
|
||||
);
|
||||
}
|
||||
|
||||
late final _get_notes_ptr =
|
||||
_lookup<ffi.NativeFunction<_c_get_notes>>('get_notes');
|
||||
late final _dart_get_notes _get_notes =
|
||||
_get_notes_ptr.asFunction<_dart_get_notes>();
|
||||
|
||||
CResult______u8 get_txs(
|
||||
int coin,
|
||||
int id,
|
||||
) {
|
||||
return _get_txs(
|
||||
coin,
|
||||
id,
|
||||
);
|
||||
}
|
||||
|
||||
late final _get_txs_ptr = _lookup<ffi.NativeFunction<_c_get_txs>>('get_txs');
|
||||
late final _dart_get_txs _get_txs = _get_txs_ptr.asFunction<_dart_get_txs>();
|
||||
|
||||
CResult______u8 get_messages(
|
||||
int coin,
|
||||
int id,
|
||||
) {
|
||||
return _get_messages(
|
||||
coin,
|
||||
id,
|
||||
);
|
||||
}
|
||||
|
||||
late final _get_messages_ptr =
|
||||
_lookup<ffi.NativeFunction<_c_get_messages>>('get_messages');
|
||||
late final _dart_get_messages _get_messages =
|
||||
_get_messages_ptr.asFunction<_dart_get_messages>();
|
||||
|
||||
CResult______u8 get_prev_next_message(
|
||||
int coin,
|
||||
int id,
|
||||
ffi.Pointer<ffi.Int8> subject,
|
||||
int height,
|
||||
) {
|
||||
return _get_prev_next_message(
|
||||
coin,
|
||||
id,
|
||||
subject,
|
||||
height,
|
||||
);
|
||||
}
|
||||
|
||||
late final _get_prev_next_message_ptr =
|
||||
_lookup<ffi.NativeFunction<_c_get_prev_next_message>>(
|
||||
'get_prev_next_message');
|
||||
late final _dart_get_prev_next_message _get_prev_next_message =
|
||||
_get_prev_next_message_ptr.asFunction<_dart_get_prev_next_message>();
|
||||
|
||||
CResult______u8 get_templates(
|
||||
int coin,
|
||||
) {
|
||||
return _get_templates(
|
||||
coin,
|
||||
);
|
||||
}
|
||||
|
||||
late final _get_templates_ptr =
|
||||
_lookup<ffi.NativeFunction<_c_get_templates>>('get_templates');
|
||||
late final _dart_get_templates _get_templates =
|
||||
_get_templates_ptr.asFunction<_dart_get_templates>();
|
||||
|
||||
CResult_u32 save_send_template(
|
||||
int coin,
|
||||
ffi.Pointer<ffi.Int8> template_,
|
||||
ffi.Pointer<ffi.Uint8> template_,
|
||||
int len,
|
||||
) {
|
||||
return _save_send_template(
|
||||
coin,
|
||||
template_,
|
||||
len,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -923,6 +1122,103 @@ class NativeLibrary {
|
|||
late final _dart_delete_send_template _delete_send_template =
|
||||
_delete_send_template_ptr.asFunction<_dart_delete_send_template>();
|
||||
|
||||
CResult______u8 get_contacts(
|
||||
int coin,
|
||||
) {
|
||||
return _get_contacts(
|
||||
coin,
|
||||
);
|
||||
}
|
||||
|
||||
late final _get_contacts_ptr =
|
||||
_lookup<ffi.NativeFunction<_c_get_contacts>>('get_contacts');
|
||||
late final _dart_get_contacts _get_contacts =
|
||||
_get_contacts_ptr.asFunction<_dart_get_contacts>();
|
||||
|
||||
CResult______u8 get_pnl_txs(
|
||||
int coin,
|
||||
int id,
|
||||
int timestamp,
|
||||
) {
|
||||
return _get_pnl_txs(
|
||||
coin,
|
||||
id,
|
||||
timestamp,
|
||||
);
|
||||
}
|
||||
|
||||
late final _get_pnl_txs_ptr =
|
||||
_lookup<ffi.NativeFunction<_c_get_pnl_txs>>('get_pnl_txs');
|
||||
late final _dart_get_pnl_txs _get_pnl_txs =
|
||||
_get_pnl_txs_ptr.asFunction<_dart_get_pnl_txs>();
|
||||
|
||||
CResult______u8 get_historical_prices(
|
||||
int coin,
|
||||
int timestamp,
|
||||
ffi.Pointer<ffi.Int8> currency,
|
||||
) {
|
||||
return _get_historical_prices(
|
||||
coin,
|
||||
timestamp,
|
||||
currency,
|
||||
);
|
||||
}
|
||||
|
||||
late final _get_historical_prices_ptr =
|
||||
_lookup<ffi.NativeFunction<_c_get_historical_prices>>(
|
||||
'get_historical_prices');
|
||||
late final _dart_get_historical_prices _get_historical_prices =
|
||||
_get_historical_prices_ptr.asFunction<_dart_get_historical_prices>();
|
||||
|
||||
CResult______u8 get_spendings(
|
||||
int coin,
|
||||
int id,
|
||||
int timestamp,
|
||||
) {
|
||||
return _get_spendings(
|
||||
coin,
|
||||
id,
|
||||
timestamp,
|
||||
);
|
||||
}
|
||||
|
||||
late final _get_spendings_ptr =
|
||||
_lookup<ffi.NativeFunction<_c_get_spendings>>('get_spendings');
|
||||
late final _dart_get_spendings _get_spendings =
|
||||
_get_spendings_ptr.asFunction<_dart_get_spendings>();
|
||||
|
||||
CResult_u8 update_excluded(
|
||||
int coin,
|
||||
int id,
|
||||
int excluded,
|
||||
) {
|
||||
return _update_excluded(
|
||||
coin,
|
||||
id,
|
||||
excluded,
|
||||
);
|
||||
}
|
||||
|
||||
late final _update_excluded_ptr =
|
||||
_lookup<ffi.NativeFunction<_c_update_excluded>>('update_excluded');
|
||||
late final _dart_update_excluded _update_excluded =
|
||||
_update_excluded_ptr.asFunction<_dart_update_excluded>();
|
||||
|
||||
CResult_u8 invert_excluded(
|
||||
int coin,
|
||||
int id,
|
||||
) {
|
||||
return _invert_excluded(
|
||||
coin,
|
||||
id,
|
||||
);
|
||||
}
|
||||
|
||||
late final _invert_excluded_ptr =
|
||||
_lookup<ffi.NativeFunction<_c_invert_excluded>>('invert_excluded');
|
||||
late final _dart_invert_excluded _invert_excluded =
|
||||
_invert_excluded_ptr.asFunction<_dart_invert_excluded>();
|
||||
|
||||
int has_cuda() {
|
||||
return _has_cuda();
|
||||
}
|
||||
|
@ -965,6 +1261,9 @@ class CResult_u8 extends ffi.Struct {
|
|||
external int value;
|
||||
|
||||
external ffi.Pointer<ffi.Int8> error;
|
||||
|
||||
@ffi.Uint32()
|
||||
external int len;
|
||||
}
|
||||
|
||||
class CResult_u32 extends ffi.Struct {
|
||||
|
@ -972,12 +1271,18 @@ class CResult_u32 extends ffi.Struct {
|
|||
external int value;
|
||||
|
||||
external ffi.Pointer<ffi.Int8> error;
|
||||
|
||||
@ffi.Uint32()
|
||||
external int len;
|
||||
}
|
||||
|
||||
class CResult_____c_char extends ffi.Struct {
|
||||
external ffi.Pointer<ffi.Int8> value;
|
||||
|
||||
external ffi.Pointer<ffi.Int8> error;
|
||||
|
||||
@ffi.Uint32()
|
||||
external int len;
|
||||
}
|
||||
|
||||
class CResult_u64 extends ffi.Struct {
|
||||
|
@ -985,6 +1290,18 @@ class CResult_u64 extends ffi.Struct {
|
|||
external int value;
|
||||
|
||||
external ffi.Pointer<ffi.Int8> error;
|
||||
|
||||
@ffi.Uint32()
|
||||
external int len;
|
||||
}
|
||||
|
||||
class CResult______u8 extends ffi.Struct {
|
||||
external ffi.Pointer<ffi.Uint8> value;
|
||||
|
||||
external ffi.Pointer<ffi.Int8> error;
|
||||
|
||||
@ffi.Uint32()
|
||||
external int len;
|
||||
}
|
||||
|
||||
const int QR_DATA_SIZE = 256;
|
||||
|
@ -993,6 +1310,92 @@ const int MAX_ATTEMPTS = 10;
|
|||
|
||||
const int N = 200000;
|
||||
|
||||
const int Account_VT_ID = 4;
|
||||
|
||||
const int Account_VT_NAME = 6;
|
||||
|
||||
const int Account_VT_BALANCE = 8;
|
||||
|
||||
const int AccountVec_VT_ACCOUNTS = 4;
|
||||
|
||||
const int Balance_VT_SHIELDED = 4;
|
||||
|
||||
const int Balance_VT_UNCONFIRMED_SPENT = 6;
|
||||
|
||||
const int Balance_VT_UNDER_CONFIRMED = 10;
|
||||
|
||||
const int Balance_VT_EXCLUDED = 12;
|
||||
|
||||
const int Balance_VT_SAPLING = 14;
|
||||
|
||||
const int Balance_VT_ORCHARD = 16;
|
||||
|
||||
const int Height_VT_HEIGHT = 4;
|
||||
|
||||
const int Height_VT_TIMESTAMP = 6;
|
||||
|
||||
const int ShieldedNote_VT_VALUE = 8;
|
||||
|
||||
const int ShieldedNote_VT_SPENT = 16;
|
||||
|
||||
const int ShieldedNoteVec_VT_NOTES = 4;
|
||||
|
||||
const int ShieldedTx_VT_TX_ID = 6;
|
||||
|
||||
const int ShieldedTx_VT_SHORT_TX_ID = 10;
|
||||
|
||||
const int ShieldedTx_VT_ADDRESS = 18;
|
||||
|
||||
const int ShieldedTx_VT_MEMO = 20;
|
||||
|
||||
const int ShieldedTxVec_VT_TXS = 4;
|
||||
|
||||
const int Message_VT_ID_MSG = 4;
|
||||
|
||||
const int Message_VT_ID_TX = 6;
|
||||
|
||||
const int Message_VT_FROM = 12;
|
||||
|
||||
const int Message_VT_TO = 14;
|
||||
|
||||
const int Message_VT_SUBJECT = 16;
|
||||
|
||||
const int Message_VT_BODY = 18;
|
||||
|
||||
const int Message_VT_READ = 20;
|
||||
|
||||
const int Message_VT_INCOMING = 22;
|
||||
|
||||
const int MessageVec_VT_MESSAGES = 4;
|
||||
|
||||
const int PrevNext_VT_PREV = 4;
|
||||
|
||||
const int PrevNext_VT_NEXT = 6;
|
||||
|
||||
const int SendTemplate_VT_TITLE = 6;
|
||||
|
||||
const int SendTemplate_VT_AMOUNT = 10;
|
||||
|
||||
const int SendTemplate_VT_FIAT_AMOUNT = 12;
|
||||
|
||||
const int SendTemplate_VT_FEE_INCLUDED = 14;
|
||||
|
||||
const int SendTemplate_VT_FIAT = 16;
|
||||
|
||||
const int SendTemplate_VT_INCLUDE_REPLY_TO = 18;
|
||||
|
||||
const int SendTemplateVec_VT_TEMPLATES = 4;
|
||||
|
||||
const int ContactVec_VT_CONTACTS = 4;
|
||||
|
||||
const int TxTimeValueVec_VT_VALUES = 4;
|
||||
|
||||
const int Quote_VT_PRICE = 6;
|
||||
|
||||
const int Spending_VT_RECIPIENT = 4;
|
||||
|
||||
const int AddressBalance_VT_INDEX = 4;
|
||||
|
||||
typedef _c_dummy_export = ffi.Void Function();
|
||||
|
||||
typedef _dart_dummy_export = void Function();
|
||||
|
@ -1013,6 +1416,16 @@ typedef _dart_deallocate_str = void Function(
|
|||
ffi.Pointer<ffi.Int8> s,
|
||||
);
|
||||
|
||||
typedef _c_deallocate_bytes = ffi.Void Function(
|
||||
ffi.Pointer<ffi.Uint8> ptr,
|
||||
ffi.Uint32 len,
|
||||
);
|
||||
|
||||
typedef _dart_deallocate_bytes = void Function(
|
||||
ffi.Pointer<ffi.Uint8> ptr,
|
||||
int len,
|
||||
);
|
||||
|
||||
typedef _c_init_wallet = CResult_u8 Function(
|
||||
ffi.Uint8 coin,
|
||||
ffi.Pointer<ffi.Int8> db_path,
|
||||
|
@ -1305,11 +1718,11 @@ typedef _dart_shield_taddr = CResult_____c_char Function(
|
|||
int confirmations,
|
||||
);
|
||||
|
||||
typedef _c_scan_transparent_accounts = ffi.Void Function(
|
||||
typedef _c_scan_transparent_accounts = CResult______u8 Function(
|
||||
ffi.Uint32 gap_limit,
|
||||
);
|
||||
|
||||
typedef _dart_scan_transparent_accounts = void Function(
|
||||
typedef _dart_scan_transparent_accounts = CResult______u8 Function(
|
||||
int gap_limit,
|
||||
);
|
||||
|
||||
|
@ -1563,14 +1976,138 @@ typedef _dart_derive_zip32 = CResult_____c_char Function(
|
|||
int address,
|
||||
);
|
||||
|
||||
typedef _c_get_account_list = CResult______u8 Function(
|
||||
ffi.Uint8 coin,
|
||||
);
|
||||
|
||||
typedef _dart_get_account_list = CResult______u8 Function(
|
||||
int coin,
|
||||
);
|
||||
|
||||
typedef _c_get_available_account_id = CResult_u32 Function(
|
||||
ffi.Uint8 coin,
|
||||
ffi.Uint32 id,
|
||||
);
|
||||
|
||||
typedef _dart_get_available_account_id = CResult_u32 Function(
|
||||
int coin,
|
||||
int id,
|
||||
);
|
||||
|
||||
typedef _c_get_t_addr = CResult_____c_char Function(
|
||||
ffi.Uint8 coin,
|
||||
ffi.Uint32 id,
|
||||
);
|
||||
|
||||
typedef _dart_get_t_addr = CResult_____c_char Function(
|
||||
int coin,
|
||||
int id,
|
||||
);
|
||||
|
||||
typedef _c_get_sk = CResult_____c_char Function(
|
||||
ffi.Uint8 coin,
|
||||
ffi.Uint32 id,
|
||||
);
|
||||
|
||||
typedef _dart_get_sk = CResult_____c_char Function(
|
||||
int coin,
|
||||
int id,
|
||||
);
|
||||
|
||||
typedef _c_update_account_name = CResult_u8 Function(
|
||||
ffi.Uint8 coin,
|
||||
ffi.Uint32 id,
|
||||
ffi.Pointer<ffi.Int8> name,
|
||||
);
|
||||
|
||||
typedef _dart_update_account_name = CResult_u8 Function(
|
||||
int coin,
|
||||
int id,
|
||||
ffi.Pointer<ffi.Int8> name,
|
||||
);
|
||||
|
||||
typedef _c_get_balances = CResult______u8 Function(
|
||||
ffi.Uint8 coin,
|
||||
ffi.Uint32 id,
|
||||
ffi.Uint32 confirmed_height,
|
||||
);
|
||||
|
||||
typedef _dart_get_balances = CResult______u8 Function(
|
||||
int coin,
|
||||
int id,
|
||||
int confirmed_height,
|
||||
);
|
||||
|
||||
typedef _c_get_db_height = CResult______u8 Function(
|
||||
ffi.Uint8 coin,
|
||||
);
|
||||
|
||||
typedef _dart_get_db_height = CResult______u8 Function(
|
||||
int coin,
|
||||
);
|
||||
|
||||
typedef _c_get_notes = CResult______u8 Function(
|
||||
ffi.Uint8 coin,
|
||||
ffi.Uint32 id,
|
||||
);
|
||||
|
||||
typedef _dart_get_notes = CResult______u8 Function(
|
||||
int coin,
|
||||
int id,
|
||||
);
|
||||
|
||||
typedef _c_get_txs = CResult______u8 Function(
|
||||
ffi.Uint8 coin,
|
||||
ffi.Uint32 id,
|
||||
);
|
||||
|
||||
typedef _dart_get_txs = CResult______u8 Function(
|
||||
int coin,
|
||||
int id,
|
||||
);
|
||||
|
||||
typedef _c_get_messages = CResult______u8 Function(
|
||||
ffi.Uint8 coin,
|
||||
ffi.Uint32 id,
|
||||
);
|
||||
|
||||
typedef _dart_get_messages = CResult______u8 Function(
|
||||
int coin,
|
||||
int id,
|
||||
);
|
||||
|
||||
typedef _c_get_prev_next_message = CResult______u8 Function(
|
||||
ffi.Uint8 coin,
|
||||
ffi.Uint32 id,
|
||||
ffi.Pointer<ffi.Int8> subject,
|
||||
ffi.Uint32 height,
|
||||
);
|
||||
|
||||
typedef _dart_get_prev_next_message = CResult______u8 Function(
|
||||
int coin,
|
||||
int id,
|
||||
ffi.Pointer<ffi.Int8> subject,
|
||||
int height,
|
||||
);
|
||||
|
||||
typedef _c_get_templates = CResult______u8 Function(
|
||||
ffi.Uint8 coin,
|
||||
);
|
||||
|
||||
typedef _dart_get_templates = CResult______u8 Function(
|
||||
int coin,
|
||||
);
|
||||
|
||||
typedef _c_save_send_template = CResult_u32 Function(
|
||||
ffi.Uint8 coin,
|
||||
ffi.Pointer<ffi.Int8> template_,
|
||||
ffi.Pointer<ffi.Uint8> template_,
|
||||
ffi.Uint64 len,
|
||||
);
|
||||
|
||||
typedef _dart_save_send_template = CResult_u32 Function(
|
||||
int coin,
|
||||
ffi.Pointer<ffi.Int8> template_,
|
||||
ffi.Pointer<ffi.Uint8> template_,
|
||||
int len,
|
||||
);
|
||||
|
||||
typedef _c_delete_send_template = CResult_u8 Function(
|
||||
|
@ -1583,6 +2120,72 @@ typedef _dart_delete_send_template = CResult_u8 Function(
|
|||
int id,
|
||||
);
|
||||
|
||||
typedef _c_get_contacts = CResult______u8 Function(
|
||||
ffi.Uint8 coin,
|
||||
);
|
||||
|
||||
typedef _dart_get_contacts = CResult______u8 Function(
|
||||
int coin,
|
||||
);
|
||||
|
||||
typedef _c_get_pnl_txs = CResult______u8 Function(
|
||||
ffi.Uint8 coin,
|
||||
ffi.Uint32 id,
|
||||
ffi.Uint32 timestamp,
|
||||
);
|
||||
|
||||
typedef _dart_get_pnl_txs = CResult______u8 Function(
|
||||
int coin,
|
||||
int id,
|
||||
int timestamp,
|
||||
);
|
||||
|
||||
typedef _c_get_historical_prices = CResult______u8 Function(
|
||||
ffi.Uint8 coin,
|
||||
ffi.Uint32 timestamp,
|
||||
ffi.Pointer<ffi.Int8> currency,
|
||||
);
|
||||
|
||||
typedef _dart_get_historical_prices = CResult______u8 Function(
|
||||
int coin,
|
||||
int timestamp,
|
||||
ffi.Pointer<ffi.Int8> currency,
|
||||
);
|
||||
|
||||
typedef _c_get_spendings = CResult______u8 Function(
|
||||
ffi.Uint8 coin,
|
||||
ffi.Uint32 id,
|
||||
ffi.Uint32 timestamp,
|
||||
);
|
||||
|
||||
typedef _dart_get_spendings = CResult______u8 Function(
|
||||
int coin,
|
||||
int id,
|
||||
int timestamp,
|
||||
);
|
||||
|
||||
typedef _c_update_excluded = CResult_u8 Function(
|
||||
ffi.Uint8 coin,
|
||||
ffi.Uint32 id,
|
||||
ffi.Int8 excluded,
|
||||
);
|
||||
|
||||
typedef _dart_update_excluded = CResult_u8 Function(
|
||||
int coin,
|
||||
int id,
|
||||
int excluded,
|
||||
);
|
||||
|
||||
typedef _c_invert_excluded = CResult_u8 Function(
|
||||
ffi.Uint8 coin,
|
||||
ffi.Uint32 id,
|
||||
);
|
||||
|
||||
typedef _dart_invert_excluded = CResult_u8 Function(
|
||||
int coin,
|
||||
int id,
|
||||
);
|
||||
|
||||
typedef _c_has_cuda = ffi.Int8 Function();
|
||||
|
||||
typedef _dart_has_cuda = int Function();
|
||||
|
|
|
@ -197,6 +197,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
flat_buffers:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flat_buffers
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.5"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
|
|
@ -14,6 +14,7 @@ dependencies:
|
|||
convert: ^3.0.1
|
||||
protobuf: ^2.0.0
|
||||
json_annotation: ^4.1.0
|
||||
flat_buffers: ^2.0.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
@ -28,8 +29,8 @@ ffigen:
|
|||
entry-points:
|
||||
- '../../native/zcash-sync/binding.h'
|
||||
# On MacOS
|
||||
# llvm-path:
|
||||
# - '/opt/homebrew/Cellar/llvm/15.0.6'
|
||||
llvm-path:
|
||||
- '/opt/homebrew/Cellar/llvm/15.0.6'
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
|
42
pubspec.lock
42
pubspec.lock
|
@ -395,6 +395,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.50.1"
|
||||
flat_buffers:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flat_buffers
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.5"
|
||||
flex_color_scheme:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -1238,34 +1245,6 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
sqflite:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: sqflite
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.2+1"
|
||||
sqflite_common:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_common
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.2.1+1"
|
||||
sqflite_common_ffi:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: sqflite_common_ffi
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
sqlite3:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqlite3
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.7.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1294,13 +1273,6 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
synchronized:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: synchronized
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.0+2"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -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.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
version: 1.2.17+336
|
||||
version: 1.2.17+337
|
||||
|
||||
environment:
|
||||
sdk: ">=2.12.0 <3.0.0"
|
||||
|
@ -25,8 +25,6 @@ dependencies:
|
|||
sdk: flutter
|
||||
warp_api:
|
||||
path: packages/warp_api_ffi
|
||||
sqflite: ^2.0.2
|
||||
sqflite_common_ffi: ^2.1.0
|
||||
flutter_mobx: ^2.0.2
|
||||
qr_flutter: ^4.0.0
|
||||
http: ^0.13.3
|
||||
|
|
Loading…
Reference in New Issue