package cash.z.android.wallet.data import androidx.room.Room import androidx.room.RoomDatabase import cash.z.android.wallet.ZcashWalletApplication import cash.z.android.wallet.vo.WalletTransaction import cash.z.android.wallet.vo.WalletTransactionStatus import cash.z.wallet.sdk.dao.BlockDao import cash.z.wallet.sdk.dao.NoteDao import cash.z.wallet.sdk.dao.TransactionDao import cash.z.wallet.sdk.db.DerivedDataDb import cash.z.wallet.sdk.vo.NoteQuery import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.BroadcastChannel import kotlinx.coroutines.channels.ReceiveChannel import kotlinx.coroutines.channels.produce import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import java.math.BigDecimal class ReceivedTransactionRepository(val scope: CoroutineScope) : TransactionRepository { private var db = injectDb() private lateinit var transactions: TransactionDao private lateinit var blocks: BlockDao private lateinit var notes: NoteDao private var existingTransactions = linkedSetOf() private var existingBalance = BigDecimal.ZERO private var balanceChannel = BroadcastChannel(100) private fun injectDb() = Room .databaseBuilder(ZcashWalletApplication.instance, DerivedDataDb::class.java, "tmp") .setJournalMode(RoomDatabase.JournalMode.TRUNCATE) .fallbackToDestructiveMigration() .build() .apply { transactions = transactionDao() blocks = blockDao() notes = noteDao() } /** * Just send a sample stream of balances, every so often */ override fun balance() = balanceChannel.openSubscription() /** * Just send a sample stream of transactions, every so often */ override fun transactions(): ReceiveChannel = scope.produce { while (isActive) { delay(1500L) val newTransactions = checkForNewTransactions() newTransactions?.forEach { existingTransactions.add(it) send(it) updateBalance(it) } } } private suspend fun updateBalance(tx: WalletTransaction) { val multiplier = when (tx.status) { WalletTransactionStatus.SENT -> -1.0 WalletTransactionStatus.RECEIVED -> 1.0 } existingBalance += tx.amount.multiply(BigDecimal(multiplier)) balanceChannel.send(existingBalance) } private fun checkForNewTransactions(): Set? { val count = notes.count() if(count == existingTransactions.size) return null val notes = notes.getAll().map { toWalletTransaction(it) } return notes.subtract(existingTransactions) } private fun toWalletTransaction(note: NoteQuery): WalletTransaction { return WalletTransaction( height = note.height, status = WalletTransactionStatus.RECEIVED, amount = BigDecimal(note.value / 1e8), timestamp = note.time ) } }