2022-03-07 06:53:18 -08:00
import ' package:shared_preferences/shared_preferences.dart ' ;
2022-08-29 20:42:08 -07:00
import ' package:warp_api/types.dart ' ;
2022-03-07 06:53:18 -08:00
import ' coin/coins.dart ' ;
import ' package:mobx/mobx.dart ' ;
import ' db.dart ' ;
import ' package:warp_api/warp_api.dart ' ;
import ' backup.dart ' ;
import ' coin/coin.dart ' ;
import ' coin/zcash.dart ' ;
import ' main.dart ' ;
import ' store.dart ' ;
part ' accounts.g.dart ' ;
class Account {
final int coin ;
final int id ;
final String name ;
final int balance ;
int tbalance = 0 ;
final ShareInfo ? share ;
2022-10-23 03:30:28 -07:00
Account ( this . coin , this . id , this . name , this . balance , this . tbalance , this . share ) ;
String get address {
2022-11-22 01:54:28 -08:00
return id ! = 0 ? WarpApi . getAddress ( this . coin , this . id , settings . uaType ) : " " ;
2022-10-23 03:30:28 -07:00
}
2022-03-07 06:53:18 -08:00
}
2022-10-23 03:30:28 -07:00
final Account emptyAccount = Account ( 0 , 0 , " " , 0 , 0 , null ) ;
2022-03-07 06:53:18 -08:00
class AccountManager2 = _AccountManager2 with _ $AccountManager2 ;
abstract class _AccountManager2 with Store {
@ observable int epoch = 0 ;
List < Account > list = [ ] ;
@ action
Future < void > refresh ( ) async {
List < Account > _list = [ ] ;
_list . addAll ( await _getList ( 0 ) ) ;
_list . addAll ( await _getList ( 1 ) ) ;
list = _list ;
epoch + = 1 ;
}
2022-07-07 20:26:20 -07:00
bool get isEmpty { return list . isEmpty ; }
2022-03-07 06:53:18 -08:00
@ action
Future < void > updateTBalance ( ) async {
for ( var a in list ) {
final tbalance = await WarpApi . getTBalanceAsync ( a . coin , a . id ) ;
a . tbalance = tbalance ;
}
epoch + = 1 ;
}
@ action
Future < void > delete ( int coin , int id ) async {
WarpApi . deleteAccount ( coin , id ) ;
2022-08-21 23:37:29 -07:00
await active . checkAndUpdate ( ) ;
2022-03-07 06:53:18 -08:00
}
@ action
Future < void > changeAccountName ( int coin , int id , String name ) async {
final c = settings . coins [ coin ] . def ; // TODO: Do in backend would be cleaner
final db = c . db ;
await db . execute ( " UPDATE accounts SET name = ?2 WHERE id_account = ?1 " ,
[ id , name ] ) ;
await refresh ( ) ;
}
void saveActive ( int coin , int id ) {
settings . coins [ coin ] . active = id ;
Future . microtask ( ( ) async {
final prefs = await SharedPreferences . getInstance ( ) ;
final def = settings . coins [ coin ] . def ;
prefs . setInt ( " ${ def . ticker } .active " , id ) ;
} ) ;
}
Account get ( int coin , int id ) = > list . firstWhere ( ( e ) = > e . coin = = coin & & e . id = = id , orElse: ( ) = > emptyAccount ) ;
2022-04-07 05:59:43 -07:00
Future < bool > hasAccount ( int coin ) async {
final c = settings . coins [ coin ] . def ;
final db = c . db ;
final List < Map > res = await db . rawQuery (
" SELECT 1 FROM accounts " ,
[ ] ) ;
return res . isNotEmpty ;
}
2022-03-07 06:53:18 -08:00
static Future < List < Account > > _getList ( int coin ) async {
final c = settings . coins [ coin ] . def ;
final db = c . db ;
List < Account > accounts = [ ] ;
final List < Map > res = await db . rawQuery (
2022-10-23 03:30:28 -07:00
" 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 " ,
2022-03-07 06:53:18 -08:00
[ ] ) ;
for ( var r in res ) {
final int id = r [ ' id_account ' ] ;
2022-03-14 02:35:37 -07:00
final account = Account (
coin ,
id ,
r [ ' name ' ] ,
r [ ' balance ' ] ,
0 ,
null ) ;
2022-03-07 06:53:18 -08:00
accounts . add ( account ) ;
}
return accounts ;
}
}
class ActiveAccount = _ActiveAccount with _ $ActiveAccount ;
abstract class _ActiveAccount with Store {
2022-03-27 22:55:48 -07:00
@ observable int dataEpoch = 0 ;
2022-03-07 06:53:18 -08:00
2022-03-27 22:55:48 -07:00
@ observable int coin = 0 ;
2022-03-31 01:20:26 -07:00
@ observable int id = 0 ;
2022-03-07 06:53:18 -08:00
Account account = emptyAccount ;
2022-07-07 20:26:20 -07:00
CoinBase coinDef = zcash ;
2022-03-07 06:53:18 -08:00
bool canPay = false ;
2022-11-12 19:43:09 -08:00
Balances balances = Balances ( ) ;
2022-07-16 20:29:51 -07:00
@ observable String taddress = " " ;
2022-03-07 06:53:18 -08:00
int tbalance = 0 ;
@ observable List < Note > notes = [ ] ;
@ observable List < Tx > txs = [ ] ;
@ observable List < Spending > spendings = [ ] ;
@ observable List < TimeSeriesPoint < double > > accountBalances = [ ] ;
@ observable List < PnL > pnls = [ ] ;
2022-04-15 23:51:13 -07:00
@ observable ObservableList < ZMessage > messages = ObservableList ( ) ;
2022-04-16 23:03:33 -07:00
@ observable int unread = 0 ;
2022-06-16 03:16:32 -07:00
@ observable String banner = " " ;
2022-03-07 06:53:18 -08:00
@ observable
bool showTAddr = false ;
@ observable
SortConfig noteSortConfig = SortConfig ( " " , SortOrder . Unsorted ) ;
@ observable
SortConfig txSortConfig = SortConfig ( " " , SortOrder . Unsorted ) ;
@ observable
int pnlSeriesIndex = 0 ;
@ observable
bool pnlDesc = false ;
2022-08-29 20:42:08 -07:00
@ observable
Recipient ? draftRecipient = null ;
2022-08-21 23:37:29 -07:00
AccountId toId ( ) { return AccountId ( coin , id ) ; }
2022-03-07 06:53:18 -08:00
@ action
Future < void > restore ( ) async {
final prefs = await SharedPreferences . getInstance ( ) ;
2022-07-08 18:51:23 -07:00
final coin = prefs . getInt ( ' coin ' ) ? ? 0 ;
2022-08-21 23:37:29 -07:00
var id = prefs . getInt ( ' account ' ) ? ? 0 ;
2022-08-26 05:59:21 -07:00
await setActiveAccount ( coin , id ) ;
2022-08-21 23:37:29 -07:00
await checkAndUpdate ( ) ;
await refreshAccount ( ) ;
}
Future < void > checkAndUpdate ( ) async {
final aid = await getAvailableId ( active . toId ( ) ) ;
if ( aid = = null ) {
await setActiveAccount ( 0 , 0 ) ;
}
else if ( aid ! = active . toId ( ) ) {
await setActiveAccount ( aid . coin , aid . id ) ;
await refreshAccount ( ) ;
}
2022-03-07 06:53:18 -08:00
}
2022-08-21 23:37:29 -07:00
Future < AccountId ? > getAvailableId ( AccountId aid ) async {
final nid = await getAvailableIdForCoin ( aid . coin , aid . id ) ;
if ( nid ! = null ) return nid ;
2022-07-07 20:26:20 -07:00
for ( var coin_data in settings . coins ) {
2022-08-21 23:37:29 -07:00
// 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 )
return nid ;
}
2022-07-07 20:26:20 -07:00
}
2022-08-21 23:37:29 -07:00
// We have no accounts
return null ;
2022-07-07 20:26:20 -07:00
}
2022-08-21 23:37:29 -07:00
// check that the account still exists
2022-07-07 20:26:20 -07:00
// if not, pick any account
2022-08-21 23:37:29 -07:00
// if there are none, return 0
Future < AccountId ? > getAvailableIdForCoin ( int coin , int id ) async {
2022-07-07 20:26:20 -07:00
coinDef = settings . coins [ coin ] . def ;
final db = coinDef . db ;
final List < Map > res1 = await db . rawQuery (
2022-08-21 23:37:29 -07:00
" SELECT 1 FROM accounts WHERE id_account = ?1 " , [ id ] ) ;
2022-07-07 20:26:20 -07:00
if ( res1 . isNotEmpty )
2022-08-21 23:37:29 -07:00
return AccountId ( coin , id ) ;
2022-07-07 20:26:20 -07:00
final List < Map > res2 = await db . rawQuery (
" SELECT id_account FROM accounts " , [ ] ) ;
2022-07-08 18:51:23 -07:00
if ( res2 . isNotEmpty ) {
2022-07-07 20:26:20 -07:00
final id = res2 [ 0 ] [ ' id_account ' ] ;
2022-08-21 23:37:29 -07:00
return AccountId ( coin , id ) ;
2022-07-07 20:26:20 -07:00
}
2022-08-21 23:37:29 -07:00
return null ;
2022-03-07 06:53:18 -08:00
}
@ action
Future < void > setActiveAccount ( int _coin , int _id ) async {
coin = _coin ;
id = _id ;
accounts . saveActive ( coin , id ) ;
final prefs = await SharedPreferences . getInstance ( ) ;
prefs . setInt ( ' coin ' , coin ) ;
prefs . setInt ( ' account ' , id ) ;
2022-08-21 23:37:29 -07:00
WarpApi . setActiveAccount ( coin , id ) ;
}
2022-03-07 06:53:18 -08:00
2022-08-21 23:37:29 -07:00
@ action
Future < void > refreshAccount ( ) async {
2022-03-07 06:53:18 -08:00
coinDef = settings . coins [ coin ] . def ;
final db = coinDef . db ;
account = accounts . get ( coin , id ) ;
if ( id > 0 ) {
final List < Map > res1 = await db . rawQuery (
" SELECT address FROM taddrs WHERE account = ?1 " , [ id ] ) ;
taddress = res1 . isNotEmpty ? res1 [ 0 ] [ ' address ' ] : " " ;
final List < Map > res2 = await db . rawQuery (
" SELECT sk FROM accounts WHERE id_account = ?1 " , [ id ] ) ;
canPay = res2 . isNotEmpty & & res2 [ 0 ] [ ' sk ' ] ! = null ;
}
showTAddr = false ;
2022-11-12 19:43:09 -08:00
balances . initialized = false ;
2022-08-29 20:42:08 -07:00
draftRecipient = null ;
2022-03-07 06:53:18 -08:00
await update ( ) ;
2022-08-21 23:37:29 -07:00
Future . microtask ( priceStore . updateChart ) ;
2022-03-07 06:53:18 -08:00
}
2022-07-16 20:29:51 -07:00
@ action
Future < void > refreshTAddr ( ) async {
coinDef = settings . coins [ coin ] . def ;
final db = coinDef . db ;
final List < Map > res1 = await db . rawQuery (
" SELECT address FROM taddrs WHERE account = ?1 " , [ active . id ] ) ;
taddress = res1 . isNotEmpty ? res1 [ 0 ] [ ' address ' ] : " " ;
}
2022-03-27 22:55:48 -07:00
@ action
Future < void > updateAccount ( ) async {
final a = accounts . get ( coin , id ) ;
if ( a . id ! = active . id )
await setActiveAccount ( a . coin , a . id ) ;
}
2022-03-07 06:53:18 -08:00
@ action
void toggleShowTAddr ( ) {
showTAddr = ! showTAddr ;
}
@ action
void updateTBalance ( ) {
2022-09-24 05:05:45 -07:00
try {
tbalance = WarpApi . getTBalance ( ) ;
}
on String { }
2022-03-07 06:53:18 -08:00
}
@ action
Future < void > updateBalances ( ) async {
final dbr = DbReader ( coin , id ) ;
2022-11-12 19:43:09 -08:00
final initialized = balances . initialized ;
final prevBalance = balances . balance ;
await dbr . updateBalances ( syncStatus . confirmHeight , balances ) ;
if ( initialized & & prevBalance ! = balances . balance ) {
showBalanceNotification ( prevBalance , balances . balance ) ;
2022-10-12 18:25:29 -07:00
}
2022-03-07 06:53:18 -08:00
}
2022-09-14 19:19:50 -07:00
@ action
void clear ( ) {
messages . clear ( ) ;
notes . clear ( ) ;
txs . clear ( ) ;
unread = 0 ;
dataEpoch + = 1 ;
}
2022-03-07 06:53:18 -08:00
@ action
Future < void > update ( ) async {
await updateBalances ( ) ;
updateTBalance ( ) ;
final dbr = DbReader ( coin , id ) ;
notes = await dbr . getNotes ( ) ;
txs = await dbr . getTxs ( ) ;
2022-10-23 03:30:28 -07:00
messages = ObservableList . of ( await dbr . getMessages ( ) ) ;
2022-04-16 23:03:33 -07:00
unread = messages . where ( ( m ) = > ! m . read ) . length ;
2022-03-07 06:53:18 -08:00
dataEpoch + = 1 ;
}
2022-08-29 20:42:08 -07:00
@ action
void setDraftRecipient ( Recipient ? v ) {
draftRecipient = v ;
}
2022-03-07 06:53:18 -08:00
String newAddress ( ) {
2022-06-07 10:00:08 -07:00
return WarpApi . newDiversifiedAddress ( ) ;
2022-03-07 06:53:18 -08:00
}
2022-11-16 06:16:44 -08:00
String getAddress ( bool t , bool s , bool o ) {
return WarpApi . getAddress ( coin , id , ( t ? 1 : 0 ) | ( s ? 2 : 0 ) | ( o ? 4 : 0 ) ) ;
}
2022-03-07 06:53:18 -08:00
@ computed
List < Note > get sortedNotes {
2022-03-28 23:54:32 -07:00
final _1 = syncStatus . syncedHeight ;
2022-03-07 06:53:18 -08:00
var notes2 = [ . . . notes ] ;
switch ( noteSortConfig . field ) {
case " time " :
return _sort ( notes2 , ( Note note ) = > note . height , noteSortConfig . order ) ;
case " amount " :
return _sort ( notes2 , ( Note note ) = > note . value , noteSortConfig . order ) ;
}
return notes2 ;
}
@ computed
List < Tx > get sortedTxs {
2022-03-28 23:54:32 -07:00
final _1 = syncStatus . syncedHeight ;
2022-03-07 06:53:18 -08:00
var txs2 = [ . . . txs ] ;
switch ( txSortConfig . field ) {
case " time " :
return _sort ( txs2 , ( Tx tx ) = > tx . height , txSortConfig . order ) ;
case " amount " :
return _sort ( txs2 , ( Tx tx ) = > tx . value , txSortConfig . order ) ;
case " txid " :
return _sort ( txs2 , ( Tx tx ) = > tx . txid , txSortConfig . order ) ;
case " address " :
return _sort (
txs2 , ( Tx tx ) = > tx . contact ? ? tx . address , txSortConfig . order ) ;
case " memo " :
return _sort ( txs2 , ( Tx tx ) = > tx . memo , txSortConfig . order ) ;
}
return txs2 ;
}
@ action
void sortNotes ( String field ) {
noteSortConfig = noteSortConfig . sortOn ( field ) ;
}
@ action
void sortTx ( String field ) {
txSortConfig = txSortConfig . sortOn ( field ) ;
}
List < C > _sort < C extends HasHeight , T extends Comparable > (
List < C > items , T Function ( C ) project , SortOrder order ) {
switch ( order ) {
case SortOrder . Ascending:
items . sort ( ( a , b ) = > project ( a ) . compareTo ( project ( b ) ) ) ;
break ;
case SortOrder . Descending:
items . sort ( ( a , b ) = > - project ( a ) . compareTo ( project ( b ) ) ) ;
break ;
case SortOrder . Unsorted:
items . sort ( ( a , b ) = > - a . height . compareTo ( b . height ) ) ;
break ;
}
return items ;
}
@ action
void setPnlSeriesIndex ( int index ) {
pnlSeriesIndex = index ;
}
@ computed
List < PnL > get pnlSorted {
if ( pnlDesc ) {
var _pnls = [ . . . pnls . reversed ] ;
return _pnls ;
}
return pnls ;
}
@ action
void togglePnlDesc ( ) {
pnlDesc = ! pnlDesc ;
}
@ 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 ] ) ;
}
@ action
Future < void > invertExcludedNotes ( ) async {
await coinDef . db . execute (
" UPDATE received_notes SET excluded = NOT(COALESCE(excluded, 0)) WHERE account = ?1 " ,
[ active . 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 ) ;
2022-10-12 18:25:29 -07:00
accountBalances = await dbr . getAccountBalanceTimeSeries ( active . id , active . balances ? . balance ? ? 0 ) ;
2022-03-07 06:53:18 -08:00
}
2022-04-15 23:51:13 -07:00
@ action
void markMessageAsRead ( int index ) {
2022-04-16 23:03:33 -07:00
if ( ! messages [ index ] . read ) {
2022-06-07 10:00:08 -07:00
WarpApi . markMessageAsRead ( messages [ index ] . id , true ) ;
2022-04-16 23:03:33 -07:00
messages [ index ] = messages [ index ] . withRead ( true ) ;
unread = unread - 1 ;
print ( " UNREAD $ unread " ) ;
}
2022-04-15 23:51:13 -07:00
}
2022-04-16 20:16:45 -07:00
@ action
void markAllMessagesAsRead ( ) {
2022-06-07 10:00:08 -07:00
WarpApi . markAllMessagesAsRead ( true ) ;
2022-04-16 20:16:45 -07:00
for ( var i = 0 ; i < messages . length ; i + + ) {
messages [ i ] = messages [ i ] . withRead ( true ) ;
}
2022-04-16 23:03:33 -07:00
unread = 0 ;
2022-04-16 20:16:45 -07:00
}
2022-04-15 23:51:13 -07:00
Future < int ? > prevInThread ( int index ) async {
final message = messages [ index ] ;
final dbr = DbReader ( active . coin , active . id ) ;
return await dbr . getPrevMessage ( message . subject , message . height , id ) ;
}
Future < int ? > nextInThread ( int index ) async {
final message = messages [ index ] ;
final dbr = DbReader ( active . coin , active . id ) ;
return await dbr . getNextMessage ( message . subject , message . height , id ) ;
}
2022-06-16 03:16:32 -07:00
@ action
void setBanner ( String msg ) {
banner = msg ;
}
2022-10-23 03:30:28 -07:00
2022-11-12 19:43:09 -08:00
String get address {
2022-10-23 03:30:28 -07:00
return account . address ;
}
2022-03-07 06:53:18 -08:00
}
2022-11-12 19:43:09 -08:00
class Balances = _Balances with _ $Balances ;
abstract class _Balances with Store {
bool initialized = false ;
@ observable int balance = 0 ;
@ observable int shieldedBalance = 0 ;
@ observable int unconfirmedSpentBalance = 0 ;
@ observable int underConfirmedBalance = 0 ;
@ observable int excludedBalance = 0 ;
@ action
void update ( int balance , int shieldedBalance , int unconfirmedSpentBalance , int underConfirmedBalance , int excludedBalance ) {
this . balance = balance ;
this . shieldedBalance = shieldedBalance ;
this . unconfirmedSpentBalance = unconfirmedSpentBalance ;
this . underConfirmedBalance = underConfirmedBalance ;
this . excludedBalance = excludedBalance ;
this . initialized = true ;
}
}
2022-11-17 01:14:39 -08:00
class AccountId {
final int coin ;
final int id ;
AccountId ( this . coin , this . id ) ;
}