mirror of
https://github.com/minibits-cash/minibits_wallet.git
synced 2026-10-05 19:28:26 +00:00
Fix transactions history
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "minibits_wallet",
|
||||
"version": "0.1.9-beta.29",
|
||||
"version": "0.1.9-beta.30",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"android:clean": "cd android && ./gradlew clean",
|
||||
|
||||
@@ -11,7 +11,7 @@ import {WalletStoreModel} from './WalletStore'
|
||||
import {NwcStoreModel} from './NwcStore'
|
||||
import { log } from '../services'
|
||||
|
||||
export const rootStoreModelVersion = 28 // Update this if model changes require migrations defined in setupRootStore.ts
|
||||
export const rootStoreModelVersion = 29 // Update this if model changes require migrations defined in setupRootStore.ts
|
||||
|
||||
/**
|
||||
* A RootStore model.
|
||||
|
||||
@@ -54,8 +54,8 @@ export enum TransactionStatus {
|
||||
}
|
||||
|
||||
export const TransactionModel = types
|
||||
.model('Transaction', {
|
||||
id: types.number,
|
||||
.model('Transaction', {
|
||||
id: types.identifierNumber,
|
||||
type: types.frozen<TransactionType>(),
|
||||
amount: types.integer,
|
||||
fee: types.optional(types.integer, 0),
|
||||
|
||||
+209
-166
@@ -19,9 +19,7 @@ import { formatDistance } from 'date-fns'
|
||||
import { MintUnit } from '../services/wallet/currency'
|
||||
import { Mint } from './Mint'
|
||||
|
||||
export const maxTransactionsInModel = 10
|
||||
export const maxTransactionsByMint = 10
|
||||
export const maxTransactionsByHostname = 3
|
||||
export const maxTransactionsInHistory = 10
|
||||
export const maxTransactionsByUnit = 3
|
||||
|
||||
export type GroupedByTimeAgo = {
|
||||
@@ -30,191 +28,222 @@ export type GroupedByTimeAgo = {
|
||||
|
||||
export const TransactionsStoreModel = types
|
||||
.model('TransactionsStore', {
|
||||
transactions: types.array(TransactionModel),
|
||||
transactionsMap: types.map(TransactionModel),
|
||||
history: types.array(types.safeReference(TransactionModel, { acceptsUndefined: false })),
|
||||
recentByUnit: types.array(types.safeReference(TransactionModel, { acceptsUndefined: false })),
|
||||
})
|
||||
.actions(withSetPropAction)
|
||||
.views(self => ({
|
||||
get all() {
|
||||
return self.transactions
|
||||
get pendingHistory() {
|
||||
return self.history
|
||||
.slice()
|
||||
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
|
||||
.filter(t => t.status === TransactionStatus.PENDING)
|
||||
},
|
||||
get historyByTimeAgo() {
|
||||
return self.history
|
||||
.slice()
|
||||
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
|
||||
.reduce((groups: GroupedByTimeAgo, transaction: Transaction) => {
|
||||
const timeAgo = formatDistance(transaction.createdAt as Date, new Date(), {addSuffix: true})
|
||||
if (!groups[timeAgo]) {
|
||||
groups[timeAgo] = []
|
||||
}
|
||||
groups[timeAgo].push(transaction)
|
||||
return groups
|
||||
}, {})
|
||||
},
|
||||
get historyPendingByTimeAgo() {
|
||||
return this.pendingHistory.reduce((groups: GroupedByTimeAgo, transaction: Transaction) => {
|
||||
const timeAgo = formatDistance(transaction.createdAt as Date, new Date(), {addSuffix: true})
|
||||
if (!groups[timeAgo]) {
|
||||
groups[timeAgo] = []
|
||||
}
|
||||
groups[timeAgo].push(transaction)
|
||||
return groups
|
||||
}, {})
|
||||
},
|
||||
get historyCount() {
|
||||
return self.history.length
|
||||
},
|
||||
get pendingHistoryCount() {
|
||||
return this.pendingHistory.length
|
||||
},
|
||||
getRecentByUnit(unit: MintUnit) {
|
||||
return self.recentByUnit
|
||||
.slice()
|
||||
.sort((a, b) => {
|
||||
// Sort by createdAt timestamp
|
||||
if (a.createdAt && b.createdAt) {
|
||||
return b.createdAt.getTime() - a.createdAt.getTime()
|
||||
}
|
||||
}) as Transaction[]
|
||||
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
|
||||
.filter(t => t.unit === unit)
|
||||
},
|
||||
get count() {
|
||||
return self.transactions.length
|
||||
},
|
||||
get recent() {
|
||||
return this.all.slice(0, 3) // Return the first 3 transactions
|
||||
},
|
||||
get pending() {
|
||||
return this.all.filter(t => t.status === TransactionStatus.PENDING)
|
||||
},
|
||||
get groupedByTimeAgo() {
|
||||
return this.all.reduce((groups: GroupedByTimeAgo, transaction: Transaction) => {
|
||||
const timeAgo = formatDistance(transaction.createdAt as Date, new Date(), {addSuffix: true})
|
||||
if (!groups[timeAgo]) {
|
||||
groups[timeAgo] = []
|
||||
}
|
||||
groups[timeAgo].push(transaction)
|
||||
return groups
|
||||
}, {})
|
||||
},
|
||||
get groupedPendingByTimeAgo() {
|
||||
return this.pending.reduce((groups: GroupedByTimeAgo, transaction: Transaction) => {
|
||||
const timeAgo = formatDistance(transaction.createdAt as Date, new Date(), {addSuffix: true})
|
||||
if (!groups[timeAgo]) {
|
||||
groups[timeAgo] = []
|
||||
}
|
||||
groups[timeAgo].push(transaction)
|
||||
return groups
|
||||
}, {})
|
||||
},
|
||||
|
||||
recentByHostname(mintHostname: string) {
|
||||
return this.all.filter(t => getHostname(t.mint as string) === mintHostname).slice(0, maxTransactionsByHostname)
|
||||
},
|
||||
recentByUnit(unit: MintUnit, count?: number) {
|
||||
if (!count || count > maxTransactionsByUnit) {
|
||||
count = maxTransactionsByUnit
|
||||
}
|
||||
|
||||
return this.all.filter(t => t.unit === unit).slice(0, count)
|
||||
},
|
||||
recentByHostnameGroupedByTimeAgo(mintHostname: string) {
|
||||
const recentByHostname = this.recentByHostname(mintHostname)
|
||||
|
||||
return recentByHostname.reduce((groups: GroupedByTimeAgo, transaction: Transaction) => {
|
||||
const timeAgo = formatDistance(transaction.createdAt as Date, new Date(), {addSuffix: true})
|
||||
if (!groups[timeAgo]) {
|
||||
groups[timeAgo] = []
|
||||
}
|
||||
groups[timeAgo].push(transaction)
|
||||
return groups
|
||||
}, {})
|
||||
},
|
||||
getByMint(mintUrl: string) {
|
||||
return this.all.filter(t => t.mint === mintUrl)
|
||||
},
|
||||
countByMint(mintUrl: string) {
|
||||
return this.getByMint(mintUrl).length
|
||||
}
|
||||
}))
|
||||
countRecentByUnit(unit: MintUnit) {
|
||||
return this.getRecentByUnit(unit).length
|
||||
}
|
||||
}))
|
||||
.actions(self => ({
|
||||
findById(id: number) {
|
||||
|
||||
let tx = self.transactions.find(tx => tx.id === id)
|
||||
findById(id: number) {
|
||||
let transaction = self.transactionsMap.get(id)
|
||||
|
||||
// Search the db and add if tx is not in the state
|
||||
if(!tx) {
|
||||
if(!transaction) {
|
||||
const dbTransaction = Database.getTransactionById(id)
|
||||
|
||||
if(dbTransaction) {
|
||||
const createdAt = new Date(dbTransaction.createdAt)
|
||||
const createdAt = new Date(dbTransaction.createdAt)
|
||||
const inStoreTransaction = {...dbTransaction, createdAt}
|
||||
|
||||
tx = TransactionModel.create(inStoreTransaction)
|
||||
self.transactions.push(tx)
|
||||
const {id} = dbTransaction
|
||||
|
||||
self.transactionsMap.set(id, inStoreTransaction)
|
||||
transaction = self.transactionsMap.get(id)
|
||||
}
|
||||
}
|
||||
|
||||
return tx
|
||||
return transaction
|
||||
},
|
||||
removeOldTransactions: () => { // not used
|
||||
const numTransactions = self.count
|
||||
/*pruneTransactionsMap() {
|
||||
// Clean up transactionMap: remove transactions not in history or recentByUnit
|
||||
self.transactionsMap.forEach((_, transactionId) => {
|
||||
const isInHistory = self.history.some(t => t.id === transactionId)
|
||||
const isInRecentByUnit = self.recentByUnit.some(t => t.id === transactionId)
|
||||
|
||||
// If there are more than 10 transactions, delete the older ones
|
||||
if (numTransactions > maxTransactionsInModel) {
|
||||
self.transactions
|
||||
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
|
||||
.splice(maxTransactionsInModel) // Remove transactions beyond the desired number to keep
|
||||
|
||||
log.debug('[removeOldTransactions]', `${
|
||||
numTransactions - maxTransactionsInModel
|
||||
} transaction(s) removed from TransactionsStore`,
|
||||
)
|
||||
}
|
||||
},
|
||||
removeOldByMint: (mintUrl: string) => {
|
||||
const numByMint = self.countByMint(mintUrl)
|
||||
// If the transaction is not in history or recentByUnit, remove it from transactionMap
|
||||
if (!isInHistory && !isInRecentByUnit) {
|
||||
self.transactionsMap.delete(transactionId as string)
|
||||
log.trace(`[pruneTransactionsMap] Transaction ${transactionId} pruned from the map`)
|
||||
} else {
|
||||
log.trace(`[pruneTransactionsMap] Transaction ${transactionId} kept in the map`)
|
||||
}
|
||||
})
|
||||
},*/
|
||||
pruneRecentByUnit(unit: MintUnit) {
|
||||
const unitCount = self.countRecentByUnit(unit)
|
||||
|
||||
if (numByMint > maxTransactionsByMint) {
|
||||
const transactionsToRemove = self.getByMint(mintUrl)
|
||||
.slice()
|
||||
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
|
||||
.slice(maxTransactionsByMint)
|
||||
|
||||
transactionsToRemove.map((t) => {
|
||||
detach(t)
|
||||
})
|
||||
log.trace('[pruneRecentByUnit]', {unit, unitCount})
|
||||
|
||||
if (unitCount > maxTransactionsByUnit) {
|
||||
const transactionsToRemove = self.getRecentByUnit(unit)
|
||||
.slice()
|
||||
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
|
||||
.splice(maxTransactionsByUnit)
|
||||
|
||||
self.transactions.replace(self.transactions.filter(t => !transactionsToRemove.some(removed => removed.id === t.id)))
|
||||
self.recentByUnit.replace(self.recentByUnit.filter(t => !transactionsToRemove.some(removed => removed.id === t.id)))
|
||||
|
||||
const txByMintAfterDelete = self.countByMint(mintUrl)
|
||||
const txTotalAfterDelete = self.count
|
||||
|
||||
log.trace('[removeOldByMint]', {txByMintAfterDelete, txTotalAfterDelete})
|
||||
log.trace('[pruneRecentByUnit]', `${transactionsToRemove.length} pruned from recentByUnit`)
|
||||
}
|
||||
},
|
||||
removeAllWithoutCurrentMint: () => {
|
||||
pruneRecentWithoutCurrentMint() {
|
||||
const rootStore = getRootStore(self)
|
||||
const {mintsStore} = rootStore
|
||||
|
||||
const transactionsToRemove = self.transactions.filter(transaction => {
|
||||
const transactionsToRemove = self.recentByUnit.filter(transaction => {
|
||||
// Check if the mint property of the transaction does not exist in the mints array
|
||||
return !mintsStore.allMints.some((mint: Mint) => mint.mintUrl === transaction.mint);
|
||||
});
|
||||
})
|
||||
|
||||
self.transactions.replace(self.transactions.filter(t => !transactionsToRemove.some(removed => removed.id === t.id)))
|
||||
self.recentByUnit.replace(self.recentByUnit.filter(t => !transactionsToRemove.some(removed => removed.id === t.id)))
|
||||
|
||||
const txTotalAfterDelete = self.count
|
||||
log.trace('[pruneRecentWithoutCurrentMint]', `${transactionsToRemove.length} pruned from recentByUnit`)
|
||||
},
|
||||
pruneHistory() {
|
||||
// Step 1: Trim history to keep only the MAX_HISTORY_TRANSACTIONS most recent
|
||||
if (self.history.length > maxTransactionsInHistory) {
|
||||
const transactionsToRemove = self.history
|
||||
.slice()
|
||||
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
|
||||
.splice(maxTransactionsInHistory)
|
||||
|
||||
log.trace('[removeAllWithoutCurrentMint]', {deleted: transactionsToRemove.length, txTotalAfterDelete})
|
||||
|
||||
}
|
||||
self.history.replace(self.history.filter(t => !transactionsToRemove.some(removed => removed.id === t.id)))
|
||||
|
||||
log.trace('[pruneHistory]', `${transactionsToRemove.length} pruned from history`)
|
||||
}
|
||||
},
|
||||
removeAllHistory() {
|
||||
self.history.clear()
|
||||
log.debug('[removeAllHistory]', 'Removed all transactions from history')
|
||||
},
|
||||
removeAllRecentByUnit() {
|
||||
self.recentByUnit.clear()
|
||||
log.debug('[removeAllRecentByUnit]', 'Removed all transactions from recentByUnit')
|
||||
},
|
||||
removeAllTransactions() {
|
||||
self.recentByUnit.clear()
|
||||
self.history.clear()
|
||||
self.transactionsMap.clear()
|
||||
log.debug('[removeAllTransactions]', 'Removed all transactions from TransactionsStore')
|
||||
},
|
||||
}))
|
||||
.actions(self => ({
|
||||
addTransaction: flow(function* addTransaction(newTransaction){
|
||||
// First let's store the transaction into the database
|
||||
const dbTransaction: TransactionRecord = yield Database.addTransactionAsync(newTransaction)
|
||||
const dbTransaction: TransactionRecord = yield Database.addTransactionAsync(newTransaction)
|
||||
|
||||
// Add the new transaction to the transactions store
|
||||
const createdAt = new Date(dbTransaction.createdAt)
|
||||
const createdAt = new Date(dbTransaction.createdAt)
|
||||
const inStoreTransaction = {...dbTransaction, createdAt}
|
||||
const {id} = dbTransaction
|
||||
|
||||
const transactionInstance = TransactionModel.create(inStoreTransaction)
|
||||
self.transactions.push(transactionInstance)
|
||||
if (!self.transactionsMap.has(id)) {
|
||||
self.transactionsMap.set(id, inStoreTransaction)
|
||||
}
|
||||
|
||||
const reference = self.transactionsMap.get(id)
|
||||
self.history.unshift(reference!)
|
||||
self.recentByUnit.unshift(reference!)
|
||||
|
||||
log.debug('[addTransaction]', 'New transaction added to the TransactionsStore')
|
||||
|
||||
// Purge the oldest transaction from cache, but keep some for each mint
|
||||
self.removeOldByMint(newTransaction.mint)
|
||||
// Purge the oldest references from cache, but keep some for each mint
|
||||
self.pruneRecentByUnit(newTransaction.unit)
|
||||
self.pruneHistory()
|
||||
|
||||
return transactionInstance as Transaction
|
||||
return reference as Transaction
|
||||
}),
|
||||
addTransactionsToModel: (dbTransactions: TransactionRecord[]) => {
|
||||
// This adds to model only. Used to have observable UI in tx history loaded from database.
|
||||
const inStoreTransactions: Transaction[] = []
|
||||
addToHistory(limit: number, offset: number, onlyPending: boolean){
|
||||
// Appends transaction to the map and adds reference to history from database.
|
||||
const result = Database.getTransactions(limit, offset, onlyPending)
|
||||
log.trace('[addToHistory] dbResult ids', {ids: result?._array.map(t => t.id)})
|
||||
|
||||
for (const dbTransaction of dbTransactions) {
|
||||
const createdAt = new Date(dbTransaction.createdAt)
|
||||
const inStoreTransaction = {...dbTransaction, createdAt}
|
||||
if (result && result.length > 0) {
|
||||
for (const dbTransaction of result._array) {
|
||||
const createdAt = new Date(dbTransaction.createdAt)
|
||||
const inStoreTransaction = {...dbTransaction, createdAt}
|
||||
const {id} = dbTransaction
|
||||
|
||||
if(self.findById(inStoreTransaction.id as number)) {
|
||||
log.trace('[addTransactionsToModel] Transaction already exists in the model, skipping...')
|
||||
continue
|
||||
}
|
||||
|
||||
const transactionInstance = TransactionModel.create(inStoreTransaction)
|
||||
inStoreTransactions.push(transactionInstance as Transaction)
|
||||
if (!self.transactionsMap.has(id)) {
|
||||
self.transactionsMap.set(id, inStoreTransaction)
|
||||
log.trace('[addToHistory]', `${id} added to transactionsMap`)
|
||||
}
|
||||
|
||||
const reference = self.transactionsMap.get(id)
|
||||
|
||||
if (!self.history.find(t => t.id === id)) {
|
||||
self.history.push(reference!)
|
||||
log.trace('[addToHistory]', `${onlyPending ? 'Pending reference' : 'Reference'} ${id} added to history`)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
addRecentByUnit() {
|
||||
// Rehydrates recent from database.
|
||||
const dbTransactions = Database.getRecentTransactionsByUnit(maxTransactionsByUnit)
|
||||
|
||||
self.transactions.push(...inStoreTransactions)
|
||||
|
||||
log.debug('[addTransactionsToModel]', `${inStoreTransactions.length} new transactions added to TransactionsStore`)
|
||||
if (dbTransactions && dbTransactions.length > 0) {
|
||||
for (const dbTransaction of dbTransactions) {
|
||||
const createdAt = new Date(dbTransaction.createdAt)
|
||||
const inStoreTransaction = {...dbTransaction, createdAt} as Transaction
|
||||
const {id} = dbTransaction
|
||||
|
||||
if (!self.transactionsMap.has(id)) {
|
||||
self.transactionsMap.set(id, inStoreTransaction)
|
||||
}
|
||||
|
||||
const reference = self.transactionsMap.get(id)
|
||||
|
||||
if (!self.recentByUnit.find(t => t.id === id)) {
|
||||
self.recentByUnit.push(reference!)
|
||||
log.trace('[addRecentByUnit]', `Transaction ${inStoreTransaction.id} added to recentByUnit`)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
updateStatuses: flow(function* updateStatuses(
|
||||
ids: number[],
|
||||
@@ -226,7 +255,7 @@ export const TransactionsStoreModel = types
|
||||
|
||||
// Update the model status and amend related tx data
|
||||
for (const id of ids) {
|
||||
const transactionInstance = self.findById(id)
|
||||
const transactionInstance = self.transactionsMap.get(id)
|
||||
|
||||
if (transactionInstance) {
|
||||
transactionInstance.status = status
|
||||
@@ -235,36 +264,50 @@ export const TransactionsStoreModel = types
|
||||
const updatedData = JSON.parse(transactionInstance.data)
|
||||
updatedData.push(JSON.parse(data))
|
||||
transactionInstance.data = JSON.stringify(updatedData)
|
||||
}
|
||||
|
||||
log.debug('[updateStatuses]', 'Transaction statuses and data updated in TransactionsStore', {ids, status})
|
||||
}
|
||||
log.trace('[updateStatuses]', 'Transaction statuses and data updated in TransactionsStore', {ids, status})
|
||||
}
|
||||
}),
|
||||
expireAllAfterRecovery: flow(function* expireAllAfterRecovery() {
|
||||
// Update status in database
|
||||
yield Database.expireAllAfterRecovery()
|
||||
}),
|
||||
deleteByStatus(status: TransactionStatus){
|
||||
|
||||
// Update the model statuses
|
||||
for (const t of self.all) {
|
||||
t.setIsExpired()
|
||||
}
|
||||
}),
|
||||
deleteByStatus: (status: TransactionStatus) => {
|
||||
for (const transaction of self.transactions) {
|
||||
if(transaction.status === status) {
|
||||
detach(transaction)
|
||||
self.transactionsMap.forEach((transaction, transactionId) => {
|
||||
if (transaction.status === status) {
|
||||
self.transactionsMap.delete(transactionId as string)
|
||||
}
|
||||
}
|
||||
|
||||
self.transactions.replace(self.transactions.filter(t => t.status !== status))
|
||||
})
|
||||
|
||||
return Database.deleteTransactionsByStatus(status)
|
||||
},
|
||||
removeAllTransactions() {
|
||||
self.transactions.clear()
|
||||
log.debug('[removeAllTransactions]', 'Removed all transactions from TransactionsStore')
|
||||
},
|
||||
}))
|
||||
}
|
||||
})).postProcessSnapshot((snapshot) => {
|
||||
// Trim history if it exceeds the limit
|
||||
let prunedHistory = snapshot.history
|
||||
|
||||
if (snapshot.history.length > maxTransactionsInHistory) {
|
||||
// Keep only the most recent transactions within the limit
|
||||
const orderedHistory = [...snapshot.history].sort((a, b) => (b as number) - (a as number))
|
||||
prunedHistory = orderedHistory.slice(0, maxTransactionsInHistory)
|
||||
}
|
||||
|
||||
// Clean up transactionMap: remove transactions not in history or recentByUnit
|
||||
const prunedTransactionsMap = Object.fromEntries(
|
||||
Object.entries(snapshot.transactionsMap).filter(([transactionId]) =>
|
||||
prunedHistory.includes(parseInt(transactionId)) ||
|
||||
snapshot.recentByUnit.includes(parseInt(transactionId))
|
||||
)
|
||||
)
|
||||
|
||||
// Return the new snapshot with the trimmed history and filtered transactionMap
|
||||
const prunedSnapshot = {
|
||||
...snapshot,
|
||||
history: prunedHistory,
|
||||
transactionsMap: prunedTransactionsMap
|
||||
}
|
||||
|
||||
console.log('[postProcessSnapshot]', {prunedSnapshot})
|
||||
|
||||
return prunedSnapshot
|
||||
})
|
||||
|
||||
const getHostname = function (mintUrl: string) {
|
||||
try {
|
||||
|
||||
@@ -55,14 +55,13 @@ export async function setupRootStore(rootStore: RootStore) {
|
||||
|
||||
// load the last known state from storage
|
||||
const start = performance.now()
|
||||
restoredState = MMKVStorage.load(ROOT_STORAGE_KEY) || {}
|
||||
const mmkvLoaded = performance.now()
|
||||
restoredState = MMKVStorage.load(ROOT_STORAGE_KEY) || {}
|
||||
const mmkvLoaded = performance.now()
|
||||
const dataSize = Buffer.byteLength(JSON.stringify(restoredState), 'utf8')
|
||||
|
||||
log.trace('[setupRootStore]', `Loading ${dataSize.toLocaleString()} bytes of state from MMKV took ${(mmkvLoaded - start).toLocaleString()} ms.`)
|
||||
log.trace({restoredState})
|
||||
log.trace('[setupRootStore]', `Loading ${dataSize.toLocaleString()} bytes of state from MMKV took ${(mmkvLoaded - start).toLocaleString()} ms.`)
|
||||
|
||||
// log.trace({restoredState})
|
||||
|
||||
applySnapshot(rootStore, restoredState)
|
||||
|
||||
const stateHydrated = performance.now()
|
||||
@@ -129,123 +128,7 @@ async function _runMigrations(rootStore: RootStore) {
|
||||
|
||||
let currentVersion = rootStore.version
|
||||
|
||||
try {
|
||||
|
||||
if(currentVersion < 6) {
|
||||
log.trace(`Starting rootStore migrations from version v${currentVersion} -> v6`)
|
||||
userSettingsStore.setLogLevel(LogLevel.ERROR)
|
||||
log.info(`Completed rootStore migrations to the version v${rootStoreModelVersion}`)
|
||||
rootStore.setVersion(rootStoreModelVersion)
|
||||
}
|
||||
|
||||
|
||||
if(currentVersion < 7) {
|
||||
log.trace(`Starting rootStore migrations from version v${currentVersion} -> v7`)
|
||||
for (const mint of mintsStore.allMints) {
|
||||
mint.setStatus(MintStatus.ONLINE)
|
||||
}
|
||||
log.info(`Completed rootStore migrations to the version v${rootStoreModelVersion}`)
|
||||
rootStore.setVersion(rootStoreModelVersion)
|
||||
}
|
||||
|
||||
|
||||
if(currentVersion < 8) {
|
||||
log.trace(`Starting rootStore migrations from version v${currentVersion} -> v8`)
|
||||
const seedHash = await KeyChain.loadSeedHash()
|
||||
|
||||
if(seedHash && walletProfileStore.pubkey) {
|
||||
await MinibitsClient.migrateSeedHash(
|
||||
walletProfileStore.pubkey,
|
||||
{
|
||||
seedHash
|
||||
}
|
||||
)
|
||||
|
||||
walletProfileStore.setSeedHash(seedHash)
|
||||
log.info(`Completed rootStore migrations to the version v${rootStoreModelVersion}`)
|
||||
rootStore.setVersion(rootStoreModelVersion)
|
||||
}
|
||||
}
|
||||
|
||||
if(currentVersion < 9) {
|
||||
log.trace(`Starting rootStore migrations from version v${currentVersion} -> v9`)
|
||||
|
||||
for (const mint of mintsStore.allMints) {
|
||||
try {
|
||||
await mint.setShortname()
|
||||
} catch (e: any) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
log.info(`Completed rootStore migrations to the version v${rootStoreModelVersion}`)
|
||||
rootStore.setVersion(rootStoreModelVersion)
|
||||
}
|
||||
|
||||
if(currentVersion < 10) {
|
||||
log.trace(`Starting rootStore migrations from version v${currentVersion} -> v10`)
|
||||
|
||||
for (const contact of contactsStore.all) {
|
||||
if(contact.isExternalDomain === false) {
|
||||
contact.setLud16(contact.nip05 as string)
|
||||
}
|
||||
}
|
||||
|
||||
log.info(`Completed rootStore migrations to the version v${rootStoreModelVersion}`)
|
||||
rootStore.setVersion(rootStoreModelVersion)
|
||||
}
|
||||
|
||||
if(currentVersion < 11) {
|
||||
log.trace(`Starting rootStore migrations from version v${currentVersion} -> v11`)
|
||||
// userSettingsStore.setIsStorageMigrated(true)
|
||||
rootStore.setVersion(rootStoreModelVersion)
|
||||
log.info(`Completed rootStore migrations to the version v${rootStoreModelVersion}`)
|
||||
}
|
||||
|
||||
if(currentVersion < 12) {
|
||||
log.trace(`Starting rootStore migrations from version v${currentVersion} -> v12`)
|
||||
|
||||
for (const mint of mintsStore.allMints) {
|
||||
try {
|
||||
mint.addUnit('sat')
|
||||
mint.resetCounters()
|
||||
} catch (e: any) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
rootStore.setVersion(rootStoreModelVersion)
|
||||
log.info(`Completed rootStore migrations to the version v${rootStoreModelVersion}`)
|
||||
}
|
||||
|
||||
if(currentVersion < 13) {
|
||||
log.trace(`Starting rootStore migrations from version v${currentVersion} -> v13`)
|
||||
|
||||
userSettingsStore.setPreferredUnit('sat')
|
||||
|
||||
for (const proof of proofsStore.allProofs) {
|
||||
try {
|
||||
if(!proof.unit) {
|
||||
proof.setUnit('sat')
|
||||
}
|
||||
} catch (e: any) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
for (const tx of transactionsStore.all) {
|
||||
try {
|
||||
if(!tx.unit) {
|
||||
tx.setUnit('sat')
|
||||
}
|
||||
} catch (e: any) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
rootStore.setVersion(rootStoreModelVersion)
|
||||
log.info(`Completed rootStore migrations to the version v${rootStoreModelVersion}`)
|
||||
}
|
||||
try {
|
||||
|
||||
if(currentVersion < 14) {
|
||||
log.trace(`Starting rootStore migrations from version v${currentVersion} -> v14`)
|
||||
@@ -295,7 +178,7 @@ async function _runMigrations(rootStore: RootStore) {
|
||||
log.info(`Completed rootStore migrations to the version v${rootStoreModelVersion}`)
|
||||
}
|
||||
if(currentVersion < 28) {
|
||||
log.trace(`Starting rootStore migrations from version v${currentVersion} -> v27`)
|
||||
log.trace(`Starting rootStore migrations from version v${currentVersion} -> v28`)
|
||||
|
||||
userSettingsStore.setExchangeCurrency(CurrencyCode.USD)
|
||||
userSettingsStore.setTheme(ThemeCode.DEFAULT)
|
||||
@@ -315,6 +198,14 @@ async function _runMigrations(rootStore: RootStore) {
|
||||
}
|
||||
}
|
||||
|
||||
rootStore.setVersion(rootStoreModelVersion)
|
||||
log.info(`Completed rootStore migrations to the version v${rootStoreModelVersion}`)
|
||||
}
|
||||
if(currentVersion < 29) {
|
||||
log.trace(`Starting rootStore migrations from version v${currentVersion} -> v29`)
|
||||
|
||||
transactionsStore.addRecentByUnit()
|
||||
|
||||
rootStore.setVersion(rootStoreModelVersion)
|
||||
log.info(`Completed rootStore migrations to the version v${rootStoreModelVersion}`)
|
||||
}
|
||||
|
||||
@@ -32,12 +32,12 @@ import {translate} from '../i18n'
|
||||
import AppError from '../utils/AppError'
|
||||
import {Database, KeyChain, NostrClient, log} from '../services'
|
||||
import {MMKVStorage} from '../services'
|
||||
import {maxTransactionsInModel} from '../models/TransactionsStore'
|
||||
import { LogLevel } from '../services/log/logTypes'
|
||||
import { getSnapshot } from 'mobx-state-tree'
|
||||
import { delay } from '../utils/delay'
|
||||
import RNExitApp from 'react-native-exit-app'
|
||||
import { TransactionStatus } from '../models/Transaction'
|
||||
import { maxTransactionsInHistory } from '../models/TransactionsStore'
|
||||
|
||||
// refresh
|
||||
|
||||
@@ -77,11 +77,11 @@ export const DeveloperScreen: FC<SettingsStackScreenProps<'Developer'>> = observ
|
||||
}, [])
|
||||
|
||||
// Reset of transaction model state and reload from DB
|
||||
const syncTransactionsFromDb = async function () {
|
||||
const syncTransactionsFromDb = function () {
|
||||
setIsLoading(true)
|
||||
try {
|
||||
const result = await Database.getTransactionsAsync(
|
||||
maxTransactionsInModel,
|
||||
const result = Database.getTransactions(
|
||||
maxTransactionsInHistory,
|
||||
0,
|
||||
)
|
||||
|
||||
@@ -89,8 +89,10 @@ export const DeveloperScreen: FC<SettingsStackScreenProps<'Developer'>> = observ
|
||||
// remove all from the transactionsStore model
|
||||
transactionsStore.removeAllTransactions()
|
||||
|
||||
// Add last 10 from database
|
||||
transactionsStore.addTransactionsToModel(result._array)
|
||||
// Add last 10 to history
|
||||
transactionsStore.addToHistory(maxTransactionsInHistory, 0, false)
|
||||
// Add recent by unit
|
||||
transactionsStore.addRecentByUnit()
|
||||
|
||||
setIsLoading(false)
|
||||
setInfo(translate('resetCompletedDetail', { transCount: result.length }))
|
||||
@@ -131,7 +133,7 @@ export const DeveloperScreen: FC<SettingsStackScreenProps<'Developer'>> = observ
|
||||
proofsStore.removeProofs(pending, true, false)
|
||||
}
|
||||
|
||||
await syncTransactionsFromDb()
|
||||
syncTransactionsFromDb()
|
||||
|
||||
setIsLoading(false)
|
||||
setInfo(`Removed pending transactions from the database and ${pendingCount} proofs from the wallet state`)
|
||||
@@ -142,8 +144,7 @@ export const DeveloperScreen: FC<SettingsStackScreenProps<'Developer'>> = observ
|
||||
},
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -254,8 +255,7 @@ Sentry id: ${userSettingsStore.userSettings.walletId}
|
||||
subText={userSettingsStore.logLevel.toUpperCase()}
|
||||
leftIcon='faListUl'
|
||||
leftIconColor={colors.palette.iconMagenta200}
|
||||
leftIconInverse={true}
|
||||
RightComponent={<View style={$rightContainer} />}
|
||||
leftIconInverse={true}
|
||||
style={$item}
|
||||
bottomSeparator={true}
|
||||
onPress={toggleLogLevelSelector}
|
||||
@@ -263,14 +263,22 @@ Sentry id: ${userSettingsStore.userSettings.walletId}
|
||||
<ListItem
|
||||
tx="showOnboarding"
|
||||
subTx="showOnboardingDesc"
|
||||
leftIcon='faRotate'
|
||||
leftIcon='faInfoCircle'
|
||||
leftIconColor={colors.light.tint}
|
||||
leftIconInverse={true}
|
||||
RightComponent={<View style={$rightContainer} />}
|
||||
style={$item}
|
||||
leftIconInverse={true}
|
||||
style={$item}
|
||||
bottomSeparator={true}
|
||||
onPress={() => userSettingsStore.setIsOnboarded(false)}
|
||||
/>
|
||||
|
||||
<ListItem
|
||||
text="Resync transactions"
|
||||
subText="Refresh recent transactions history from database."
|
||||
leftIcon='faRotate'
|
||||
leftIconColor={colors.palette.blue200}
|
||||
leftIconInverse={true}
|
||||
style={$item}
|
||||
onPress={syncTransactionsFromDb}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -75,9 +75,6 @@ export const TranDetailScreen: FC<TransactionsStackScreenProps<'TranDetail'>> =
|
||||
const noteInputRef = useRef<TextInput>(null)
|
||||
|
||||
const [transaction, setTransaction] = useState<Transaction>()
|
||||
/* const [proofsByStatus, setProofsByStatus] = useState<
|
||||
ProofsByStatus | undefined
|
||||
>(undefined)*/
|
||||
const [error, setError] = useState<AppError | undefined>()
|
||||
const [isNoteEditing, setIsNoteEditing] = useState(transaction?.noteToSelf ? false : true)
|
||||
const [isDataParsable, setIsDataParsable] = useState<boolean>(true)
|
||||
|
||||
@@ -27,24 +27,24 @@ import {
|
||||
import {TransactionsStackScreenProps} from '../navigation'
|
||||
import {useHeader} from '../utils/useHeader'
|
||||
import {useStores} from '../models'
|
||||
import {GroupedByTimeAgo, maxTransactionsInModel} from '../models/TransactionsStore'
|
||||
import {Database, log} from '../services'
|
||||
import AppError from '../utils/AppError'
|
||||
import {TransactionListItem} from './Transactions/TransactionListItem'
|
||||
import { Transaction, TransactionStatus } from '../models/Transaction'
|
||||
import { height } from '@fortawesome/free-solid-svg-icons/faWallet'
|
||||
import { translate } from '../i18n'
|
||||
import { maxTransactionsInHistory } from '../models/TransactionsStore'
|
||||
|
||||
if (Platform.OS === 'android' &&
|
||||
UIManager.setLayoutAnimationEnabledExperimental) {
|
||||
UIManager.setLayoutAnimationEnabledExperimental(true)
|
||||
}
|
||||
// Number of transactions held in TransactionsStore model
|
||||
const limit = maxTransactionsInModel
|
||||
const limit = maxTransactionsInHistory
|
||||
|
||||
export const TranHistoryScreen: FC<TransactionsStackScreenProps<'TranHistory'>> = observer(function TranHistoryScreen(_props) {
|
||||
const {navigation} = _props
|
||||
const {transactionsStore, proofsStore, mintsStore} = useStores()
|
||||
const {transactionsStore, mintsStore} = useStores()
|
||||
useHeader({
|
||||
leftIcon: 'faArrowLeft',
|
||||
onLeftPress: () => navigation.goBack(),
|
||||
@@ -58,9 +58,7 @@ export const TranHistoryScreen: FC<TransactionsStackScreenProps<'TranHistory'>>
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [isHeaderVisible, setIsHeaderVisible] = useState(true)
|
||||
const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false)
|
||||
const [offset, setOffset] = useState<number>(transactionsStore.count) // load from db those that are not already displayed
|
||||
const [pendingOffset, setPendingOffset] = useState<number>(transactionsStore.pending.length) // load from db those that are not already displayed
|
||||
const [dbCount, setDbCount] = useState<number>(0)
|
||||
const [totalDbCount, setTotalDbCount] = useState<number>(0)
|
||||
const [pendingDbCount, setPendingDbCount] = useState<number>(0)
|
||||
const [expiredDbCount, setExpiredDbCount] = useState<number>(0)
|
||||
const [erroredDbCount, setErroredDbCount] = useState<number>(0)
|
||||
@@ -71,106 +69,111 @@ export const TranHistoryScreen: FC<TransactionsStackScreenProps<'TranHistory'>>
|
||||
useEffect(() => {
|
||||
const init = async () => {
|
||||
setIsLoading(true)
|
||||
const count = Database.getTransactionsCount() // all
|
||||
const pendingCount = Database.getTransactionsCount(TransactionStatus.PENDING)
|
||||
const expiredCount = Database.getTransactionsCount(TransactionStatus.EXPIRED)
|
||||
const erroredCount = Database.getTransactionsCount(TransactionStatus.ERROR)
|
||||
const revertedCount = Database.getTransactionsCount(TransactionStatus.REVERTED)
|
||||
const countByStatus = Database.getTransactionsCount()
|
||||
|
||||
log.trace('transaction counts', {count, pendingCount, erroredCount, revertedCount})
|
||||
log.trace('Database transaction counts', {countByStatus})
|
||||
|
||||
setPendingDbCount(countByStatus[TransactionStatus.PENDING] || 0)
|
||||
setExpiredDbCount(countByStatus[TransactionStatus.EXPIRED] || 0)
|
||||
setErroredDbCount(countByStatus[TransactionStatus.ERROR] || 0)
|
||||
setRevertedDbCount(countByStatus[TransactionStatus.REVERTED] || 0)
|
||||
setTotalDbCount(countByStatus.total)
|
||||
|
||||
setDbCount(count)
|
||||
setPendingDbCount(pendingCount)
|
||||
setExpiredDbCount(expiredCount)
|
||||
setErroredDbCount(erroredCount)
|
||||
setRevertedDbCount(revertedCount)
|
||||
// Preload transactions to model in case they are not there
|
||||
if(countByStatus.total > 0) {
|
||||
if(transactionsStore.historyCount === 0) {
|
||||
transactionsStore.addToHistory(limit, 0, false)
|
||||
}
|
||||
|
||||
if(transactionsStore.recentByUnit.length === 0) {
|
||||
transactionsStore.addRecentByUnit()
|
||||
}
|
||||
}
|
||||
|
||||
setIsLoading(false)
|
||||
|
||||
if (count <= limit) {
|
||||
log.trace('setAll true')
|
||||
if (countByStatus.total <= limit) {
|
||||
log.trace('[init] setAll true')
|
||||
setIsAll(true)
|
||||
}
|
||||
|
||||
if (pendingCount <= limit) {
|
||||
/* if (countByStatus[TransactionStatus.PENDING] <= limit) {
|
||||
log.trace('setPendingAll true')
|
||||
setPendingIsAll(true)
|
||||
}
|
||||
// Run on component unmount (cleanup)
|
||||
return () => {
|
||||
/* When leaving screen we remove all transactions over maxTransactionsByMint
|
||||
* from the transactionsStore that might have been sourced from sqlite db while browsing older records
|
||||
*/
|
||||
for (const mint of mintsStore.allMints) {
|
||||
transactionsStore.removeOldByMint(mint.mintUrl)
|
||||
transactionsStore.removeAllWithoutCurrentMint() // avoid that tx from deleted mints remain in model forever
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
init()
|
||||
return () => {}
|
||||
return () => {
|
||||
if(showPendingOnly) {
|
||||
// Full clean if filtered, next visit will reload from db
|
||||
transactionsStore.removeAllHistory()
|
||||
} else {
|
||||
// Keep recent in history to load fast on next visit
|
||||
transactionsStore.pruneHistory()
|
||||
}
|
||||
// general cleanup - avoid that tx from deleted mints remain in state forever
|
||||
transactionsStore.pruneRecentWithoutCurrentMint()
|
||||
}
|
||||
}, [])
|
||||
|
||||
const toggleDeleteModal = () => {
|
||||
setIsDeleteModalVisible(previousState => !previousState)
|
||||
}
|
||||
|
||||
// TODO debug
|
||||
const getTransactionsList = async function () {
|
||||
|
||||
const addTransactionsToList = function () {
|
||||
setIsLoading(true)
|
||||
try {
|
||||
const result = await Database.getTransactionsAsync(limit, offset)
|
||||
transactionsStore.addToHistory(limit, transactionsStore.historyCount, false)
|
||||
|
||||
if (result && result.length > 0) {
|
||||
// Add new transaction to the transactions store so mobx refreshes UI
|
||||
transactionsStore.addTransactionsToModel(result._array)
|
||||
log.trace('[addTransactionsToList]', {
|
||||
currentOffset: transactionsStore.historyCount,
|
||||
totalDbCount
|
||||
})
|
||||
|
||||
setOffset(offset + result.length)
|
||||
|
||||
log.trace({storeCount: transactionsStore.count, dbCount})
|
||||
if (transactionsStore.count >= dbCount) {
|
||||
log.trace('[getTransactionsList] setAll true')
|
||||
setIsAll(true)
|
||||
}
|
||||
}
|
||||
if (transactionsStore.historyCount >= totalDbCount) {
|
||||
log.trace('[getTransactionsList] setAll true')
|
||||
setIsAll(true)
|
||||
}
|
||||
|
||||
setIsLoading(false)
|
||||
} catch (e: any) {
|
||||
handleError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TODO debug
|
||||
const getPendingTransactionsList = async function () {
|
||||
const addPendingTransactionsToList = function () {
|
||||
setIsLoading(true)
|
||||
try {
|
||||
const result = await Database.getTransactionsAsync(limit, pendingOffset, true) // pending
|
||||
transactionsStore.addToHistory(limit, transactionsStore.historyCount, true)
|
||||
|
||||
if (result && result.length > 0) {
|
||||
// Add new transaction to the transactions store so that mobx refreshes UI
|
||||
transactionsStore.addTransactionsToModel(result._array)
|
||||
log.trace('[addTransactionsToList] onlyPending', {
|
||||
currentOffset: transactionsStore.historyCount,
|
||||
pendingDbCount
|
||||
})
|
||||
|
||||
setOffset(pendingOffset + result.length)
|
||||
|
||||
if (transactionsStore.pending.length === pendingDbCount) {
|
||||
log.trace('[getTransactionsList] setAll true')
|
||||
setPendingIsAll(true)
|
||||
}
|
||||
}
|
||||
if (transactionsStore.historyCount >= pendingDbCount) {
|
||||
log.trace('[getTransactionsList] onlyPending setAll true')
|
||||
setPendingIsAll(true)
|
||||
}
|
||||
|
||||
setIsLoading(false)
|
||||
} catch (e: any) {
|
||||
handleError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const toggleShowPendingOnly = async function () {
|
||||
if (showPendingOnly) {
|
||||
const toggleShowPendingOnly = function () {
|
||||
if (showPendingOnly) {
|
||||
transactionsStore.removeAllHistory()
|
||||
addTransactionsToList()
|
||||
setShowPendingOnly(false)
|
||||
} else {
|
||||
await getPendingTransactionsList()
|
||||
transactionsStore.removeAllHistory()
|
||||
addPendingTransactionsToList() // hydrate with onlyPending = true
|
||||
setShowPendingOnly(true)
|
||||
}
|
||||
}
|
||||
@@ -192,59 +195,24 @@ export const TranHistoryScreen: FC<TransactionsStackScreenProps<'TranHistory'>>
|
||||
}
|
||||
|
||||
|
||||
const onDeleteExpired = function () {
|
||||
const onDelete = function (status: TransactionStatus) {
|
||||
try {
|
||||
toggleDeleteModal()
|
||||
setIsLoading(true)
|
||||
transactionsStore.deleteByStatus(TransactionStatus.EXPIRED)
|
||||
|
||||
const count = Database.getTransactionsCount() // all
|
||||
const expiredCount = Database.getTransactionsCount(TransactionStatus.EXPIRED)
|
||||
|
||||
setDbCount(count)
|
||||
setExpiredDbCount(expiredCount)
|
||||
transactionsStore.deleteByStatus(status)
|
||||
const countByStatus = Database.getTransactionsCount()
|
||||
|
||||
setPendingDbCount(countByStatus[TransactionStatus.PENDING] || 0)
|
||||
setExpiredDbCount(countByStatus[TransactionStatus.EXPIRED] || 0)
|
||||
setErroredDbCount(countByStatus[TransactionStatus.ERROR] || 0)
|
||||
setRevertedDbCount(countByStatus[TransactionStatus.REVERTED] || 0)
|
||||
setTotalDbCount(countByStatus.total)
|
||||
setIsLoading(false)
|
||||
} catch (e: any) {
|
||||
handleError(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const onDeleteErrored = function () {
|
||||
try {
|
||||
toggleDeleteModal()
|
||||
setIsLoading(true)
|
||||
transactionsStore.deleteByStatus(TransactionStatus.ERROR)
|
||||
|
||||
const count = Database.getTransactionsCount() // all
|
||||
const erroredCount = Database.getTransactionsCount(TransactionStatus.ERROR)
|
||||
|
||||
setDbCount(count)
|
||||
setErroredDbCount(erroredCount)
|
||||
setIsLoading(false)
|
||||
} catch (e: any) {
|
||||
handleError(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const onDeleteReverted = function () {
|
||||
try {
|
||||
toggleDeleteModal()
|
||||
setIsLoading(true)
|
||||
transactionsStore.deleteByStatus(TransactionStatus.REVERTED)
|
||||
|
||||
const count = Database.getTransactionsCount() // all
|
||||
const revertedCount = Database.getTransactionsCount(TransactionStatus.REVERTED)
|
||||
|
||||
setDbCount(count)
|
||||
setRevertedDbCount(revertedCount)
|
||||
setIsLoading(false)
|
||||
} catch (e: any) {
|
||||
handleError(e)
|
||||
}
|
||||
}
|
||||
|
||||
const handleError = function (e: AppError): void {
|
||||
setIsLoading(false)
|
||||
setError(e)
|
||||
@@ -255,12 +223,12 @@ export const TranHistoryScreen: FC<TransactionsStackScreenProps<'TranHistory'>>
|
||||
const activeIconColor = useThemeColor('button')
|
||||
const headerTitle = useThemeColor('headerTitle')
|
||||
|
||||
const sections = showPendingOnly ? Object.keys(transactionsStore.groupedPendingByTimeAgo).map((timeAgo) => ({
|
||||
const sections = showPendingOnly ? Object.keys(transactionsStore.historyPendingByTimeAgo).map((timeAgo) => ({
|
||||
title: timeAgo,
|
||||
data: transactionsStore.groupedPendingByTimeAgo[timeAgo],
|
||||
})) : Object.keys(transactionsStore.groupedByTimeAgo).map((timeAgo) => ({
|
||||
data: transactionsStore.historyPendingByTimeAgo[timeAgo],
|
||||
})) : Object.keys(transactionsStore.historyByTimeAgo).map((timeAgo) => ({
|
||||
title: timeAgo,
|
||||
data: transactionsStore.groupedByTimeAgo[timeAgo],
|
||||
data: transactionsStore.historyByTimeAgo[timeAgo],
|
||||
}))
|
||||
|
||||
return (
|
||||
@@ -277,12 +245,12 @@ export const TranHistoryScreen: FC<TransactionsStackScreenProps<'TranHistory'>>
|
||||
<ListItem
|
||||
text={showPendingOnly
|
||||
? translate("tranHistory.showingPaginationPending", {
|
||||
amount: transactionsStore.pending.length,
|
||||
amount: transactionsStore.pendingHistoryCount,
|
||||
total: pendingDbCount
|
||||
})
|
||||
: translate("tranHistory.showingPaginationTotal", {
|
||||
amount: transactionsStore.count,
|
||||
total: dbCount
|
||||
amount: transactionsStore.historyCount,
|
||||
total: totalDbCount
|
||||
})
|
||||
}
|
||||
LeftComponent={
|
||||
@@ -299,7 +267,7 @@ export const TranHistoryScreen: FC<TransactionsStackScreenProps<'TranHistory'>>
|
||||
/>
|
||||
<ListItem
|
||||
text={translate("tranHistory.pendingParam", {
|
||||
param: transactionsStore.pending.length
|
||||
param: transactionsStore.pendingHistoryCount
|
||||
})}
|
||||
LeftComponent={
|
||||
<Icon
|
||||
@@ -350,7 +318,7 @@ export const TranHistoryScreen: FC<TransactionsStackScreenProps<'TranHistory'>>
|
||||
) : (
|
||||
<Button
|
||||
preset="secondary"
|
||||
onPress={getTransactionsList}
|
||||
onPress={addTransactionsToList}
|
||||
tx="tranHistory.viewMore"
|
||||
style={{minHeight: 25, paddingVertical: spacing.tiny}}
|
||||
textStyle={{fontSize: 14}}
|
||||
@@ -389,7 +357,7 @@ export const TranHistoryScreen: FC<TransactionsStackScreenProps<'TranHistory'>>
|
||||
count: expiredDbCount
|
||||
})}
|
||||
leftIcon='faRotate'
|
||||
onPress={onDeleteExpired}
|
||||
onPress={() => onDelete(TransactionStatus.EXPIRED)}
|
||||
bottomSeparator={true}
|
||||
/>
|
||||
<ListItem
|
||||
@@ -398,7 +366,7 @@ export const TranHistoryScreen: FC<TransactionsStackScreenProps<'TranHistory'>>
|
||||
count: erroredDbCount
|
||||
})}
|
||||
leftIcon='faBug'
|
||||
onPress={onDeleteErrored}
|
||||
onPress={() => onDelete(TransactionStatus.ERROR)}
|
||||
bottomSeparator={true}
|
||||
/>
|
||||
<ListItem
|
||||
@@ -407,7 +375,7 @@ export const TranHistoryScreen: FC<TransactionsStackScreenProps<'TranHistory'>>
|
||||
count: revertedDbCount
|
||||
})}
|
||||
leftIcon='faBan'
|
||||
onPress={onDeleteReverted}
|
||||
onPress={() => onDelete(TransactionStatus.REVERTED)}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -146,8 +146,7 @@ export const WalletScreen: FC<WalletScreenProps> = observer(
|
||||
}
|
||||
|
||||
// On app start
|
||||
useEffect(() => {
|
||||
// get deeplink if any
|
||||
useEffect(() => {
|
||||
const getInitialData = async () => {
|
||||
// get deeplink data if any
|
||||
const url = await Linking.getInitialURL()
|
||||
@@ -465,6 +464,8 @@ export const WalletScreen: FC<WalletScreenProps> = observer(
|
||||
const renderUnitTabs = function ({ route }: { route: { key: string } }) {
|
||||
const unitMints = groupedMints.find((mintUnit) => mintUnit.unit === route.key)
|
||||
|
||||
// log.trace('[renderUnitTabs]', {recentByUnit: transactionsStore.getRecentByUnit(unitMints!.unit), unit: unitMints!.unit})
|
||||
|
||||
if (unitMints) {
|
||||
return (
|
||||
<>
|
||||
@@ -487,11 +488,11 @@ export const WalletScreen: FC<WalletScreenProps> = observer(
|
||||
</Pressable>
|
||||
</View>
|
||||
<View style={$tabContainer}>
|
||||
{transactionsStore.recentByUnit(unitMints.unit).length > 0 ? (
|
||||
{transactionsStore.getRecentByUnit(unitMints.unit).length > 0 ? (
|
||||
<Card
|
||||
ContentComponent={
|
||||
<FlatList
|
||||
data={transactionsStore.recentByUnit(unitMints.unit) as Transaction[]}
|
||||
data={transactionsStore.getRecentByUnit(unitMints.unit) as Transaction[]}
|
||||
renderItem={({item, index}) => {
|
||||
return (
|
||||
<TransactionListItem
|
||||
|
||||
+136
-83
@@ -16,7 +16,7 @@ import {log} from './logService'
|
||||
import {LogLevel} from './log/logTypes'
|
||||
import {BackupProof} from '../models/Proof'
|
||||
import { CashuUtils } from './cashu/cashuUtils'
|
||||
import { CurrencyCode } from './wallet/currency'
|
||||
import { CurrencyCode, MintUnit } from './wallet/currency'
|
||||
import { ThemeCode } from '../theme'
|
||||
|
||||
let _db: QuickSQLiteConnection
|
||||
@@ -517,32 +517,141 @@ const updateUserSettings = function (settings: UserSettings): UserSettings {
|
||||
* Transactions
|
||||
*/
|
||||
|
||||
const getTransactionsCount = function (status?: TransactionStatus) {
|
||||
let query: string = ''
|
||||
let params
|
||||
try {
|
||||
query = `
|
||||
SELECT COUNT(*) FROM transactions
|
||||
`
|
||||
const getTransactions = function (limit: number, offset: number, onlyPending: boolean = false) {
|
||||
let query: string = ''
|
||||
try {
|
||||
query = `
|
||||
SELECT *
|
||||
FROM transactions
|
||||
ORDER BY id DESC
|
||||
LIMIT ? OFFSET ?
|
||||
`
|
||||
|
||||
if(status) {
|
||||
query = `
|
||||
SELECT COUNT(*)
|
||||
FROM transactions
|
||||
WHERE status = ?
|
||||
`
|
||||
params = [status]
|
||||
}
|
||||
if(onlyPending) {
|
||||
query = `
|
||||
SELECT *
|
||||
FROM transactions
|
||||
WHERE status = 'PENDING'
|
||||
ORDER BY id DESC
|
||||
LIMIT ? OFFSET ?
|
||||
`
|
||||
}
|
||||
|
||||
const db = getInstance()
|
||||
const {rows} = db.execute(query, params)
|
||||
|
||||
return rows?.item(0)['COUNT(*)'] as number
|
||||
} catch (e: any) {
|
||||
throw new AppError(Err.DATABASE_ERROR, 'Transaction count error', e.message)
|
||||
}
|
||||
const params = [limit, offset]
|
||||
|
||||
// log.trace(query, params)
|
||||
|
||||
const db = getInstance()
|
||||
const {rows} = db.execute(query, params)
|
||||
|
||||
log.trace(`[getTransactions], Returned ${rows?.length} rows`)
|
||||
return rows
|
||||
} catch (e: any) {
|
||||
throw new AppError(
|
||||
Err.DATABASE_ERROR,
|
||||
'Transactions could not be retrieved from the database',
|
||||
e.message,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const getTransactionsCount = function (status?: TransactionStatus) {
|
||||
let query: string
|
||||
let params: any[] = []
|
||||
|
||||
try {
|
||||
if (status) {
|
||||
// Query to get the count for a specific status along with the total
|
||||
query = `
|
||||
WITH total_count AS (
|
||||
SELECT COUNT(*) AS total FROM transactions
|
||||
)
|
||||
SELECT status, COUNT(*) AS count, (SELECT total FROM total_count) AS total
|
||||
FROM transactions
|
||||
WHERE status = ?
|
||||
GROUP BY status
|
||||
`
|
||||
params = [status]
|
||||
} else {
|
||||
// Query to get the count per status along with the total
|
||||
query = `
|
||||
WITH total_count AS (
|
||||
SELECT COUNT(*) AS total FROM transactions
|
||||
)
|
||||
SELECT status, COUNT(*) AS count, (SELECT total FROM total_count) AS total
|
||||
FROM transactions
|
||||
GROUP BY status
|
||||
`
|
||||
}
|
||||
|
||||
const db = getInstance()
|
||||
const { rows } = db.execute(query, params)
|
||||
|
||||
// Convert rows to an object with status counts and a total count
|
||||
if(rows) {
|
||||
const counts: Record<string, number> = { total: 0 }
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const row = rows.item(i)
|
||||
counts[row.status] = row.count
|
||||
counts.total = row.total
|
||||
}
|
||||
return counts
|
||||
} else {
|
||||
return {total: 0}
|
||||
}
|
||||
|
||||
} catch (e: any) {
|
||||
throw new AppError(Err.DATABASE_ERROR, 'Transaction count error', e.message)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const getRecentTransactionsByUnit = (countRecent: number) => {
|
||||
try {
|
||||
const query = `
|
||||
SELECT *
|
||||
FROM (
|
||||
SELECT *,
|
||||
ROW_NUMBER() OVER (PARTITION BY unit ORDER BY createdAt DESC) as row_num
|
||||
FROM transactions
|
||||
)
|
||||
WHERE row_num <= ?
|
||||
ORDER BY unit, createdAt DESC
|
||||
`
|
||||
|
||||
const params = [countRecent]
|
||||
const db = getInstance()
|
||||
const { rows } = db.execute(query, params)
|
||||
|
||||
return rows?._array
|
||||
|
||||
} catch (e: any) {
|
||||
throw new AppError(Err.DATABASE_ERROR, 'Error retrieving last 3 transactions by unit', e.message)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const getPendingAmount = function () {
|
||||
try {
|
||||
const query = `
|
||||
SELECT
|
||||
SUM(amount)
|
||||
FROM transactions
|
||||
WHERE status = ?
|
||||
`
|
||||
const params = [TransactionStatus.PENDING]
|
||||
|
||||
const db = getInstance()
|
||||
const {rows} = db.execute(query, params)
|
||||
|
||||
return rows?.item(0)['SUM(amount)']
|
||||
} catch (e: any) {
|
||||
throw new AppError(Err.DATABASE_ERROR, 'Transaction not found', e.message)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const getTransactionById = function (id: number) {
|
||||
try {
|
||||
const query = `
|
||||
@@ -1002,63 +1111,6 @@ const updateZapRequest = function (id: number, zapRequest: string) {
|
||||
}
|
||||
} */
|
||||
|
||||
const getTransactionsAsync = async function (limit: number, offset: number, isPending: boolean = false) {
|
||||
let query: string = ''
|
||||
try {
|
||||
query = `
|
||||
SELECT *
|
||||
FROM transactions
|
||||
ORDER BY id DESC
|
||||
LIMIT ? OFFSET ?
|
||||
`
|
||||
|
||||
if(isPending) {
|
||||
query = `
|
||||
SELECT *
|
||||
FROM transactions
|
||||
WHERE status = 'PENDING'
|
||||
ORDER BY id DESC
|
||||
LIMIT ? OFFSET ?
|
||||
`
|
||||
}
|
||||
|
||||
const params = [limit, offset]
|
||||
|
||||
// log.trace(query, params)
|
||||
|
||||
const db = getInstance()
|
||||
const {rows} = await db.executeAsync(query, params)
|
||||
|
||||
log.trace('[getTransactionsAsync], Returned rows', rows?.length)
|
||||
return rows
|
||||
} catch (e: any) {
|
||||
throw new AppError(
|
||||
Err.DATABASE_ERROR,
|
||||
'Transactions could not be retrieved from the database',
|
||||
e.message,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const getPendingAmount = function () {
|
||||
try {
|
||||
const query = `
|
||||
SELECT
|
||||
SUM(amount)
|
||||
FROM transactions
|
||||
WHERE status = ?
|
||||
`
|
||||
const params = [TransactionStatus.PENDING]
|
||||
|
||||
const db = getInstance()
|
||||
const {rows} = db.execute(query, params)
|
||||
|
||||
return rows?.item(0)['SUM(amount)']
|
||||
} catch (e: any) {
|
||||
throw new AppError(Err.DATABASE_ERROR, 'Transaction not found', e.message)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const deleteTransactionsByStatus = function (status: TransactionStatus) {
|
||||
try {
|
||||
@@ -1381,7 +1433,9 @@ export const Database = {
|
||||
updateUserSettings,
|
||||
getTransactionsCount,
|
||||
getTransactionById,
|
||||
addTransactionAsync,
|
||||
getRecentTransactionsByUnit,
|
||||
getTransactions,
|
||||
addTransactionAsync,
|
||||
updateStatus,
|
||||
expireAllAfterRecovery,
|
||||
updateStatusesAsync,
|
||||
@@ -1395,8 +1449,7 @@ export const Database = {
|
||||
updateZapRequest,
|
||||
updateInputToken,
|
||||
updateOutputToken,
|
||||
updateProof,
|
||||
getTransactionsAsync,
|
||||
updateProof,
|
||||
deleteTransactionsByStatus,
|
||||
getPendingAmount,
|
||||
addOrUpdateProof,
|
||||
|
||||
Reference in New Issue
Block a user