Privacy: turn on-off error logging, set log levels

This commit is contained in:
minibits-cash
2023-11-03 00:02:51 +01:00
parent 6bc67c3360
commit 51eb334950
70 changed files with 561 additions and 576 deletions
+1 -3
View File
@@ -81,9 +81,7 @@ jobs:
- name: Generate .env
if: ${{ env.IS_VERSION_CHANGED == 'true' }}
run: |-
echo APP_ENV='PROD' >> .env
echo LOG_LEVEL='INFO' >> .env
echo SENTRY_ACTIVE='TRUE' >> .env
echo APP_ENV='PROD' >> .env
echo SENTRY_DSN='${{ secrets.SENTRY_DSN }}' >> .env
echo CODEPUSH_PRODUCTION_DEPLOYMENT_KEY='${{ secrets.CODEPUSH_PRODUCTION_DEPLOYMENT_KEY }}' >> .env
echo NATIVE_VERSION_ANDROID='${{ env.NATIVE_VERSION }}' >> .env
+1 -3
View File
@@ -81,9 +81,7 @@ jobs:
- name: Generate .env
if: ${{ env.IS_VERSION_CHANGED == 'true' }}
run: |-
echo APP_ENV='TEST' >> .env
echo LOG_LEVEL='INFO' >> .env
echo SENTRY_ACTIVE='TRUE' >> .env
echo APP_ENV='TEST' >> .env
echo SENTRY_DSN='${{ secrets.SENTRY_DSN }}' >> .env
echo CODEPUSH_STAGING_DEPLOYMENT_KEY='${{ secrets.CODEPUSH_STAGING_DEPLOYMENT_KEY }}' >> .env
echo NATIVE_VERSION_ANDROID='${{ env.NATIVE_VERSION }}' >> .env
+1
View File
@@ -1,5 +1,6 @@
# App specific
.env
.vscode/
# OSX
#
.DS_Store
-1
View File
@@ -153,7 +153,6 @@ After the dependecies are installed, continue to create the following .env file
```bash
APP_ENV = 'DEV'
LOG_LEVEL = 'TRACE'
SENTRY_ACTIVE = 'FALSE'
```
+1
View File
@@ -51,6 +51,7 @@
"react-native-gesture-handler": "^2.9.0",
"react-native-json-tree": "^1.3.0",
"react-native-keychain": "^8.1.1",
"react-native-logs": "^5.0.1",
"react-native-mmkv": "^2.8.0",
"react-native-modal": "^13.0.1",
"react-native-pager-view": "^6.2.0",
+7 -6
View File
@@ -6,8 +6,7 @@ import {
JS_BUNDLE_VERSION,
NATIVE_VERSION_ANDROID,
CODEPUSH_STAGING_DEPLOYMENT_KEY,
CODEPUSH_PRODUCTION_DEPLOYMENT_KEY,
SENTRY_ACTIVE,
CODEPUSH_PRODUCTION_DEPLOYMENT_KEY,
} from '@env'
import codePush from 'react-native-code-push'
import FlashMessage from "react-native-flash-message"
@@ -24,13 +23,14 @@ import {useInitialRootStore, useStores} from './models'
import {Database} from './services'
import {ErrorBoundary} from './screens/ErrorScreen/ErrorBoundary'
import Config from './config'
import {Env, log, SentryActive} from './utils/logger'
import {log} from './services'
import {Env} from './utils/envtypes'
import AppError from './utils/AppError'
setSizeMattersBaseWidth(375)
setSizeMattersBaseHeight(812)
if (!__DEV__ && SENTRY_ACTIVE === SentryActive.TRUE) {
if (!__DEV__) {
Sentry.init({
dsn: SENTRY_DSN,
environment: APP_ENV,
@@ -55,7 +55,7 @@ function App(props: AppProps) {
const {userSettingsStore} = useStores()
const {rehydrated} = useInitialRootStore(() => {
// This runs after the root store has been initialized and rehydrated from storage.
log.trace('Root store rehydrated', [], 'useInitialRootStore')
// Creates and opens a sqlite database that stores transactions history and user settings.
// It triggers db migrations if database version has changed.
@@ -63,7 +63,8 @@ function App(props: AppProps) {
Database.getDatabaseVersion()
// Syncs userSettings store with the database where they are persisted
userSettingsStore.loadUserSettings()
userSettingsStore.loadUserSettings()
log.trace('[useInitialRootStore]', 'Root store rehydrated')
})
if (!rehydrated) {
-1
View File
@@ -10,7 +10,6 @@ import {
import {moderateScale, scale, verticalScale} from '@gocodingnow/rn-size-matters'
import { colors, spacing, typography, useThemeColor } from "../theme"
import { Text, TextProps } from "./Text"
import { log } from "../utils/logger"
type Presets = keyof typeof $viewPresets
+5 -2
View File
@@ -171,7 +171,9 @@ const en = {
privacyScreen: {
torDaemon: "Use Tor network",
torDaemonDescription: "Allow connections with mints through Tor .onion addresses.",
torStatus: "Tor status"
torStatus: "Tor status",
logger: "Enable logging",
loggerDescription: "Log anonymous error information in order to identify and fix wallet bugs during beta testing. Switch off for best privacy.",
},
updateScreen: {
updateAvailable: "Update is available",
@@ -192,7 +194,8 @@ const en = {
resetDescription: "This will wipe out all local data from storage and from the local database. Do only during development or testing.",
go: "Let's go!",
devOptions: "Developer options",
info: "About Minibits wallet"
info: "About Minibits wallet",
logLevel: "Log level"
},
errorScreen: {
title: "Something went wrong!",
+3 -4
View File
@@ -1,6 +1,6 @@
import {Instance, SnapshotIn, SnapshotOut, types} from 'mobx-state-tree'
import { MINIBITS_NIP05_DOMAIN, MINIBITS_SERVER_API_HOST } from '@env'
import { log } from '../utils/logger'
import { log } from '../services/logService'
export type ContactData = {
[index: string]: any
@@ -28,10 +28,9 @@ export const ContactModel = types
})
.actions(self => ({
refreshPicture() {
const cleaned = MINIBITS_SERVER_API_HOST + '/profile/avatar/' + self.pubkey // remove refresh suffix
log.trace('Cleaned', cleaned)
const cleaned = MINIBITS_SERVER_API_HOST + '/profile/avatar/' + self.pubkey // remove refresh suffix
self.picture = cleaned + '?r=' + Math.floor(Math.random() * 100) // force url refresh
log.trace('Refreshed', self.picture)
},
}))
.views(self => ({
+60 -81
View File
@@ -12,15 +12,14 @@ import {
ContactModel,
Contact,
} from './Contact'
import {log} from '../utils/logger'
import {log} from '../services/logService'
// export const maxContactsInModel = 10
export const ContactsStoreModel = types
.model('ContactsStore', {
contacts: types.array(ContactModel),
publicPubkey: types.maybe(types.string),
publicRelay: types.maybe(types.string), // TODO remove, replaced by relaysStore
publicPubkey: types.maybe(types.string),
lastPendingReceivedCheck: types.maybe(types.number), // UNIX timestamp
receivedEventIds: types.optional(types.array(types.string), [])
})
@@ -33,91 +32,71 @@ import {
findByNpub: (npub: string) => {
const c = self.contacts.find(c => c.npub === npub)
return c ? c : undefined
},
removeContact: (removedContact: Contact) => {
let contactInstance: Contact | undefined
if (isStateTreeNode(removedContact)) {
contactInstance = removedContact
} else {
contactInstance = self.contacts.find(
(c: Contact) => c.pubkey === (removedContact as Contact).pubkey,
)
}
if (contactInstance) {
detach(contactInstance) // needed
destroy(contactInstance)
log.trace('Contact removed from ContactsStore')
}
},
},
}))
.actions(self => ({
addContact(newContact: Contact) {
addContact(newContact: Contact) {
newContact.createdAt = Math.floor(Date.now() / 1000)
const contactInstance = ContactModel.create(newContact)
self.contacts.push(contactInstance)
log.trace('New contact added to the ContactsStore', newContact, 'addContact')
return contactInstance
},
refreshPicture(pubkey: string) {
newContact.createdAt = Math.floor(Date.now() / 1000)
const contactInstance = ContactModel.create(newContact)
self.contacts.push(contactInstance)
log.debug('[addContact]', 'New contact added to the ContactsStore', newContact)
return contactInstance
},
refreshPicture(pubkey: string) {
const contactInstance = self.findByPubkey(pubkey)
if (contactInstance) {
contactInstance.refreshPicture()
log.trace('Contact picture refreshed in ContactsStore')
}
const contactInstance = self.findByPubkey(pubkey)
if (contactInstance) {
contactInstance.refreshPicture()
log.debug('[refreshPicture]', 'Contact picture refreshed in ContactsStore')
}
return contactInstance
},
saveNote (pubkey: string, note: string) {
const contactInstance = self.findByPubkey(pubkey)
if (contactInstance) {
contactInstance.noteToSelf = note
log.trace('Contact note updated in ContactsStore')
}
},
removeContact(contactToBeRemoved: Contact) {
let contactInstance: Contact | undefined
return contactInstance
},
saveNote (pubkey: string, note: string) {
const contactInstance = self.findByPubkey(pubkey)
if (contactInstance) {
contactInstance.noteToSelf = note
log.debug('[saveNote]', 'Contact note updated in ContactsStore')
}
},
removeContact(contactToBeRemoved: Contact) {
let contactInstance: Contact | undefined
if (isStateTreeNode(contactToBeRemoved)) {
contactInstance = contactToBeRemoved
} else {
contactInstance = self.findByNpub((contactToBeRemoved as Contact).npub)
}
if (isStateTreeNode(contactToBeRemoved)) {
contactInstance = contactToBeRemoved
} else {
contactInstance = self.findByNpub((contactToBeRemoved as Contact).npub)
}
if (contactInstance) {
destroy(contactInstance)
log.info('Contact removed from MintsStore')
}
},
removeAllContacts() {
self.contacts.clear()
log.info('Removed all Contacts from ContactsStore')
},
setPublicPubkey(publicPubkey: string) {
self.publicPubkey = publicPubkey
log.info('setPublicPubkey', publicPubkey)
},
setPublicRelay(publicRelay: string) {
self.publicRelay = publicRelay
log.info('setPublicRelay', publicRelay)
},
setLastPendingReceivedCheck() {
const ts: number = Math.floor(Date.now() / 1000)
log.trace('Timestamp', {ts}, 'setLastPendingReceivedCheck')
self.lastPendingReceivedCheck = ts
},
addReceivedEventId(id: string) {
self.receivedEventIds.push(id)
},
eventAlreadyReceived(id: string) {
return self.receivedEventIds.includes(id)
},
if (contactInstance) {
detach(contactInstance) // needed
destroy(contactInstance)
log.debug('[removeContact]', 'Contact removed from MintsStore')
}
},
removeAllContacts() {
self.contacts.clear()
log.debug('[removeAllContacts]', 'Removed all Contacts from ContactsStore')
},
setPublicPubkey(publicPubkey: string) {
self.publicPubkey = publicPubkey
log.debug('[setPublicPubkey]', publicPubkey)
},
setLastPendingReceivedCheck() {
const ts: number = Math.floor(Date.now() / 1000)
log.trace('[setLastPendingReceivedCheck]', {ts})
self.lastPendingReceivedCheck = ts
},
addReceivedEventId(id: string) {
self.receivedEventIds.push(id)
},
eventAlreadyReceived(id: string) {
return self.receivedEventIds.includes(id)
},
}))
.views(self => ({
get count() {
+3 -6
View File
@@ -1,7 +1,7 @@
import {Instance, SnapshotIn, SnapshotOut, types} from 'mobx-state-tree'
import {LightningUtils} from '../services/lightning/lightningUtils'
import {withSetPropAction} from './helpers/withSetPropAction'
import {log} from '../utils/logger'
import {log} from '../services/logService'
import addSeconds from 'date-fns/addSeconds'
/**
@@ -16,7 +16,7 @@ export const InvoiceModel = types
description: types.optional(types.string, ''),
paymentHash: types.string,
expiry: types.optional(types.number, 600),
memo: types.optional(types.string, 'Topup of minibits wallet'),
memo: types.optional(types.string, 'Topup of Minibits wallet'),
transactionId: types.number,
expiresAt: types.maybe(types.Date),
createdAt: types.optional(types.Date, new Date()),
@@ -29,10 +29,7 @@ export const InvoiceModel = types
const expiresAt = addSeconds(new Date(timestamp as number * 1000), expiry as number)
log.trace(
`Invoice expiry is ${expiry}, setting expiresAt to ${expiresAt}`,
)
log.trace('[setExpiresAt]', `Invoice expiry is ${expiry}, setting expiresAt to ${expiresAt}`)
self.expiresAt = new Date(expiresAt)
},
}))
+3 -3
View File
@@ -7,7 +7,7 @@ import {
} from 'mobx-state-tree'
import {withSetPropAction} from './helpers/withSetPropAction'
import {InvoiceModel, Invoice} from './Invoice'
import {log} from '../utils/logger'
import {log} from '../services/logService'
export const InvoicesStoreModel = types
.model('Invoices', {
@@ -31,7 +31,7 @@ export const InvoicesStoreModel = types
invoiceInstance.setExpiresAt()
self.invoices.push(invoiceInstance)
log.info('New invoice added to InvoicesStore', newInvoice)
log.info('[addInvoice]', 'New invoice added to InvoicesStore', newInvoice)
return invoiceInstance
},
@@ -49,7 +49,7 @@ export const InvoicesStoreModel = types
}
if (invoiceInstance) {
log.trace('Invoice to be removed from InvoicesStore', invoiceToRemove)
log.info('[removeInvoice]', 'Invoice to be removed from InvoicesStore', invoiceToRemove)
destroy(invoiceInstance)
}
},
+6 -6
View File
@@ -7,7 +7,7 @@ import {
} from 'mobx-state-tree'
import {withSetPropAction} from './helpers/withSetPropAction'
import {MintModel, Mint} from './Mint'
import {log} from '../utils/logger'
import {log} from '../services/logService'
export type MintsByHostname = {
hostname: string
@@ -44,12 +44,12 @@ export const MintsStoreModel = types
self.mints.push(mintInstance)
log.info('New mint added to the MintsStore', newMint.mintUrl)
log.info('[addMint]', 'New mint added to the MintsStore', newMint.mintUrl)
},
removeMint(mintToBeRemoved: Mint) {
if (self.blockedMintUrls.some(m => m === mintToBeRemoved.mintUrl)) {
self.blockedMintUrls.remove(mintToBeRemoved.mintUrl)
log.info('Mint removed from blockedMintUrls')
log.debug('[removeMint]', 'Mint removed from blockedMintUrls')
}
let mintInstance: Mint | undefined
@@ -62,16 +62,16 @@ export const MintsStoreModel = types
if (mintInstance) {
destroy(mintInstance)
log.trace('Mint removed from MintsStore')
log.info('[removeMint]', 'Mint removed from MintsStore')
}
},
blockMint(mintToBeBlocked: Mint) {
self.blockedMintUrls.push(mintToBeBlocked.mintUrl)
log.trace('Mint blocked in MintsStore')
log.debug('[blockMint]', 'Mint blocked in MintsStore')
},
unblockMint(blockedMint: Mint) {
self.blockedMintUrls.remove(blockedMint.mintUrl)
log.trace('Mint unblocked in MintsStore')
log.debug('[unblockMint]', 'Mint unblocked in MintsStore')
},
}))
.views(self => ({
+2 -4
View File
@@ -1,7 +1,7 @@
import {Instance, SnapshotIn, SnapshotOut, types} from 'mobx-state-tree'
import {LightningUtils} from '../services/lightning/lightningUtils'
import {withSetPropAction} from './helpers/withSetPropAction'
import {log} from '../utils/logger'
import {log} from '../services/logService'
import addSeconds from 'date-fns/addSeconds'
/**
@@ -40,9 +40,7 @@ export const PaymentRequestModel = types
const expiresAt = addSeconds(new Date(timestamp * 1000), expiry as number)
log.trace(
`PaymentRequest expiry is ${expiry}, setting expiresAt to ${expiresAt}`,
)
log.debug('[setExpiresAt]', `PaymentRequest expiry is ${expiry}, setting expiresAt to ${expiresAt}`)
self.expiresAt = new Date(expiresAt)
},
setStatus(status: PaymentRequestStatus) {
+3 -3
View File
@@ -8,7 +8,7 @@ import {
} from 'mobx-state-tree'
import {withSetPropAction} from './helpers/withSetPropAction'
import {PaymentRequestModel, PaymentRequest, PaymentRequestStatus} from './PaymentRequest'
import {log} from '../utils/logger'
import {log} from '../services/logService'
import AppError, { Err } from '../utils/AppError'
import {LightningUtils} from '../services/lightning/lightningUtils'
import isBefore from 'date-fns/isBefore'
@@ -70,7 +70,7 @@ export const PaymentRequestsStoreModel = types
paymentRequestInstance.setExpiresAt()
self.paymentRequests.push(paymentRequestInstance)
log.info('New paymentRequest added to PaymentRequestsStore', newPaymentRequest)
log.info('[addPaymentRequest]', 'New paymentRequest added to PaymentRequestsStore', newPaymentRequest)
return paymentRequestInstance
},
@@ -90,7 +90,7 @@ export const PaymentRequestsStoreModel = types
if (paymentRequestInstance) {
detach(paymentRequestInstance)
destroy(paymentRequestInstance)
log.info('PaymentRequest removed from the store')
log.info('[removePaymentRequest]', 'PaymentRequest removed from the store')
}
},
+5 -7
View File
@@ -7,7 +7,7 @@ import {
} from 'mobx-state-tree'
import {withSetPropAction} from './helpers/withSetPropAction'
import {ProofModel, Proof} from './Proof'
import {log} from '../utils/logger'
import {log} from '../services/logService'
import {getRootStore} from './helpers/getRootStore'
import AppError, {Err} from '../utils/AppError'
import {Mint, MintBalance} from './Mint'
@@ -71,7 +71,7 @@ export const ProofsStoreModel = types
}
}
log.trace(`Added new ${newProofs.length}${isPending ? ' pending' : ''} proofs to ProofsStore`,)
log.debug('[addProofs]', `Added new ${newProofs.length}${isPending ? ' pending' : ''} proofs to ProofsStore`,)
const rootStore = getRootStore(self)
const {userSettingsStore} = rootStore
@@ -113,7 +113,7 @@ export const ProofsStoreModel = types
proofs.replace(proofs.filter(proof => !proofsToRemove.includes(proof)))
log.trace(`${count} ${(isPending) ? 'pending' : ''} proofs removed from ProofsStore`)
log.debug('[removeProofs]', `${count} ${(isPending) ? 'pending' : ''} proofs removed from ProofsStore`)
} catch (e: any) {
throw new AppError(Err.STORAGE_ERROR, e.message.toString())
@@ -171,9 +171,7 @@ export const ProofsStoreModel = types
totalPendingBalance,
mintBalances,
mintPendingBalances,
}
log.trace(balances)
}
return balances
},
@@ -199,7 +197,7 @@ export const ProofsStoreModel = types
return maxBalance
}, balances[0])
log.trace('Max Balance', maxBalance, 'getMintBalanceWithMaxBalance')
log.debug('[getMintBalanceWithMaxBalance]', maxBalance)
return maxBalance
},
getProofsToSend: (amount: number, proofs: Proof[]) => {
+6 -6
View File
@@ -8,7 +8,7 @@ import {
} from 'mobx-state-tree'
import {withSetPropAction} from './helpers/withSetPropAction'
import {RelayModel, Relay} from './Relay'
import {log} from '../utils/logger'
import {log} from '../services/logService'
import { MINIBITS_RELAY_URL } from '@env'
import { NostrClient } from '../services'
@@ -46,19 +46,19 @@ export const RelaysStoreModel = types
relayInstance?.setError(error)
}
log.trace('Relay updated in the RelaysStore', {relay}, 'addOrUpdateRelay')
log.debug('[addOrUpdateRelay]', 'Relay updated in the RelaysStore', {relay})
} else {
log.trace('Passed URL', relay.url)
log.trace('[addOrUpdateRelay]', 'Passed URL', relay.url)
const normalized = NostrClient.getNormalizedRelayUrl(relay.url)
log.trace('Normalized URL', normalized)
log.trace('[addOrUpdateRelay]', 'Normalized URL', normalized)
relay.url = normalized
const relayInstance = RelayModel.create(relay)
relayInstance.setHostname()
self.relays.push(relayInstance)
log.info('New relay added to the RelaysStore', {relay}, 'addOrUpdateRelay')
log.info('[addOrUpdateRelay]', 'New relay added to the RelaysStore', {relay})
}
},
removeRelay(relayUrl: string) {
@@ -68,7 +68,7 @@ export const RelaysStoreModel = types
if (relayInstance) {
detach(relayInstance)
destroy(relayInstance)
log.info('Relay removed from RelaysStore', {relayUrl}, 'removeRelay')
log.info('[removeRelay]', 'Relay removed from RelaysStore', {relayUrl})
}
},
}))
+1 -1
View File
@@ -9,7 +9,7 @@ import {PaymentRequestsStoreModel} from './PaymentRequestsStore'
import {ProofsStoreModel} from './ProofsStore'
import {RelaysStoreModel} from './RelaysStore'
export const rootStoreModelVersion = 5 // Update this if model changes require migrations defined in setupRootStore.ts
export const rootStoreModelVersion = 6 // Update this if model changes require migrations defined in setupRootStore.ts
/**
* A RootStore model.
+3 -2
View File
@@ -1,5 +1,5 @@
import {Instance, SnapshotIn, SnapshotOut, types} from 'mobx-state-tree'
import { log } from '../utils/logger'
import { log } from '../services/logService'
export interface TransactionRecord {
id?: number
@@ -13,7 +13,8 @@ export interface TransactionRecord {
balanceAfter?: number | null
noteToSelf?: string | null
tags?: Array<string> | null
status: TransactionStatus
status: TransactionStatus
createdAt: Date
}
export type TransactionData = {
+20 -29
View File
@@ -14,7 +14,7 @@ import {
TransactionRecord,
} from './Transaction'
import {Database} from '../services'
import {log} from '../utils/logger'
import {log} from '../services/logService'
export const maxTransactionsInModel = 10
@@ -27,21 +27,21 @@ export const TransactionsStoreModel = types
findById: (id: number) => {
const tx = self.transactions.find(tx => tx.id === id)
return tx ? tx : undefined
},
}
}))
.actions(self => ({
removeTransaction: (removedTransaction: Transaction) => {
let transactionInstance: Transaction | undefined
if (isStateTreeNode(removedTransaction)) {
transactionInstance = removedTransaction
} else {
transactionInstance = self.transactions.find(
(t: Transaction) => t.id === (removedTransaction as Transaction).id,
)
transactionInstance = self.findById((removedTransaction as Transaction).id as number)
}
if (transactionInstance) {
destroy(transactionInstance)
log.trace('Transaction removed from TransactionsStore')
log.info('[removeTransaction]', 'Transaction removed from TransactionsStore')
}
},
removeOldTransactions: () => {
@@ -53,10 +53,9 @@ export const TransactionsStoreModel = types
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime()) // Sort transactions by createdAt in descending order
.splice(maxTransactionsInModel) // Remove transactions beyond the desired number to keep
log.trace(
`${
log.debug('[removeOldTransactions]', `${
numTransactions - maxTransactionsInModel
} transaction(s) removed from TransactionsStore`,
} transaction(s) removed from TransactionsStore`,
)
}
},
@@ -73,11 +72,7 @@ export const TransactionsStoreModel = types
const transactionInstance = TransactionModel.create(inStoreTransaction)
self.transactions.push(transactionInstance)
log.trace(
'New transaction added to the TransactionsStore',
[],
'addTransaction',
)
log.debug('[addTransaction]', 'New transaction added to the TransactionsStore')
// Purge the oldest transaction from the model if the maximum number of transactions is reached
if (self.transactions.length > maxTransactionsInModel) {
@@ -95,13 +90,12 @@ export const TransactionsStoreModel = types
const inStoreTransaction = {...dbTransaction, createdAt}
const transactionInstance = TransactionModel.create(inStoreTransaction)
inStoreTransactions.push(transactionInstance)
inStoreTransactions.push(transactionInstance as Transaction)
}
self.transactions.push(...inStoreTransactions)
log.trace(
`${inStoreTransactions.length} new transactions added to TransactionsStore`,
log.debug('[addTransactionsToModel]', `${inStoreTransactions.length} new transactions added to TransactionsStore`,
)
},
updateStatus: flow(function* updateStatus(
@@ -117,10 +111,7 @@ export const TransactionsStoreModel = types
if (transactionInstance) {
transactionInstance.status = status
transactionInstance.data = data
log.trace(
'Transaction status and data updated in TransactionsStore',
'updateStatus',
)
log.debug('[updateStatus]', 'Transaction status and data updated in TransactionsStore', {id, status})
}
return transactionInstance
@@ -146,7 +137,7 @@ export const TransactionsStoreModel = types
transactionInstance.data = JSON.stringify(updatedData)
log.trace('Transaction status and data updated in TransactionsStore',[],'updateStatuses',)
log.debug('[updateStatuses]', 'Transaction statuses and data updated in TransactionsStore', {ids, status})
}
}
}),
@@ -162,7 +153,7 @@ export const TransactionsStoreModel = types
// Update in the model
if (transactionInstance) {
transactionInstance.balanceAfter = balanceAfter
log.trace('Transaction balanceAfter updated in TransactionsStore',[],'updateBalanceAfter',)
log.debug('[updateBalanceAfter]', 'Transaction balanceAfter updated in TransactionsStore', {balanceAfter})
}
return transactionInstance
@@ -176,7 +167,7 @@ export const TransactionsStoreModel = types
// Update in the model
if (transactionInstance) {
transactionInstance.fee = fee
log.trace('Transaction fee updated in TransactionsStore',[],'updateFee')
log.debug('[updateFee]', 'Transaction fee updated in TransactionsStore', {fee})
}
return transactionInstance
@@ -193,7 +184,7 @@ export const TransactionsStoreModel = types
// Update in the model
if (transactionInstance) {
transactionInstance.amount = amount
log.trace('Transaction amount updated in TransactionsStore')
log.debug('[updateReceivedAmount]', 'Transaction amount updated in TransactionsStore', {id, amount})
}
return transactionInstance
@@ -208,7 +199,7 @@ export const TransactionsStoreModel = types
// TODO how to handle missing UI updates for older notes that are stored in db only
if (transactionInstance) {
transactionInstance.noteToSelf = note
log.trace('Transaction note updated in TransactionsStore')
log.debug('[saveNote]', 'Transaction note updated in TransactionsStore', {note})
}
}),
updateSentFrom: flow(function* updateSentFrom(id: number, sentFrom: string) {
@@ -217,7 +208,7 @@ export const TransactionsStoreModel = types
const transactionInstance = self.findById(id)
if (transactionInstance) {
transactionInstance.sentFrom = sentFrom
log.trace('Transaction sentFrom updated in TransactionsStore')
log.debug('[updateSentFrom]', 'Transaction sentFrom updated in TransactionsStore', {id, sentFrom})
}
}),
updateSentTo: flow(function* updateSentTo(id: number, sentTo: string) { //
@@ -226,12 +217,12 @@ export const TransactionsStoreModel = types
const transactionInstance = self.findById(id)
if (transactionInstance) {
transactionInstance.sentTo = sentTo
log.trace('Transaction sentTo updated in TransactionsStore')
log.debug('[updateSentTo]', 'Transaction sentTo updated in TransactionsStore', {id, sentTo})
}
}),
removeAllTransactions() {
self.transactions.clear()
log.info('Removed all transactions from TransactionsStore')
log.debug('[removeAllTransactions]', 'Removed all transactions from TransactionsStore')
},
}))
.views(self => ({
+27 -17
View File
@@ -1,7 +1,7 @@
import {Instance, SnapshotOut, types, flow} from 'mobx-state-tree'
import {Database} from '../services'
import {MMKVStorage} from '../services'
import {log} from '../utils/logger'
import {LogLevel} from '../services/log/logTypes'
export type UserSettings = {
id?: number
@@ -10,6 +10,8 @@ export type UserSettings = {
isStorageEncrypted: boolean | 0 | 1
isLocalBackupOn: boolean | 0 | 1
isTorDaemonOn: boolean | 0 | 1
isLoggerOn: boolean | 0 | 1
logLevel: LogLevel
}
export const UserSettingsStoreModel = types
@@ -20,6 +22,8 @@ export const UserSettingsStoreModel = types
isStorageEncrypted: types.optional(types.boolean, false),
isLocalBackupOn: types.optional(types.boolean, true),
isTorDaemonOn: types.optional(types.boolean, false),
isLoggerOn: types.optional(types.boolean, true),
logLevel: types.optional(types.frozen<LogLevel>(), LogLevel.ERROR)
})
.actions(self => ({
loadUserSettings: () => {
@@ -28,38 +32,38 @@ export const UserSettingsStoreModel = types
isOnboarded,
isStorageEncrypted,
isLocalBackupOn,
isTorDaemonOn
isTorDaemonOn,
isLoggerOn,
logLevel
} = Database.getUserSettings()
const booleanIsOnboarded = isOnboarded === 1
const booleanIsStorageEncrypted = isStorageEncrypted === 1
const booleanIsLocalBackupOn = isLocalBackupOn === 1
const booleanIsTorDaemonOn = isTorDaemonOn === 1
const booleanIsTorDaemonOn = isTorDaemonOn === 1
const booleanIsLoggerOn = isLoggerOn === 1
self.walletId = walletId as string
self.isOnboarded = booleanIsOnboarded as boolean
self.isStorageEncrypted = booleanIsStorageEncrypted as boolean
self.isLocalBackupOn = booleanIsLocalBackupOn as boolean
self.isTorDaemonOn = booleanIsTorDaemonOn as boolean
self.isLoggerOn = booleanIsLoggerOn as boolean
self.logLevel = logLevel as LogLevel
},
setWalletId: (walletId: string) => {
Database.updateUserSettings({...self, walletId})
self.walletId = walletId
log.info('walletId updated', {walletId}, 'setWalletId')
return walletId
},
setIsOnboarded: (isOnboarded: boolean) => {
Database.updateUserSettings({...self, isOnboarded})
self.isOnboarded = isOnboarded
log.info('Onboarded new device', {isOnboarded}, 'setIsOnboarded')
self.isOnboarded = isOnboarded
},
setIsLocalBackupOn: (isLocalBackupOn: boolean) => {
Database.updateUserSettings({...self, isLocalBackupOn})
self.isLocalBackupOn = isLocalBackupOn
log.info('Local backup is turned on', {isLocalBackupOn}, 'setIsLocalBackupOn')
self.isLocalBackupOn = isLocalBackupOn
return isLocalBackupOn
},
setIsStorageEncrypted: flow(function* setIsStorageEncryptedvalue(
@@ -67,18 +71,24 @@ export const UserSettingsStoreModel = types
) {
const isEncrypted = yield MMKVStorage.recryptStorage()
Database.updateUserSettings({...self, isStorageEncrypted: isEncrypted})
self.isStorageEncrypted = isEncrypted
log.info('Storage encryption changed to', {isEncrypted: value}, 'setIsStorageEncrypted')
self.isStorageEncrypted = isEncrypted
return isEncrypted
}),
setIsTorDaemonOn: (isTorDaemonOn: boolean) => {
Database.updateUserSettings({...self, isTorDaemonOn})
self.isTorDaemonOn = isTorDaemonOn
log.info('Tor daemon is turned on', {isTorDaemonOn}, 'setIsTorDaemonOn')
self.isTorDaemonOn = isTorDaemonOn
return isTorDaemonOn
},
setIsLoggerOn: (isLoggerOn: boolean) => {
Database.updateUserSettings({...self, isLoggerOn})
self.isLoggerOn = isLoggerOn
return isLoggerOn
},
setLogLevel: (logLevel: LogLevel) => {
Database.updateUserSettings({...self, logLevel})
self.logLevel = logLevel
return logLevel
},
}))
.views(self => ({
get isUserOnboarded() {
+6 -6
View File
@@ -1,6 +1,6 @@
import {Instance, SnapshotOut, types, flow} from 'mobx-state-tree'
import {MinibitsClient, NostrClient, NostrUnsignedEvent, Wallet} from '../services'
import {log} from '../utils/logger'
import {log} from '../services/logService'
import { getRootStore } from './helpers/getRootStore'
@@ -64,7 +64,7 @@ export const WalletProfileStoreModel = types
const relaysToPublish: string[] = relaysStore.allUrls
log.trace('relaysToPublish', {relaysToPublish},'publishToRelays')
log.debug('[publishToRelays]', 'Publish profile to relays', {profileEvent, relaysToPublish})
const publishedEvent: Event | undefined = yield NostrClient.publish(
profileEvent,
@@ -93,7 +93,7 @@ export const WalletProfileStoreModel = types
const publishedEvent = yield self.publishToRelays()
log.trace('Wallet profile saved in WalletProfileStore', {self, publishedEvent})
log.info('[create]', 'Wallet profile saved in WalletProfileStore', {self, publishedEvent})
return self
}),
updateName: flow(function* update(name: string) {
@@ -109,7 +109,7 @@ export const WalletProfileStoreModel = types
self.nip05 = profileRecord.nip05
const publishedEvent = yield self.publishToRelays()
log.trace('Wallet name updated in the WalletProfileStore', {self, publishedEvent})
log.debug('[updateName]', 'Wallet name updated in the WalletProfileStore', {self, publishedEvent})
return self
}),
updatePicture: flow(function* update(picture: string) {
@@ -125,7 +125,7 @@ export const WalletProfileStoreModel = types
const publishedEvent = yield self.publishToRelays()
log.trace('Wallet picture updated in the WalletProfileStore', {self, publishedEvent})
log.debug('[updatePicture]', 'Wallet picture updated in the WalletProfileStore', {self, publishedEvent})
return self
}),
updateNip05: flow(function* update(newPubkey: string, nip05: string, name: string, picture: string, isOwnProfile: boolean) {
@@ -148,7 +148,7 @@ export const WalletProfileStoreModel = types
// do not publish to relay as this is external
log.trace('Wallet nip05 updated in the WalletProfileStore', {self})
log.info('[updateNip05]', 'Wallet nip05 updated in the WalletProfileStore', {self})
return self
}),
setNip05(nip05: string) { // used in migration to v3 model
+11 -56
View File
@@ -19,12 +19,11 @@ import RNExitApp from 'react-native-exit-app'
import type {RootStore} from '../RootStore'
import {MMKVStorage, NostrClient} from '../../services'
import {Database} from '../../services'
import { log } from '../../utils/logger'
import { log } from '../../services/logService'
import { rootStoreModelVersion } from '../RootStore'
import AppError, { Err } from '../../utils/AppError'
import { MINIBITS_NIP05_DOMAIN, MINIBITS_RELAY_URL } from '@env'
import { WalletProfileStoreModel } from '../WalletProfileStore'
import useIsInternetReachable from '../../utils/useIsInternetReachable'
import { MINIBITS_NIP05_DOMAIN } from '@env'
import { LogLevel } from '../../services/log/logTypes'
/**
* The key we'll be saving our state as within storage.
@@ -87,7 +86,7 @@ export async function setupRootStore(rootStore: RootStore) {
// run migrations if needed, needs to be after onSnapshot to be persisted
try {
log.trace('Device rootStorage version', rootStore.version, 'setupRootStore')
log.debug('[setupRootStore]', `Device rootStore.version is: ${rootStore.version}`)
if(rootStore.version < rootStoreModelVersion) {
await _runMigrations(rootStore)
@@ -149,8 +148,15 @@ async function _runMigrations(rootStore: RootStore) {
log.info(`Completed rootStore migrations to the version v${rootStoreModelVersion}`)
}
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)
} catch (e: any) {
throw new AppError(
Err.STORAGE_ERROR,
@@ -159,54 +165,3 @@ async function _runMigrations(rootStore: RootStore) {
)
}
}
/* interface StoreConfig { storageKey: string, store: any }
let _disposers: IDisposer[] = []
export async function setupRootStore(rootStore: RootStore) {
const storeConfigs: StoreConfig[] = [
{ storageKey: "mintsStore", store: rootStore.mintsStore },
{ storageKey: "transactionsStore", store: rootStore.transactionsStore },
]
const restoredStates: { [key: string]: any } = {}
try {
// retrieve key asynchronously
const encryptionKey = await MMKVStorage.getOrCreateMMKVEncryptionKey()
// initialize or reuse storage with encryption key so we can work in synchronous mode
MMKVStorage.getInstance(encryptionKey)
for (const { storageKey, store } of storeConfigs) {
const restoredState = (MMKVStorage.load(storageKey)) || {}
applySnapshot(store, restoredState)
restoredStates[storageKey] = restoredState
}
} catch (e: any) {
// if there's any problems loading, then inform the dev what happened
if (__DEV__) {
console.error(e.message, null)
}
}
// stop tracking state changes if we've already setup
_disposers.forEach(disposer => disposer)
_disposers = []
// track changes & save to AsyncStorage for each store
for (const { storageKey, store } of storeConfigs) {
const disposer = onSnapshot(store, (snapshot) => MMKVStorage.save(storageKey, snapshot))
_disposers.push(disposer)
}
const unsubscribe = () => {
_disposers.forEach(disposer => disposer())
_disposers = []
}
return { rootStore, restoredStates, unsubscribe }
} */
+3 -3
View File
@@ -23,7 +23,7 @@ import {
MintsScreen,
DeveloperScreen,
SecurityScreen,
// PrivacyScreen,
PrivacyScreen,
UpdateScreen,
BackupScreen,
LocalRecoveryScreen,
@@ -204,7 +204,7 @@ export type SettingsStackParamList = {
Settings: undefined
Mints: {scannedMintUrl? : string}
Security: undefined
// Privacy: undefined
Privacy: undefined
Update: {
isUpdateAvailable : boolean,
isNativeUpdateAvailable: boolean,
@@ -237,7 +237,7 @@ const SettingsNavigator = function SettingsNavigator() {
<SettingsStack.Screen name="Settings" component={SettingsScreen} />
<SettingsStack.Screen name="Mints" component={MintsScreen} />
<SettingsStack.Screen name="Security" component={SecurityScreen} />
{/*<SettingsStack.Screen name="Privacy" component={PrivacyScreen} />*/}
<SettingsStack.Screen name="Privacy" component={PrivacyScreen} />
<SettingsStack.Screen name="Update" component={UpdateScreen} />
<SettingsStack.Screen name="Backup" component={BackupScreen} />
<SettingsStack.Screen name="LocalRecovery" component={LocalRecoveryScreen} />
+1 -1
View File
@@ -6,7 +6,7 @@ import {BottomModal, Button, Card, ErrorModal, Icon, InfoModal, ListItem, Loadin
import {useStores} from '../../models'
import { MinibitsClient} from '../../services'
import AppError, { Err } from '../../utils/AppError'
import {log} from '../../utils/logger'
import {log} from '../../services/logService'
import { TransactionStatus } from '../../models/Transaction'
import { poller, stopPolling } from '../../utils/poller'
import QRCode from 'react-native-qrcode-svg'
+1 -1
View File
@@ -9,7 +9,7 @@ import {ContactsStackParamList} from '../../navigation'
import { MinibitsClient, NostrClient, NostrProfile } from '../../services'
import AppError, { Err } from '../../utils/AppError'
import {MINIBITS_NIP05_DOMAIN} from '@env'
import { log } from '../../utils/logger'
import { log } from '../../services/logService'
import { ContactListItem } from './ContactListItem'
import { Contact, ContactType } from '../../models/Contact'
import { StackNavigationProp } from '@react-navigation/stack'
+1 -1
View File
@@ -8,7 +8,7 @@ import {BottomModal, Button, Card, ErrorModal, Icon, InfoModal, ListItem, Loadin
import {useStores} from '../../models'
import {NostrClient, NostrEvent, NostrFilter, NostrProfile} from '../../services'
import AppError, { Err } from '../../utils/AppError'
import { log } from '../../utils/logger'
import { log } from '../../services/logService'
import { Contact, ContactType } from '../../models/Contact'
import { StackNavigationProp } from '@react-navigation/stack'
import { ContactsStackParamList } from '../../navigation'
+1 -1
View File
@@ -9,7 +9,7 @@ import {ContactsStackScreenProps} from '../navigation'
import { NostrClient, NostrUnsignedEvent } from '../services'
import { PrivateContacts } from './Contacts/PrivateContacts'
import { PublicContacts } from './Contacts/PublicContacts'
import { log } from '../utils/logger'
import { log } from '../services/logService'
import { getImageSource } from '../utils/utils'
import { MINIBITS_NIP05_DOMAIN } from '@env'
import { ReceiveOption } from './ReceiveOptionsScreen'
+110 -25
View File
@@ -4,9 +4,7 @@ import {Alert, TextStyle, View, ViewStyle} from 'react-native'
import {colors, spacing, useThemeColor} from '../theme'
import {SettingsStackScreenProps} from '../navigation'
import {
APP_ENV,
LOG_LEVEL,
SENTRY_ACTIVE,
APP_ENV,
NATIVE_VERSION_ANDROID,
JS_BUNDLE_VERSION,
COMMIT,
@@ -21,6 +19,8 @@ import {
Loading,
ErrorModal,
InfoModal,
BottomModal,
Button,
} from '../components'
import {useHeader} from '../utils/useHeader'
import {useStores} from '../models'
@@ -29,7 +29,7 @@ import AppError from '../utils/AppError'
import {Database, KeyChain, NostrClient} from '../services'
import {MMKVStorage} from '../services'
import {maxTransactionsInModel} from '../models/TransactionsStore'
import { log } from '../utils/logger'
import { LogLevel } from '../services/log/logTypes'
export const DeveloperScreen: FC<SettingsStackScreenProps<'Developer'>> = observer(function DeveloperScreen(_props) {
const {navigation} = _props
@@ -42,6 +42,8 @@ export const DeveloperScreen: FC<SettingsStackScreenProps<'Developer'>> = observ
const [isLoading, setIsLoading] = useState(false)
const [rnVersion, setRnVersion] = useState<string>('')
const [isLogLevelSelectorVisible, setIsLogLevelSelectorVisible] = useState<boolean>(false)
const [selectedLogLevel, setSelectedLogLevel] = useState<LogLevel>(userSettingsStore.logLevel)
const [error, setError] = useState<AppError | undefined>()
const [info, setInfo] = useState('')
@@ -84,6 +86,22 @@ export const DeveloperScreen: FC<SettingsStackScreenProps<'Developer'>> = observ
}
}
const toggleLogLevelSelector = () =>
setIsLogLevelSelectorVisible(previousState => !previousState)
const onLogLevelSelect = function (logLevel: LogLevel) {
try {
const result = userSettingsStore.setLogLevel(logLevel)
setSelectedLogLevel(result)
} catch (e: any) {
handleError(e)
}
}
const factoryReset = async function () {
Alert.alert(
'Confirmation',
@@ -128,6 +146,8 @@ export const DeveloperScreen: FC<SettingsStackScreenProps<'Developer'>> = observ
}
const headerBg = useThemeColor('header')
const iconSelectedColor = useThemeColor('button')
const iconColor = useThemeColor('textDim')
return (
<Screen style={$screen} preset='auto'>
@@ -143,43 +163,36 @@ export const DeveloperScreen: FC<SettingsStackScreenProps<'Developer'>> = observ
style={$card}
HeadingComponent={
<>
<ListItem
tx="developerScreen.logLevel"
subText={userSettingsStore.logLevel.toUpperCase()}
leftIcon='faListUl'
leftIconColor={colors.palette.iconMagenta200}
leftIconInverse={true}
RightComponent={<View style={$rightContainer} />}
style={$item}
bottomSeparator={true}
onPress={toggleLogLevelSelector}
/>
<ListItem
tx="developerScreen.transactions"
subText={translate(
'developerScreen.transactionsDescription',
{count: transactionsStore.count},
)}
leftIcon='faRotate'
leftIconColor={colors.light.tint}
leftIcon='faDownload'
leftIconColor={colors.palette.iconYellow300}
leftIconInverse={true}
RightComponent={<View style={$rightContainer} />}
style={$item}
bottomSeparator={true}
onPress={syncTransactionsFromDb}
/>
<ListItem
tx="developerScreen.info"
subText={`Environment: ${APP_ENV}
Native version: ${NATIVE_VERSION_ANDROID}
JS Bundle version: ${JS_BUNDLE_VERSION}
React Native: ${rnVersion}
Commit: ${COMMIT}
Log level: ${LOG_LEVEL}
Sentry active: ${SENTRY_ACTIVE}
Sentry id: ${userSettingsStore.userSettings.walletId}
`}
leftIcon='faInfoCircle'
leftIconColor={colors.palette.iconGreen300}
leftIconInverse={true}
bottomSeparator={true}
RightComponent={<View style={$rightContainer} />}
style={$item}
/>
<ListItem
text="Show onboarding again"
subText="Go through onboarding screens again next time you restart Minibits."
leftIcon='faRotate'
leftIconColor={colors.palette.secondary300}
leftIconColor={colors.light.tint}
leftIconInverse={true}
RightComponent={<View style={$rightContainer} />}
style={$item}
@@ -189,6 +202,26 @@ Sentry id: ${userSettingsStore.userSettings.walletId}
</>
}
/>
<Card
style={[$card, {marginTop: spacing.medium}]}
HeadingComponent={
<ListItem
tx="developerScreen.info"
subText={`Environment: ${APP_ENV}
Native version: ${NATIVE_VERSION_ANDROID}
JS Bundle version: ${JS_BUNDLE_VERSION}
React Native: ${rnVersion}
Commit: ${COMMIT}
Sentry id: ${userSettingsStore.userSettings.walletId}
`}
leftIcon='faInfoCircle'
leftIconColor={colors.palette.iconGreen300}
leftIconInverse={true}
RightComponent={<View style={$rightContainer} />}
style={$item}
/>
}
/>
<Card
style={[$card, {marginTop: spacing.medium}]}
HeadingComponent={
@@ -205,6 +238,51 @@ Sentry id: ${userSettingsStore.userSettings.walletId}
}
/>
</View>
<BottomModal
isVisible={isLogLevelSelectorVisible ? true : false}
style={{alignItems: 'stretch'}}
ContentComponent={
<>
<ListItem
text={LogLevel.ERROR.toUpperCase()}
subText={'Log errors and crashes only, without private data.'}
leftIcon={selectedLogLevel === LogLevel.ERROR ? 'faCheckCircle' : 'faCircle'}
leftIconColor={selectedLogLevel === LogLevel.ERROR ? iconSelectedColor as string : iconColor as string}
onPress={() => onLogLevelSelect(LogLevel.ERROR)}
style={{paddingHorizontal: spacing.small}}
bottomSeparator={true}
/>
<ListItem
text={LogLevel.INFO.toUpperCase()}
subText={'Log anonymous usage, without private data.'}
leftIcon={selectedLogLevel === LogLevel.INFO ? 'faCheckCircle' : 'faCircle'}
leftIconColor={selectedLogLevel === LogLevel.INFO ? iconSelectedColor as string : iconColor as string}
onPress={() => onLogLevelSelect(LogLevel.INFO)}
style={{paddingHorizontal: spacing.small}}
bottomSeparator={true}
/>
<ListItem
text={LogLevel.DEBUG.toUpperCase()}
subText={'Log details, set only if testing or on request by support.'}
leftIcon={selectedLogLevel === LogLevel.DEBUG ? 'faCheckCircle' : 'faCircle'}
leftIconColor={selectedLogLevel === LogLevel.DEBUG ? iconSelectedColor as string : iconColor as string}
onPress={() => onLogLevelSelect(LogLevel.DEBUG)}
style={{paddingHorizontal: spacing.small}}
bottomSeparator={true}
/>
<View style={$buttonContainer}>
<Button
preset="secondary"
text={'Close'}
onPress={toggleLogLevelSelector}
/>
</View>
</>
}
onBackButtonPress={toggleLogLevelSelector}
onBackdropPress={toggleLogLevelSelector}
/>
{isLoading && <Loading />}
{error && <ErrorModal error={error} />}
{info && <InfoModal message={info} />}
@@ -242,3 +320,10 @@ const $rightContainer: ViewStyle = {
alignSelf: 'center',
marginLeft: spacing.small,
}
const $buttonContainer: ViewStyle = {
flexDirection: 'row',
alignSelf: 'center',
alignItems: 'center',
marginTop: spacing.large,
}
+1 -1
View File
@@ -24,7 +24,7 @@ import {
} from '../components'
import {SettingsStackScreenProps} from '../navigation'
import {useHeader} from '../utils/useHeader'
import {log} from '../utils/logger'
import {log} from '../services/logService'
import {Database} from '../services'
import AppError from '../utils/AppError'
import {BackupProof, Proof} from '../models/Proof'
+1 -1
View File
@@ -20,7 +20,7 @@ import {Mint} from '../models/Mint'
import {useStores} from '../models'
import {useHeader} from '../utils/useHeader'
import {MintKeys, MintKeySets, MintClient} from '../services'
import {log} from '../utils/logger'
import {log} from '../services/logService'
import AppError from '../utils/AppError'
import {translate} from '../i18n'
import {MintListItem} from './Mints/MintListItem'
+1 -1
View File
@@ -10,7 +10,7 @@ import {useHeader} from '../utils/useHeader'
import {useStores} from '../models'
import AppError, { Err } from '../utils/AppError'
import Clipboard from '@react-native-clipboard/clipboard'
import { log } from '../utils/logger'
import { log } from '../services/logService'
import { KeyChain, KeyPair, NostrClient, NostrEvent, NostrFilter, NostrProfile } from '../services'
import { MINIBITS_NIP05_DOMAIN } from '@env'
+45 -9
View File
@@ -2,7 +2,7 @@ import {observer} from 'mobx-react-lite'
import React, {FC, useEffect, useState} from 'react'
import {Switch, TextStyle, View, ViewStyle} from 'react-native'
import {colors, spacing, useThemeColor} from '../theme'
import {SettingsStackScreenProps} from '../navigation' // @demo remove-current-line
import {SettingsStackScreenProps} from '../navigation'
import {
Icon,
ListItem,
@@ -18,8 +18,8 @@ import {useHeader} from '../utils/useHeader'
import {useStores} from '../models'
import AppError from '../utils/AppError'
import {ResultModalInfo} from './Wallet/ResultModalInfo'
import { TorDaemon } from '../services'
import { log } from '../utils/logger'
// import { TorDaemon } from '../services'
import { log } from '../services/logService'
enum TorStatus {
NOTINIT = 'NOTINIT',
@@ -27,7 +27,7 @@ enum TorStatus {
DONE = 'DONE',
}
export const PrivacyScreen: FC<SettingsStackScreenProps<'Security'>> = observer(function PrivacyScreen(_props) {
export const PrivacyScreen: FC<SettingsStackScreenProps<'Privacy'>> = observer(function PrivacyScreen(_props) {
const {navigation} = _props
useHeader({
leftIcon: 'faArrowLeft',
@@ -40,13 +40,16 @@ export const PrivacyScreen: FC<SettingsStackScreenProps<'Security'>> = observer(
const [isTorDaemonOn, setIsTorDaemonOn] = useState<boolean>(
userSettingsStore.isTorDaemonOn,
)
const [isLoggerOn, setIsLoggerOn] = useState<boolean>(
userSettingsStore.isLoggerOn,
)
const [torStatus, setTorStatus] = useState<TorStatus>(TorStatus.NOTINIT)
const [error, setError] = useState<AppError | undefined>()
const [isTorModalVisible, setIsTorModalVisible] = useState<boolean>(false)
const [torResultMessage, setTorResultMessage] = useState<string>()
useEffect(() => {
/* useEffect(() => {
const getTorStatus = async () => {
if(!userSettingsStore.isTorDaemonOn) {
return
@@ -127,7 +130,20 @@ export const PrivacyScreen: FC<SettingsStackScreenProps<'Security'>> = observer(
const toggleTorModal = () =>
setIsTorModalVisible(previousState => !previousState)
*/
const toggleLoggerSwitch = async () => {
try {
const result = userSettingsStore.setIsLoggerOn(
!isLoggerOn,
)
setIsLoggerOn(result)
} catch (e: any) {
handleError(e)
}
}
const handleError = function (e: AppError): void {
setIsLoading(false)
@@ -147,7 +163,7 @@ export const PrivacyScreen: FC<SettingsStackScreenProps<'Security'>> = observer(
style={[$card, {marginTop: spacing.medium}]}
ContentComponent={
<>
<ListItem
{/* <ListItem
tx="privacyScreen.torDaemon"
subTx="privacyScreen.torDaemonDescription"
leftIcon={'faBullseye'}
@@ -193,13 +209,33 @@ export const PrivacyScreen: FC<SettingsStackScreenProps<'Security'>> = observer(
style={$item}
onPress={startTor}
/>
)}
)} */}
<ListItem
tx="privacyScreen.logger"
subTx="privacyScreen.loggerDescription"
leftIcon={'faBug'}
leftIconColor={
isLoggerOn
? colors.palette.angry500
: iconColor as string
}
leftIconInverse={true}
RightComponent={
<View style={$rightContainer}>
<Switch
onValueChange={toggleLoggerSwitch}
value={isLoggerOn}
/>
</View>
}
style={$item}
/>
</>
}
/>
{isLoading && <Loading />}
</View>
<BottomModal
{/*<BottomModal
isVisible={isTorModalVisible ? true : false}
ContentComponent={
<ResultModalInfo
@@ -217,7 +253,7 @@ export const PrivacyScreen: FC<SettingsStackScreenProps<'Security'>> = observer(
}
onBackButtonPress={toggleTorModal}
onBackdropPress={toggleTorModal}
/>
/>*/}
{error && <ErrorModal error={error} />}
{info && <InfoModal message={info} />}
</Screen>
+1 -1
View File
@@ -9,7 +9,7 @@ import {useStores} from '../models'
import AppError from '../utils/AppError'
import { ProfileHeader } from './Contacts/ProfileHeader'
import Clipboard from '@react-native-clipboard/clipboard'
import { log } from '../utils/logger'
import { log } from '../services/logService'
import { KeyChain, MinibitsClient } from '../services'
import { getRandomUsername } from '../utils/usernames'
import { MINIBITS_NIP05_DOMAIN } from '@env'
+1 -1
View File
@@ -16,7 +16,7 @@ import {
ScanIcon,
} from '../components'
import {useHeader} from '../utils/useHeader'
import {log} from '../utils/logger'
import {log} from '../services/logService'
import AppError from '../utils/AppError'
import useIsInternetReachable from '../utils/useIsInternetReachable'
import { infoMessage } from '../utils/utils'
+1 -1
View File
@@ -23,7 +23,7 @@ import {Transaction, TransactionStatus} from '../models/Transaction'
import {useStores} from '../models'
import {useHeader} from '../utils/useHeader'
import {Wallet} from '../services'
import {log} from '../utils/logger'
import {log} from '../services/logService'
import AppError from '../utils/AppError'
import {CashuUtils} from '../services/cashu/cashuUtils'
+1 -1
View File
@@ -10,7 +10,7 @@ import {WalletStackScreenProps} from '../navigation'
import {CameraScreen, CameraType} from 'react-native-camera-kit'
import {spacing, typography} from '../theme'
import {useHeader} from '../utils/useHeader'
import {log} from '../utils/logger'
import {log} from '../services/logService'
import { IncomingDataType, IncomingParser } from '../services/incomingParser'
import AppError from '../utils/AppError'
import { Button, ErrorModal } from '../components'
+1 -1
View File
@@ -20,7 +20,7 @@ import AppError from '../utils/AppError'
import {ResultModalInfo} from './Wallet/ResultModalInfo'
import { KeyChain } from '../services'
import { BIOMETRY_TYPE } from 'react-native-keychain'
import { log } from '../utils/logger'
import { log } from '../services/logService'
export const SecurityScreen: FC<SettingsStackScreenProps<'Security'>> = observer(function SecurityScreen(_props) {
const {navigation} = _props
+1 -1
View File
@@ -18,7 +18,7 @@ import {
ScanIcon,
} from '../components'
import {useHeader} from '../utils/useHeader'
import {log} from '../utils/logger'
import {log} from '../services/logService'
import AppError from '../utils/AppError'
import useIsInternetReachable from '../utils/useIsInternetReachable'
import { infoMessage } from '../utils/utils'
+1 -1
View File
@@ -44,7 +44,7 @@ import {TransactionStatus, Transaction} from '../models/Transaction'
import {useStores} from '../models'
import {useHeader} from '../utils/useHeader'
import {Wallet, NostrClient, NostrProfile, KeyPair, NostrUnsignedEvent} from '../services'
import {log} from '../utils/logger'
import {log} from '../services/logService'
import AppError, {Err} from '../utils/AppError'
import {translate} from '../i18n'
+8 -9
View File
@@ -15,10 +15,10 @@ import {Icon, ListItem, Screen, Text, Card} from '../components'
import {useHeader} from '../utils/useHeader'
import {useStores} from '../models'
import {translate} from '../i18n'
import { Env, log } from '../utils/logger'
import { log } from '../services'
import {Env} from '../utils/envtypes'
import { round } from '../utils/number'
import EventEmitter from '../utils/eventEmitter'
import { Relay } from '../models/Relay'
interface SettingsScreenProps extends SettingsStackScreenProps<'Settings'> {}
@@ -65,12 +65,11 @@ export const SettingsScreen: FC<SettingsScreenProps> = observer(
const gotoSecurity = function() {
navigation.navigate('Security')
}
/*
}
const gotoPrivacy = function() {
navigation.navigate('Privacy')
} */
}
const gotoDevOptions = function() {
navigation.navigate('Developer')
@@ -145,7 +144,7 @@ export const SettingsScreen: FC<SettingsScreenProps> = observer(
bottomSeparator={true}
onPress={gotoSecurity}
/>
{/*<ListItem
<ListItem
tx='settingsScreen.privacy'
leftIcon='faEyeSlash'
leftIconColor={colors.palette.blue200}
@@ -153,7 +152,7 @@ export const SettingsScreen: FC<SettingsScreenProps> = observer(
style={$item}
bottomSeparator={true}
onPress={gotoPrivacy}
/>*/}
/>
<ListItem
tx='settingsScreen.update'
leftIcon='faWandMagicSparkles'
+1 -1
View File
@@ -35,7 +35,7 @@ import {TransactionStatus, Transaction} from '../models/Transaction'
import {useStores} from '../models'
import {useHeader} from '../utils/useHeader'
import {NostrClient, NostrProfile, NostrUnsignedEvent, Wallet} from '../services'
import {log} from '../utils/logger'
import {log} from '../services/logService'
import AppError, { Err } from '../utils/AppError'
import {Mint, MintBalance} from '../models/Mint'
+1 -1
View File
@@ -32,7 +32,7 @@ import {
TransactionType,
} from '../models/Transaction'
import AppError, {Err} from '../utils/AppError'
import {log} from '../utils/logger'
import {log} from '../services/logService'
import {isArray} from 'lodash'
import {Database, Wallet} from '../services'
import {BackupProof, Proof} from '../models/Proof'
+1 -1
View File
@@ -30,7 +30,7 @@ import {Transaction, TransactionStatus} from '../models/Transaction'
import {useStores} from '../models'
import {useHeader} from '../utils/useHeader'
import {MintClient, Wallet} from '../services'
import {log} from '../utils/logger'
import {log} from '../services/logService'
import AppError, {Err} from '../utils/AppError'
import {MintBalance} from '../models/Mint'
import {MintListItem} from './Mints/MintListItem'
+2 -1
View File
@@ -23,7 +23,8 @@ import {
} from '../components'
import {useHeader} from '../utils/useHeader'
import AppError from '../utils/AppError'
import { Env, log } from '../utils/logger'
import { log } from '../services'
import {Env} from '../utils/envtypes'
const deploymentKey = APP_ENV === Env.PROD ? CODEPUSH_PRODUCTION_DEPLOYMENT_KEY : CODEPUSH_STAGING_DEPLOYMENT_KEY
+3 -3
View File
@@ -39,7 +39,8 @@ import EventEmitter from '../utils/eventEmitter'
import {WalletStackScreenProps} from '../navigation'
import {Mint, MintBalance} from '../models/Mint'
import {MintsByHostname} from '../models/MintsStore'
import {Env, log} from '../utils/logger'
import {log} from '../services'
import {Env} from '../utils/envtypes'
import {Transaction, TransactionStatus} from '../models/Transaction'
import {TransactionListItem} from './Transactions/TransactionListItem'
import {MintClient, MintKeys, NostrClient, ReceivedEventResult, Wallet} from '../services'
@@ -81,8 +82,7 @@ export const WalletScreen: FC<WalletScreenProps> = observer(
const [defaultMintUrl, setDefaultMintUrl] = useState<string>('https://mint.minibits.cash/Bitcoin')
const [error, setError] = useState<AppError | undefined>()
const [isLoading, setIsLoading] = useState<boolean>(false)
const [relayStatusList, setRelayStatusList] = useState<RelayStatus[]>([])
const [isUpdateAvailable, setIsUpdateAvailable] = useState<boolean>(false)
const [isUpdateModalVisible, setIsUpdateModalVisible] = useState<boolean>(false)
const [updateDescription, setUpdateDescription] = useState<string>('')
+1 -1
View File
@@ -25,7 +25,7 @@ export * from './OwnKeysScreen'
export * from './SettingsScreen'
export * from './MintsScreen'
export * from './SecurityScreen'
// export * from './PrivacyScreen'
export * from './PrivacyScreen'
export * from './UpdateScreen'
export * from './BackupScreen'
export * from './LocalRecoveryScreen'
+1 -1
View File
@@ -7,7 +7,7 @@ import AppError, {Err} from '../../utils/AppError'
import {Token} from '../../models/Token'
import {TokenEntry} from '../../models/TokenEntry'
import {Proof} from '../../models/Proof'
import { log } from '../../utils/logger'
import { log } from '../logService'
+1 -1
View File
@@ -8,7 +8,7 @@ import {
import { TorDaemon } from '../services'
import {CashuUtils} from './cashu/cashuUtils'
import AppError, {Err} from '../utils/AppError'
import {log} from '../utils/logger'
import {log} from './logService'
import {Token} from '../models/Token'
import {Proof} from '../models/Proof'
+1 -1
View File
@@ -4,7 +4,7 @@ import { getParams, LNURLResponse } from 'js-lnurl'
import { ReceiveOption, SendOption } from '../screens'
import { colors } from '../theme'
import AppError, { Err } from '../utils/AppError'
import { log } from '../utils/logger'
import { log } from './logService'
import { CashuUtils } from './cashu/cashuUtils'
import { MintClient, MintKeys } from './cashuMintClient'
import { LightningUtils } from './lightning/lightningUtils'
+1
View File
@@ -5,4 +5,5 @@ export * from "./keyChain"
export * from "./walletService"
export * from "./minibitsService"
export * from "./nostrService"
export * from "./logService"
// export * from "./torService"
+1 -1
View File
@@ -3,7 +3,7 @@ import AppError, {Err} from '../utils/AppError'
import QuickCrypto from 'react-native-quick-crypto'
import * as nostrTools from 'nostr-tools'
import {btoa, atob, fromByteArray} from 'react-native-quick-base64'
import {log} from '../utils/logger'
import {log} from './logService'
export enum KeyChainServiceName {
MMKV = 'app.minibits.mmkv',
+1 -1
View File
@@ -3,7 +3,7 @@ import {
} from '@cashu/cashu-ts'
import AppError, {Err} from '../../utils/AppError'
import addSeconds from 'date-fns/addSeconds'
import { log } from '../../utils/logger'
import { log } from '../logService'
// TODO refactor all this into own module
+1 -1
View File
@@ -1,7 +1,7 @@
import {decodelnurl} from 'js-lnurl/lib/helpers/decodelnurl'
import AppError, {Err} from '../../utils/AppError'
import { log } from '../../utils/logger'
import { log } from '../logService'
const findEncodedLnurl = function (content: string) {
+1 -1
View File
@@ -1,6 +1,6 @@
import { getParams, LNURLPayParams, LNURLResponse, LNURLWithdrawParams } from 'js-lnurl'
import AppError, { Err } from "../utils/AppError"
import { log } from '../utils/logger'
import { log } from './logService'
import { LnurlUtils } from './lnurl/lnurlUtils'
import { MinibitsClient } from './minibitsService'
+7
View File
@@ -0,0 +1,7 @@
export enum LogLevel {
TRACE = 'trace',
DEBUG = 'debug',
INFO = 'info',
WARN = 'warn',
ERROR = 'error',
}
+78
View File
@@ -0,0 +1,78 @@
import {
logger,
mapConsoleTransport,
transportFunctionType,
} from "react-native-logs"
import {lightFormat} from 'date-fns'
import * as Sentry from '@sentry/react-native'
import {rootStoreInstance} from '../models'
import {Env, LogLevel} from './log/logTypes'
const {
userSettingsStore
} = rootStoreInstance
const customSentryTransport: transportFunctionType = props => {
if (!props.options?.SENTRY) {
throw Error('No sentry instance provided')
}
// Log to Sentry only if user setting is switched on
if (!userSettingsStore.isLoggerOn) {
return true
}
if (props.level.text === 'error') {
// Capture error log
if (props.rawMsg && props.rawMsg.stack && props.rawMsg.message) {
// this is probably a JS error
props.options.SENTRY.captureException(props.rawMsg)
} else {
props.options.SENTRY.captureException(props.msg)
}
return true
}
// Capture log based on user setting for log level
if (userSettingsStore.logLevel === LogLevel.DEBUG && [LogLevel.WARN, LogLevel.INFO, LogLevel.DEBUG].includes(props.level.text as LogLevel)) {
props.options.SENTRY.captureMessage(props.msg)
}
if (userSettingsStore.logLevel === LogLevel.INFO && [LogLevel.WARN, LogLevel.INFO].includes(props.level.text as LogLevel)) {
props.options.SENTRY.captureMessage(props.msg)
}
if (userSettingsStore.logLevel === LogLevel.WARN && props.level.text === LogLevel.WARN) {
props.options.SENTRY.captureMessage(props.msg)
}
return true
}
const log = logger.createLogger<LogLevel.TRACE | LogLevel.DEBUG | LogLevel.INFO | LogLevel.WARN | LogLevel.ERROR>({
severity: __DEV__ ? LogLevel.TRACE : userSettingsStore.logLevel,
levels: {
trace: 0,
debug: 1,
info: 2,
warn: 3,
error: 4,
},
transport: __DEV__ ? mapConsoleTransport : customSentryTransport,
transportOptions: {
SENTRY: Sentry,
mapLevels: {
trace: "log",
debug: "debug",
info: "info",
warn: "warn",
error: "error",
}
},
dateFormat: (date: Date) => {
return `${lightFormat(new Date(), 'HH:mm:ss:SSS')} | `
},
})
export {log}
+1 -1
View File
@@ -1,5 +1,5 @@
import AppError, { Err } from "../utils/AppError"
import { Env, log } from "../utils/logger"
import { Env, log } from "./logService"
import {
MINIBITS_SERVER_API_KEY,
MINIBITS_SERVER_API_HOST,
+3 -3
View File
@@ -1,5 +1,5 @@
import {MMKV} from 'react-native-mmkv'
import {log} from '../utils/logger'
import {log} from './logService'
import AppError, {Err} from '../utils/AppError'
import {KeyChain} from './keyChain'
@@ -21,13 +21,13 @@ const getInstance = function () {
encryptionKey: _encryptionKey,
})
log.trace('MMKV encrypted storage initialized')
log.trace('[getInstance]', 'MMKV encrypted storage initialized')
} else {
_storage = new MMKV({
id: 'storage-v1',
})
log.trace('MMKV storage initialized')
log.trace('[getInstance]', 'MMKV storage initialized')
}
}
+1 -1
View File
@@ -15,7 +15,7 @@ import {
MINIBITS_RELAY_URL,
} from '@env'
import {KeyChain, KeyPair} from './keyChain'
import {log} from '../utils/logger'
import {log} from './logService'
import AppError, { Err } from '../utils/AppError'
import { MinibitsClient } from './minibitsService'
+1 -1
View File
@@ -1,6 +1,6 @@
import notifee, { AndroidImportance } from '@notifee/react-native'
import { colors } from '../theme';
import { log } from '../utils/logger';
import { log } from './logService';
const createLocalNotification = async function (title: string, body: string, largeIcon?: string) {
+42 -38
View File
@@ -12,14 +12,15 @@ import {
import {getRandomUsername} from '../utils/usernames'
import {UserSettings} from '../models/UserSettingsStore'
import AppError, {Err} from '../utils/AppError'
import {log} from '../utils/logger'
import {log} from './logService'
import {LogLevel} from './log/logTypes'
import {BackupProof} from '../models/Proof'
import { CashuUtils } from './cashu/cashuUtils'
import { Contact, ContactType } from '../models/Contact'
let _db: QuickSQLiteConnection
const _dbVersion = 6 // Update this if db changes require migrations
const _dbVersion = 7 // Update this if db changes require migrations
const getInstance = function () {
if (!_db) {
@@ -74,6 +75,8 @@ const _createOrUpdateSchema = function (db: QuickSQLiteConnection) {
isStorageEncrypted BOOLEAN,
isLocalBackupOn BOOLEAN,
isTorDaemonOn BOOLEAN,
isLoggerOn BOOLEAN,
logLevel TEXT,
createdAt TEXT
)`,
],
@@ -106,7 +109,7 @@ const _createOrUpdateSchema = function (db: QuickSQLiteConnection) {
}
const {version} = getDatabaseVersion()
log.info('Device database version:', version)
log.info('[_createOrUpdateSchema]', `Device database version: ${version}`)
// Trigger migrations if there is versions mismatch
if (version < _dbVersion) {
@@ -177,6 +180,17 @@ const _runMigrations = function (db: QuickSQLiteConnection) {
log.info(`Prepared database migrations from ${currentVersion} -> 6`)
}
if (currentVersion < 7) {
migrationQueries.push([
`ALTER TABLE usersettings
ADD COLUMN isLoggerOn BOOLEAN`,
],[
`ALTER TABLE usersettings
ADD COLUMN logLevel TEXT`,
])
log.info(`Prepared database migrations from ${currentVersion} -> 6`)
}
// Update db version as a part of migration sqls
migrationQueries.push([
@@ -210,7 +224,7 @@ const _generateWalletId = (): string => {
const base64Key = btoa(stringKey)*/
const walletId = getRandomUsername()
log.info('New walletId created:', walletId)
log.debug('[_generateWalletId]', 'New walletId created:', walletId)
return walletId
} catch (e: any) {
@@ -235,7 +249,7 @@ const cleanAll = function () {
const {rowsAffected} = db.executeBatch(dropQueries)
if (rowsAffected && rowsAffected > 0) {
log.info('Database tables were deleted')
log.info('[cleanAll]', 'Database tables were deleted')
}
} catch (e: any) {
throw new AppError(
@@ -299,8 +313,10 @@ const getUserSettings = function (): UserSettings {
isStorageEncrypted: 0,
isLocalBackupOn: 1,
isTorDaemonOn: 0,
isLoggerOn: 1,
logLevel: LogLevel.ERROR
})
log.trace('Stored default user settings in the database')
log.debug('[getUserSettings]', 'Stored default user settings in the database')
return defaultSettings
}
@@ -317,11 +333,11 @@ const getUserSettings = function (): UserSettings {
const updateUserSettings = function (settings: UserSettings): UserSettings {
try {
const now = new Date()
const {walletId, isOnboarded, isStorageEncrypted, isLocalBackupOn, isTorDaemonOn} = settings
const {walletId, isOnboarded, isStorageEncrypted, isLocalBackupOn, isTorDaemonOn, isLoggerOn, logLevel} = settings
const query = `
INSERT OR REPLACE INTO usersettings (id, walletId, isOnboarded, isStorageEncrypted, isLocalBackupOn, isTorDaemonOn, createdAt)
VALUES (?, ?, ?, ?, ?, ?, ?)
INSERT OR REPLACE INTO usersettings (id, walletId, isOnboarded, isStorageEncrypted, isLocalBackupOn, isTorDaemonOn, isLoggerOn, logLevel, createdAt)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`
const params = [
1,
@@ -330,13 +346,15 @@ const updateUserSettings = function (settings: UserSettings): UserSettings {
isStorageEncrypted,
isLocalBackupOn,
isTorDaemonOn,
isLoggerOn,
logLevel,
now.toISOString(),
]
const db = getInstance()
db.execute(query, params)
log.trace('User settings created or updated in the database', params)
log.debug('[updateUserSettings]', 'User settings created or updated in the database', params)
const updated = getUserSettings()
return updated
@@ -384,11 +402,7 @@ const addTransactionAsync = async function (tx: Transaction) {
const db = getInstance()
const result = await db.executeAsync(query, params)
log.info(
'New transaction added to the database',
{id: result.insertId, type, mint, status},
'addTransactionAsync',
)
log.info('[addTransactionAsync]', 'New transaction added to the database', {type, mint, status})
const newTx = getTransactionById(result.insertId as number)
@@ -418,11 +432,7 @@ const updateStatusAsync = async function (
const db = getInstance()
await db.executeAsync(query, params)
log.info(
`[${status}] Transaction status updated`,
{id, status},
'updateStatusAsync',
)
log.info('[updateStatusAsync]', `[${status}] Transaction status updated`, {id})
const updatedTx = getTransactionById(id as number)
@@ -477,11 +487,7 @@ const updateStatusesAsync = async function (
const result2 = await _db.executeAsync(updateQuery, params)
log.info(
`[${status}] Transactions statuses updated.`,
{numUpdates: result2.rowsAffected, status},
'updateStatusesAsync',
)
log.info('[updateStatusAsync]', `[${status}] Transactions statuses updated.`, {numUpdates: result2.rowsAffected, status})
return result2
} catch (e: any) {
@@ -506,10 +512,9 @@ const updateBalanceAfterAsync = async function (
const params = [balanceAfter, id]
const db = getInstance()
await db.executeAsync(query, params)
await db.executeAsync(query, params)
// DO NOT log balance to Sentry
log.trace('Transaction balanceAfter updated')
log.debug('[updateBalanceAfterAsync]', 'Transaction balanceAfter updated', {id, balanceAfter})
const updatedTx = getTransactionById(id as number)
@@ -535,7 +540,7 @@ const updateFeeAsync = async function (id: number, fee: number) {
const db = getInstance()
await db.executeAsync(query, params)
log.info('Transaction fee updated', {id, fee}, 'updateFeeAsync')
log.debug('[updateFeeAsync]', 'Transaction fee updated', {id, fee})
const updatedTx = getTransactionById(id as number)
@@ -561,7 +566,7 @@ const updateReceivedAmountAsync = async function (id: number, amount: number) {
const db = getInstance()
await db.executeAsync(query, params)
log.info('Transaction received amount updated', {id}, 'updateReceivedAmountAsync')
log.debug('[updateReceivedAmountAsync]', 'Transaction received amount updated', {id})
const updatedTx = getTransactionById(id as number)
@@ -587,7 +592,7 @@ const updateNoteAsync = async function (id: number, note: string) {
const db = getInstance()
await db.executeAsync(query, params)
// DO NOT log to Sentry
log.trace('Transaction note updated')
log.debug('[updateNoteAsync]', 'Transaction note updated')
const updatedTx = getTransactionById(id as number)
@@ -614,7 +619,7 @@ const updateSentFromAsync = async function (id: number, sentFrom: string) {
const db = getInstance()
await db.executeAsync(query, params)
log.trace('Transaction sentFrom updated', {sentFrom}, 'updateSentFromAsync')
log.debug('[updateSentFromAsync]', 'Transaction sentFrom updated', {id, sentFrom})
const updatedTx = getTransactionById(id as number)
@@ -641,7 +646,7 @@ const updateSentToAsync = async function (id: number, sentTo: string) {
const db = getInstance()
await db.executeAsync(query, params)
log.trace('Transaction sentTo updated', {sentTo}, 'updateSentToAsync')
log.debug('[updateSentToAsync]', 'Transaction sentFrom updated', {id, sentTo})
const updatedTx = getTransactionById(id as number)
@@ -748,9 +753,8 @@ const addOrUpdateProof = function (
const db = getInstance()
const result = db.execute(query, params)
// DO NOT log proof secrets to Sentry
log.info(
`${isPending ? ' Pending' : ''} proof added or updated in the database backup`,
[{id: result.insertId, tId: proof.tId, isPending, isSpent}],
log.info('[addOrUpdateProof]', `${isPending ? ' Pending' : ''} proof added or updated in the database backup`,
{id: result.insertId, tId: proof.tId, isPending, isSpent},
)
const newProof = getProofById(result.insertId as number)
@@ -799,7 +803,7 @@ const addOrUpdateProofs = function (
const totalAmount = CashuUtils.getProofsAmount(proofs)
// DO NOT log proof secrets to Sentry
log.trace(
log.info('[addOrUpdateProofs]',
`${rowsAffected}${isPending ? ' pending' : ''
} proofs were added or updated in the database backup`,
{isPending, isSpent}
@@ -823,7 +827,7 @@ const removeAllProofs = async function () {
const db = getInstance()
db.execute(query)
log.info('All proofs were removed from the database.')
log.info('[removeAllProofs]', 'All proofs were removed from the database.')
return true
} catch (e: any) {
+40 -60
View File
@@ -1,7 +1,7 @@
import {deriveKeysetId, getEncodedToken} from '@cashu/cashu-ts'
import {addSeconds, isBefore} from 'date-fns'
import {getSnapshot, isStateTreeNode} from 'mobx-state-tree'
import {log} from '../utils/logger'
import {log} from './logService'
import {MintClient, MintKeys, MintKeySets} from './cashuMintClient'
import {Proof} from '../models/Proof'
import {
@@ -137,7 +137,7 @@ const checkPendingReceived = async function () {
contactsStore.setLastPendingReceivedCheck()
log.trace('Creating subscription...', filter, 'checkPendingReceived')
log.trace('[checkPendingReceived]', 'Creating Nostr subscription...', filter)
const pool = NostrClient.getRelayPool()
let relaysToConnect = relaysStore.allUrls
@@ -407,10 +407,7 @@ async function _checkSpentByMint(mintUrl: string, isPending: boolean = false) {
const proofsFromMint = proofsStore.getByMint(mintUrl, isPending) as Proof[]
if (proofsFromMint.length < 1) {
log.trace(
`No ${isPending ? 'pending' : ''} proofs found for mint`,
mintUrl, '_checkSpentByMint'
)
log.trace('[_checkSpentByMint]', `No ${isPending ? 'pending' : ''} proofs found for mint`, mintUrl)
return
}
@@ -422,14 +419,10 @@ async function _checkSpentByMint(mintUrl: string, isPending: boolean = false) {
const spentCount = spentProofs.length
const spentAmount = CashuUtils.getProofsAmount(spentProofs)
log.trace('spentProofs amount', spentAmount, '_checkSpentByMint')
log.trace('[_checkSpentByMint]', `spentProofs amount: ${spentAmount}`)
if (spentProofs.length < 1) {
log.trace(
`No spent ${isPending ? 'pending' : ''} proofs returned from the mint`,
mintUrl,
'_checkSpentByMint',
)
log.trace('[_checkSpentByMint]', `No spent ${isPending ? 'pending' : ''} proofs returned from the mint`, mintUrl)
return
}
@@ -450,11 +443,7 @@ async function _checkSpentByMint(mintUrl: string, isPending: boolean = false) {
proofsStore.removeProofs(spentProofs as Proof[], isPending)
// Update related transactions statuses
log.info(
'Transaction id(s) to complete',
relatedTransactionIds.toString(),
'_checkSpentByMint',
)
log.debug('[_checkSpentByMint]', 'Transaction id(s) to complete', relatedTransactionIds.toString())
// Complete related transactions in normal wallet operations
if (isPending) {
@@ -478,7 +467,7 @@ async function _checkSpentByMint(mintUrl: string, isPending: boolean = false) {
} catch (e: any) {
// silent
log.info(e.name, {message: e.message, mintUrl}, '_checkSpentByMint')
log.error('[_checkSpentByMint]', e.name, {message: e.message, mintUrl})
}
}
@@ -493,7 +482,7 @@ const receive = async function (
try {
const tokenMints: string[] = CashuUtils.getMintsFromToken(token as Token)
log.trace('receiveToken tokenMints', tokenMints, 'receive')
log.trace('[receive]', 'receiveToken tokenMints', tokenMints)
if (tokenMints.length === 0) {
throw new AppError(
@@ -549,10 +538,7 @@ const receive = async function (
// Handle missing mint, we add it automatically
const alreadyExists = mintsStore.alreadyExists(mintToReceive)
if (!alreadyExists) {
log.trace('Adding new mint', mintToReceive, 'receive')
if (!alreadyExists) {
const mintKeys: {
keys: MintKeys
keyset: string
@@ -593,7 +579,7 @@ const receive = async function (
if (errorToken && errorToken.token.length > 0) {
amountWithErrors += CashuUtils.getTokenAmounts(errorToken as Token).totalAmount
log.trace('receiveToken amountWithErrors', amountWithErrors, 'receive')
log.warn('[receive]', 'receiveToken amountWithErrors', amountWithErrors)
}
if (amountWithErrors === amountToReceive) {
@@ -619,7 +605,7 @@ const receive = async function (
// This should be amountToReceive - amountWithErrors but let's set it from updated token
const receivedAmount = CashuUtils.getTokenAmounts(updatedToken as Token).totalAmount
log.trace('Received amount', receivedAmount, 'receive')
log.debug('[receive]', `Received amount: ${receivedAmount}`)
// Update tx amount if full amount was not received
if (receivedAmount !== amountToReceive) {
@@ -698,7 +684,7 @@ const receiveOfflinePrepare = async function (
try {
const tokenMints: string[] = CashuUtils.getMintsFromToken(token as Token)
log.trace('receiveToken tokenMints', tokenMints, 'receive')
log.trace('[receiveOfflinePrepare]', 'receiveToken tokenMints', tokenMints)
if (tokenMints.length === 0) {
throw new AppError(
@@ -820,7 +806,7 @@ const receiveOfflineComplete = async function (
)
if (!encodedToken) {
throw new AppError(Err.VALIDATION_ERROR, 'Could not find ecash token to redeem', 'receiveOfflineComplete')
throw new AppError(Err.VALIDATION_ERROR, 'Could not find ecash token to redeem', {caller: 'receiveOfflineComplete'})
}
const token = CashuUtils.decodeToken(encodedToken)
@@ -846,10 +832,7 @@ const receiveOfflineComplete = async function (
// Handle missing mint, we add it automatically
const alreadyExists = mintsStore.alreadyExists(mintToReceive)
if (!alreadyExists) {
log.trace('Adding new mint', mintToReceive, 'receive')
if (!alreadyExists) {
const mintKeys: {
keys: MintKeys
keyset: string
@@ -875,7 +858,7 @@ const receiveOfflineComplete = async function (
if (errorToken && errorToken.token.length > 0) {
amountWithErrors += CashuUtils.getTokenAmounts(errorToken as Token).totalAmount
log.trace('receiveToken amountWithErrors', amountWithErrors, 'receive')
log.warn('[receiveOfflineComplete]', 'receiveToken amountWithErrors', amountWithErrors)
}
if (amountWithErrors === transaction.amount) {
@@ -901,7 +884,7 @@ const receiveOfflineComplete = async function (
// This should be amountToReceive - amountWithErrors but let's set it from updated token
const receivedAmount = CashuUtils.getTokenAmounts(updatedToken as Token).totalAmount
log.trace('Received amount', receivedAmount, 'receive')
log.debug('[receiveOfflineComplete]', 'Received amount', receivedAmount)
// Update tx amount if full amount was not received
if (receivedAmount !== transaction.amount) {
@@ -978,7 +961,7 @@ const _sendFromMint = async function (
try {
const proofsFromMint = proofsStore.getByMint(mintUrl) as Proof[]
log.trace('proofsFromMint', proofsFromMint.length, '_sendFromMint')
log.debug('[_sendFromMint]', 'proofsFromMint count', proofsFromMint.length)
if (proofsFromMint.length < 1) {
throw new AppError(
@@ -1035,7 +1018,7 @@ const _sendFromMint = async function (
amountToSend,
proofsFromMint,
)
log.trace('proofsToSendFrom', proofsToSendFrom, '_sendFromMint')
log.debug('[_sendFromMint]', 'proofsToSendFrom', proofsToSendFrom)
// if split to required denominations was necessary, this gets it done with the mint and we get the return
const {returnedProofs, proofsToSend, newKeys} = await MintClient.sendFromMint(
@@ -1044,8 +1027,8 @@ const _sendFromMint = async function (
proofsToSendFrom,
)
log.trace('returnedProofs', returnedProofs, '_sendFromMint')
log.trace('proofsToSend', proofsToSend, '_sendFromMint')
log.debug('[_sendFromMint]', 'returnedProofs', returnedProofs)
log.debug('[_sendFromMint]', 'proofsToSend', proofsToSend)
if (newKeys) {_updateMintKeys(mintUrl, newKeys)}
@@ -1113,9 +1096,9 @@ const send = async function (
const mintUrl = mintBalanceToSendFrom.mint
log.trace('mintBalanceToSendFrom', mintBalanceToSendFrom, 'send')
log.trace('amountToSend', amountToSend, 'send')
log.trace('memo', memo, 'send')
log.trace('[send]', 'mintBalanceToSendFrom', mintBalanceToSendFrom)
log.trace('[send]', 'amountToSend', amountToSend)
log.trace('[send]', 'memo', memo)
// create draft transaction
const transactionData: TransactionData[] = [
@@ -1206,13 +1189,12 @@ const send = async function (
20,
5,
)
.then(() => log.trace('poller completed', [], '_checkSpentByMint'))
.then(() => log.trace('[checkSpentByMintPoller]', 'polling completed'))
.catch(error =>
log.error(
Err.POLLING_ERROR,
'polling error:',
error,
'_checkSpentByMint',
error.message,
{caller: '_checkSpentByMint'},
),
)
@@ -1257,9 +1239,9 @@ const transfer = async function (
) {
const mintUrl = mintBalanceToTransferFrom.mint
log.trace('mintBalanceToTransferFrom', mintBalanceToTransferFrom, 'transfer')
log.trace('amountToTransfer', amountToTransfer, 'transfer')
log.trace('estimatedFee', estimatedFee, 'transfer')
log.debug('[transfer]', 'mintBalanceToTransferFrom', mintBalanceToTransferFrom)
log.debug('[transfer]', 'amountToTransfer', amountToTransfer)
log.debug('[transfer]', 'estimatedFee', estimatedFee)
if (amountToTransfer + estimatedFee > mintBalanceToTransferFrom.balance) {
throw new AppError(Err.VALIDATION_ERROR, 'Mint balance is insufficient to cover the amount to transfer with expected Lightning fees.')
@@ -1311,7 +1293,7 @@ const transfer = async function (
)
const proofsAmount = CashuUtils.getProofsAmount(proofsToPay as Proof[])
log.info('Prepared poofsToPay amount', proofsAmount, 'transfer')
log.debug('[transfer]', 'Prepared poofsToPay amount', proofsAmount)
// Update transaction status
transactionData.push({
@@ -1388,9 +1370,7 @@ const transfer = async function (
const balanceAfter = proofsStore.getBalances().totalBalance
await transactionsStore.updateBalanceAfter(transactionId, balanceAfter)
log.trace('totalBalance after', balanceAfter, 'transfer')
await transactionsStore.updateBalanceAfter(transactionId, balanceAfter)
return {
transaction: completedTransaction,
@@ -1415,7 +1395,7 @@ const transfer = async function (
// Return tokens intended for payment to the wallet if payment failed with an error
if (proofsToPay.length > 0) {
log.info('Returning proofsToPay to the wallet likely after failed lightning payment', proofsToPay.length, 'transfer')
log.info('[transfer]', 'Returning proofsToPay to the wallet likely after failed lightning payment', proofsToPay.length)
const amount = _addCashuProofs(proofsToPay, mintUrl, transactionId, true)
}
}
@@ -1446,7 +1426,7 @@ const _addCashuProofs = function (
proofsStore.addProofs(proofsToAdd as Proof[])
const amount = CashuUtils.getProofsAmount(proofsToAdd as Proof[])
log.trace('Added proofs with amount', { amount, isRecoveredFromPending })
log.trace('[_addCashuProofs]', 'Added proofs with amount', { amount, isRecoveredFromPending })
if(isRecoveredFromPending) {
// Remove them from pending if they are returned to the wallet due to failed lightning payment
@@ -1464,8 +1444,8 @@ const topup = async function (
amountToTopup: number,
memo: string,
) {
log.info('mintBalanceToTopup', mintBalanceToTopup, 'topup')
log.info('amountToTopup', amountToTopup, 'topup')
log.info('[topup]', 'mintBalanceToTopup', mintBalanceToTopup)
log.info('[topup]', 'amountToTopup', amountToTopup)
// create draft transaction
const transactionData: TransactionData[] = [
@@ -1534,7 +1514,7 @@ const topup = async function (
poller('checkPendingTopupsPoller', checkPendingTopups, 6 * 1000, 20, 5)
.then(() => log.trace('Polling completed', [], 'checkPendingTopups'))
.catch(error =>
log.trace(error.message, [], 'checkPendingTopups'),
log.warn(error.message, [], 'checkPendingTopups'),
)
return {
@@ -1574,7 +1554,7 @@ const checkPendingTopups = async function () {
const invoices: Invoice[] = invoicesStore.allInvoices
if (invoices.length === 0) {
log.trace('No invoices in store', [], 'checkPendingTopups')
log.trace('[checkPendingTopups]', 'No invoices in store')
return
}
@@ -1588,11 +1568,11 @@ const checkPendingTopups = async function () {
)) as {proofs: Proof[], newKeys: MintKeys}
if (!proofs || proofs.length === 0) {
log.trace('No proofs returned from mint', [], 'checkPendingTopups')
log.trace('[checkPendingTopups]', 'No proofs returned from mint')
// remove already expired invoices only after check that they have not been paid
// Fixes #3
if (isBefore(invoice.expiresAt as Date, new Date())) {
log.trace('Invoice expired, removing', invoice.paymentHash, 'checkPendingTopups')
log.debug('[checkPendingTopups]', `Invoice expired, removing: ${invoice.paymentHash}`)
const transactionId = invoice.transactionId
invoicesStore.removeInvoice(invoice)
@@ -1671,7 +1651,7 @@ const checkPendingTopups = async function () {
const _updateMintKeys = function (mintUrl: string, newKeys: MintKeys) {
if(!CashuUtils.validateMintKeys(newKeys)) {
// silent
log.info('Invalid mint keys to update, skipping', newKeys)
log.warn('[_updateMintKeys]', 'Invalid mint keys to update, skipping', newKeys)
return
}
+2 -7
View File
@@ -1,7 +1,7 @@
import * as Sentry from '@sentry/react-native'
import {SENTRY_ACTIVE} from '@env'
import {log, SentryActive} from './logger'
import {log, SentryActive} from '../services/logService'
export enum Err {
CONNECTION_ERROR = 'CONNECTION_ERROR',
@@ -52,12 +52,7 @@ class AppError extends Error {
}
}
log.error(name, message, params, callerFunctionName)
// Sentry trial
if (!__DEV__ && SENTRY_ACTIVE === SentryActive.TRUE) {
Sentry.captureException(error)
}
log.error(`[${callerFunctionName}]`, name, message, params)
Object.setPrototypeOf(this, new.target.prototype)
}
+2 -6
View File
@@ -1,7 +1,5 @@
declare module '@env' {
export const APP_ENV: string
export const LOG_LEVEL: string
export const SENTRY_ACTIVE: string
export const APP_ENV: string
export const SENTRY_DSN: string
export const CODEPUSH_PRODUCTION_DEPLOYMENT_KEY: string
export const CODEPUSH_STAGING_DEPLOYMENT_KEY: string
@@ -11,9 +9,7 @@ declare module '@env' {
export const MINIBITS_NIP05_DOMAIN: string
export const MINIBITS_RELAY_URL: string
export const JS_BUNDLE_VERSION: string
export const COMMIT: string
export const COMMIT: string
}
+5
View File
@@ -0,0 +1,5 @@
export enum Env {
DEV = 'DEV',
TEST = 'TEST',
PROD = 'PROD',
}
-135
View File
@@ -1,135 +0,0 @@
import {lightFormat} from 'date-fns'
import * as Sentry from '@sentry/react-native'
import {Err} from './AppError'
import {APP_ENV, LOG_LEVEL, SENTRY_ACTIVE} from '@env'
// refresh // refresh // refresh
export enum LogLevel {
TRACE = 'TRACE',
DEBUG = 'DEBUG',
INFO = 'INFO',
ERROR = 'ERROR',
}
export enum Env {
DEV = 'DEV',
TEST = 'TEST',
PROD = 'PROD',
}
export enum SentryActive {
TRUE = 'TRUE',
FALSE = 'FALSE',
}
const _showTimestamps: boolean = true
const _logLevel = LOG_LEVEL as LogLevel
const trace = function (message: any, params: any = {}, caller: string = '') {
if (APP_ENV === Env.PROD) return
let callerFunctionName = ''
const error = new Error()
const stackTrace = error.stack?.split('\n')
if (stackTrace) {
const callerLine = stackTrace[2]
callerFunctionName =
callerLine !== null ? callerLine.match(/at\s+(.*)\s+\(/)[1] : ''
if(callerFunctionName === '?anon_0_' || callerFunctionName === 'anonymous') {
callerFunctionName = caller
}
}
if (_logLevel === LogLevel.TRACE) {
if (_showTimestamps) {
const t = lightFormat(new Date(), 'HH:mm:ss:SSS')
console.log(`[TRACE] ${t} [${callerFunctionName}]`, message, params)
} else {
console.log(`[TRACE] [${callerFunctionName}]`, message, params)
}
}
}
const debug = function (
message: string,
params: any = {},
caller: string = '',
) {
if (APP_ENV === Env.PROD) return
let callerFunctionName = ''
const error = new Error()
const stackTrace = error.stack?.split('\n')
if (stackTrace) {
const callerLine = stackTrace[2]
callerFunctionName = callerLine !== null ? callerLine.match(/at\s+(.*)\s+\(/)[1] : ''
if(callerFunctionName === '?anon_0_' || callerFunctionName === 'anonymous') {
callerFunctionName = caller
}
}
if ([LogLevel.TRACE, LogLevel.DEBUG].includes(_logLevel)) {
if (_showTimestamps) {
const t = lightFormat(new Date(), 'HH:mm:ss:SSS')
console.log(`[DEBUG] ${t} [${callerFunctionName}]`, message, params)
} else {
console.log(`[DEBUG] [${callerFunctionName}]`, message, params)
}
}
}
const info = function (message: string, params: any = {}, caller: string = '') {
let callerFunctionName = ''
const error = new Error()
const stackTrace = error.stack?.split('\n')
if (stackTrace) {
const callerLine = stackTrace[2]
callerFunctionName =
callerLine !== null ? callerLine.match(/at\s+(.*)\s+\(/)[1] : ''
if( callerFunctionName === '?anon_0_' || callerFunctionName === 'anonymous') {
callerFunctionName = caller
}
}
if ([LogLevel.TRACE, LogLevel.DEBUG, LogLevel.INFO].includes(_logLevel)) {
if (_showTimestamps) {
const t = lightFormat(new Date(), 'HH:mm:ss:SSS')
console.log(`[INFO] ${t} [${callerFunctionName}]`, message, params)
} else {
console.log(`[INFO] [${callerFunctionName}]`, message, params)
}
if (!__DEV__ && SENTRY_ACTIVE === SentryActive.TRUE) {
Sentry.captureMessage(message, params)
}
}
}
const error = function (
name: Err,
message: string,
params: any = {},
caller: string = '',
) {
if (_showTimestamps) {
const t = lightFormat(new Date(), 'HH:mm:ss:SSS')
console.error(`[${name}] ${t} [${caller}]`, message, params)
} else {
console.error(`[${name}] [${caller}]`, message, params)
}
}
export const log = {
trace,
debug,
info,
error
}
+1 -1
View File
@@ -1,4 +1,4 @@
import { log } from './logger'
import { log } from '../services/logService'
import { Err } from './AppError'
const pollers = new Map<string, boolean>() // Map to track active pollers
+1 -1
View File
@@ -1,6 +1,6 @@
import { useEffect, useState } from 'react'
import NetInfo, { NetInfoState } from '@react-native-community/netinfo'
import {log} from './logger'
import {log} from '../services/logService'
export default function useIsInternetReachable() {
+5
View File
@@ -6993,6 +6993,11 @@ react-native-keychain@^8.1.1:
resolved "https://registry.yarnpkg.com/react-native-keychain/-/react-native-keychain-8.1.1.tgz#3bb5e37946b964a7bcf7df2fe470dd244e01a340"
integrity sha512-8fxgeHKwGcL657eAYpdBTkDIxNhbIHI+kyyO0Yac2dgVAN184JoIwQcW2z6snahwDaCObQOu0biLFHnsH+4KSg==
react-native-logs@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/react-native-logs/-/react-native-logs-5.0.1.tgz#53665919587b499e22f514e50cb6a834cedba1ce"
integrity sha512-30aQugeLFfcBEKd6U4I75SKc5q5fb9Qrwv0Vnsy3jwpC716xON9SxRbBEnxoPSDJjzK527r4zi+qYqzig6lPlQ==
react-native-mmkv@^2.8.0:
version "2.10.1"
resolved "https://registry.yarnpkg.com/react-native-mmkv/-/react-native-mmkv-2.10.1.tgz#1a4e64edbf9561fe81a5ae3cefde17e88dfc669f"