From 832e22e465ecead91ca100c682cb0b323c06667b Mon Sep 17 00:00:00 2001 From: minibits-cash Date: Fri, 3 May 2024 01:47:06 +0200 Subject: [PATCH] PreferredUnit impl, fixing --- src/components/Card.tsx | 4 +-- src/i18n/en.ts | 1 + src/models/UserSettingsStore.ts | 19 ++++++++--- src/models/helpers/setupRootStore.ts | 2 ++ src/navigation/TabsNavigator.tsx | 2 -- src/screens/ContactDetailScreen.tsx | 9 +++-- src/screens/Contacts/PrivateContacts.tsx | 4 +-- src/screens/Contacts/PublicContacts.tsx | 4 +-- src/screens/LightningPayScreen.tsx | 6 ++-- src/screens/ScanScreen.tsx | 18 ++++++---- src/screens/SendScreen.tsx | 3 +- src/screens/SettingsScreen.tsx | 24 ++++++++++++-- src/screens/TopupScreen.tsx | 3 +- src/screens/TranDetailScreen.tsx | 8 ++--- src/screens/TranHistoryScreen.tsx | 8 ++--- .../Transactions/TransactionListItem.tsx | 2 +- src/screens/WalletScreen.tsx | 33 ++++++++++++++----- src/services/incomingParser.ts | 21 +++++++----- src/services/nostrService.ts | 2 -- src/services/sqlite.ts | 29 ++++++++++++---- src/services/wallet/sendTask.ts | 2 +- 21 files changed, 136 insertions(+), 68 deletions(-) diff --git a/src/components/Card.tsx b/src/components/Card.tsx index 20be1284..af944a3f 100644 --- a/src/components/Card.tsx +++ b/src/components/Card.tsx @@ -162,7 +162,7 @@ export function Card(props: CardProps) { const Wrapper: ComponentType = isPressable ? TouchableOpacity : View const HeaderContentWrapper = verticalAlignment === "force-footer-bottom" ? View : Fragment - const $containerStyle = [$containerPresets[preset], $containerStyleOverride] + const $containerStyle = [$containerPresets[preset], { backgroundColor }, $containerStyleOverride] const $headingStyle = [ $headingPresets[preset], (isFooterPresent || isContentPresent) && { marginBottom: spacing.micro }, @@ -191,7 +191,7 @@ export function Card(props: CardProps) { return ( (), 'sat'), isOnboarded: types.optional(types.boolean, false), isStorageEncrypted: types.optional(types.boolean, false), isLocalBackupOn: types.optional(types.boolean, true), @@ -30,7 +33,8 @@ export const UserSettingsStoreModel = types .actions(self => ({ loadUserSettings: () => { const { - walletId, + walletId, + preferredUnit, isOnboarded, isStorageEncrypted, isLocalBackupOn, @@ -47,7 +51,8 @@ export const UserSettingsStoreModel = types const booleanIsLoggerOn = isLoggerOn === 1 const booleanIsStorageMigrated = isStorageMigrated === 1 - self.walletId = walletId as string + self.walletId = walletId as string + self.preferredUnit = preferredUnit as MintUnit self.isOnboarded = booleanIsOnboarded as boolean self.isStorageEncrypted = booleanIsStorageEncrypted as boolean self.isLocalBackupOn = booleanIsLocalBackupOn as boolean @@ -62,6 +67,12 @@ export const UserSettingsStoreModel = types return walletId }, + setPreferredUnit: (preferredUnit: MintUnit) => { + Database.updateUserSettings({...self, preferredUnit}) + self.preferredUnit = preferredUnit + + return preferredUnit + }, setIsOnboarded: (isOnboarded: boolean) => { Database.updateUserSettings({...self, isOnboarded}) self.isOnboarded = isOnboarded diff --git a/src/models/helpers/setupRootStore.ts b/src/models/helpers/setupRootStore.ts index 56617654..a4c75b7b 100644 --- a/src/models/helpers/setupRootStore.ts +++ b/src/models/helpers/setupRootStore.ts @@ -242,6 +242,8 @@ async function _runMigrations(rootStore: RootStore) { if(currentVersion < 12) { log.trace(`Starting rootStore migrations from version v${currentVersion} -> v12`) + userSettingsStore.setPreferredUnit('sat') + for (const mint of mintsStore.allMints) { try { mint.addUnit('sat') diff --git a/src/navigation/TabsNavigator.tsx b/src/navigation/TabsNavigator.tsx index e1c95dd3..96f5bbe0 100644 --- a/src/navigation/TabsNavigator.tsx +++ b/src/navigation/TabsNavigator.tsx @@ -43,10 +43,8 @@ import { import { Contact } from "../models/Contact" import { colors, useThemeColor, spacing, typography } from "../theme" import { AppStackParamList, AppStackScreenProps } from "./AppNavigator" -import { MintBalance } from "../models/Mint" import { PaymentRequest } from "../models/PaymentRequest" import { LNURLPayParams, LNURLWithdrawParams } from "js-lnurl" -import { IncomingDataType } from "../services/incomingParser" import { MintUnit } from "../services/wallet/currency" diff --git a/src/screens/ContactDetailScreen.tsx b/src/screens/ContactDetailScreen.tsx index 19166b7e..263e5142 100644 --- a/src/screens/ContactDetailScreen.tsx +++ b/src/screens/ContactDetailScreen.tsx @@ -22,7 +22,7 @@ interface ContactDetailScreenProps extends ContactsStackScreenProps<'ContactDeta export const ContactDetailScreen: FC = observer( function ContactScreen({route, navigation}) { const {contact} = route.params - const {contactsStore} = useStores() + const {contactsStore, userSettingsStore} = useStores() const noteInputRef = useRef(null) useHeader({ @@ -77,8 +77,11 @@ export const ContactDetailScreen: FC = observer( try { await IncomingParser.navigateWithIncomingData({ type: IncomingDataType.LNURL_ADDRESS, - encoded: contact.lud16 - }, navigation) + encoded: contact.lud16 + }, + navigation, + userSettingsStore.preferredUnit + ) return } catch (e: any) { diff --git a/src/screens/Contacts/PrivateContacts.tsx b/src/screens/Contacts/PrivateContacts.tsx index e0fa50b4..8590ade9 100644 --- a/src/screens/Contacts/PrivateContacts.tsx +++ b/src/screens/Contacts/PrivateContacts.tsx @@ -26,7 +26,7 @@ export const PrivateContacts = observer(function (props: { navigation: StackNavigationProp, paymentOption: ReceiveOption | SendOption | undefined}, ) { - const {contactsStore, relaysStore} = useStores() + const {contactsStore, relaysStore, userSettingsStore} = useStores() const {navigation} = props const contactNameInputRef = useRef(null) @@ -211,7 +211,7 @@ export const PrivateContacts = observer(function (props: { await IncomingParser.navigateWithIncomingData({ type: IncomingDataType.LNURL_ADDRESS, encoded: contact.lud16 - }, navigation) + }, navigation, userSettingsStore.preferredUnit) //reset navigation.setParams({ diff --git a/src/screens/Contacts/PublicContacts.tsx b/src/screens/Contacts/PublicContacts.tsx index f2a7e4d3..7a45987d 100644 --- a/src/screens/Contacts/PublicContacts.tsx +++ b/src/screens/Contacts/PublicContacts.tsx @@ -34,7 +34,7 @@ export const PublicContacts = observer(function (props: { navigation: StackNavigationProp, paymentOption: ReceiveOption | SendOption |undefined} ) { - const {contactsStore, relaysStore} = useStores() + const {contactsStore, relaysStore, userSettingsStore} = useStores() const {navigation} = props const npubInputRef = useRef(null) @@ -392,7 +392,7 @@ export const PublicContacts = observer(function (props: { await IncomingParser.navigateWithIncomingData({ type: IncomingDataType.LNURL_ADDRESS, encoded: contact.lud16 - }, navigation) + }, navigation, userSettingsStore.preferredUnit) setIsLoading(false) //reset diff --git a/src/screens/LightningPayScreen.tsx b/src/screens/LightningPayScreen.tsx index 1231be20..95bfe6d7 100644 --- a/src/screens/LightningPayScreen.tsx +++ b/src/screens/LightningPayScreen.tsx @@ -105,7 +105,7 @@ export const LightningPayScreen: FC> = fu try { const invoiceResult = IncomingParser.findAndExtract(lightningData as string, IncomingDataType.INVOICE) - return IncomingParser.navigateWithIncomingData(invoiceResult, navigation, mint && unit && {mintUrl: mint.mintUrl, unit}) + return IncomingParser.navigateWithIncomingData(invoiceResult, navigation, unit, mint && mint.mintUrl) } catch (e: any) { const maybeLnurlAddress = LnurlUtils.findEncodedLnurlAddress(lightningData as string) @@ -119,7 +119,7 @@ export const LightningPayScreen: FC> = fu await IncomingParser.navigateWithIncomingData({ type: IncomingDataType.LNURL_ADDRESS, encoded: validAddress, - }, navigation, mint && unit && {mintUrl: mint.mintUrl, unit}) + }, navigation, unit, mint && mint.mintUrl) } return } catch (e3: any) { @@ -139,7 +139,7 @@ export const LightningPayScreen: FC> = fu await IncomingParser.navigateWithIncomingData({ type: IncomingDataType.LNURL, encoded: encodedLnurl - }, navigation, mint && unit && {mintUrl: mint.mintUrl, unit}) + }, navigation, unit, mint && mint.mintUrl) } return } catch (e2: any) { diff --git a/src/screens/ScanScreen.tsx b/src/screens/ScanScreen.tsx index 023ed215..1274b30f 100644 --- a/src/screens/ScanScreen.tsx +++ b/src/screens/ScanScreen.tsx @@ -19,6 +19,7 @@ import { BottomModal, Button, ErrorModal, Icon, Text } from '../components' import { LnurlUtils } from '../services/lnurl/lnurlUtils' import { infoMessage } from '../utils/utils' import Clipboard from '@react-native-clipboard/clipboard' +import { useStores } from '../models' const hasAndroidCameraPermission = async () => { const cameraPermission = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.CAMERA) @@ -36,6 +37,7 @@ export const ScanScreen: FC> = function ScanScree }) const addressInputRef = useRef(null) + const {userSettingsStore} = useStores() const [shouldLoad, setShouldLoad] = useState(false) const [isScanned, setIsScanned] = useState(false) @@ -78,14 +80,16 @@ export const ScanScreen: FC> = function ScanScree } - const onIncomingData = async function(incoming: any) { + const onIncomingData = async function(incoming: any) { + + const {preferredUnit: unit} = userSettingsStore switch (prevRouteName) { case 'ReceiveOptions': log.trace('ReceiveOptions') try { const tokenResult = IncomingParser.findAndExtract(incoming, IncomingDataType.CASHU) - return IncomingParser.navigateWithIncomingData(tokenResult, navigation) + return IncomingParser.navigateWithIncomingData(tokenResult, navigation, unit) } catch (e: any) { const maybeLnurl = LnurlUtils.findEncodedLnurl(incoming) @@ -99,7 +103,7 @@ export const ScanScreen: FC> = function ScanScree await IncomingParser.navigateWithIncomingData({ type: IncomingDataType.LNURL, encoded: encodedLnurl - }, navigation) + }, navigation, unit) } return } catch (e2: any) { @@ -116,7 +120,7 @@ export const ScanScreen: FC> = function ScanScree case 'LightningPay': try { const invoiceResult = IncomingParser.findAndExtract(incoming, IncomingDataType.INVOICE) - return IncomingParser.navigateWithIncomingData(invoiceResult, navigation) + return IncomingParser.navigateWithIncomingData(invoiceResult, navigation, unit) } catch (e: any) { const maybeLnurlAddress = LnurlUtils.findEncodedLnurlAddress(incoming) @@ -130,7 +134,7 @@ export const ScanScreen: FC> = function ScanScree await IncomingParser.navigateWithIncomingData({ type: IncomingDataType.LNURL_ADDRESS, encoded: validAddress - }, navigation) + }, navigation, unit) } return } catch (e3: any) { @@ -150,7 +154,7 @@ export const ScanScreen: FC> = function ScanScree await IncomingParser.navigateWithIncomingData({ type: IncomingDataType.LNURL, encoded: encodedLnurl - }, navigation) + }, navigation, unit) } return } catch (e2: any) { @@ -167,7 +171,7 @@ export const ScanScreen: FC> = function ScanScree try { // generic scan button on wallet screen const incomingData = IncomingParser.findAndExtract(incoming) - return IncomingParser.navigateWithIncomingData(incomingData, navigation) + return IncomingParser.navigateWithIncomingData(incomingData, navigation, unit) } catch (e: any) { e.name = Err.VALIDATION_ERROR e.params = {caller: 'onIncomingData', clipboard: incoming.slice(0, 100)} diff --git a/src/screens/SendScreen.tsx b/src/screens/SendScreen.tsx index 499441fa..280d211c 100644 --- a/src/screens/SendScreen.tsx +++ b/src/screens/SendScreen.tsx @@ -989,7 +989,8 @@ const SendAsQRCodeBlock = observer(function (props: { return ( {} @@ -26,7 +27,7 @@ export const SettingsScreen: FC = observer( function SettingsScreen(_props) { const {navigation} = _props useHeader({}) // default header component - const {mintsStore, relaysStore} = useStores() + const {mintsStore, relaysStore, userSettingsStore} = useStores() const [isUpdateAvailable, setIsUpdateAvailable] = useState(false) const [updateDescription, setUpdateDescription] = useState('') @@ -124,10 +125,27 @@ export const SettingsScreen: FC = observer( bottomSeparator={true} onPress={gotoMints} /> + + + + } + bottomSeparator={true} + onPress={gotoBackupRestore} + /> = observer( > = +export const TranDetailScreen: FC> = observer(function TranDetailScreen(_props) { const {navigation, route} = _props const {transactionsStore, userSettingsStore} = useStores() diff --git a/src/screens/TranHistoryScreen.tsx b/src/screens/TranHistoryScreen.tsx index 3a2dc5f0..94c2538d 100644 --- a/src/screens/TranHistoryScreen.tsx +++ b/src/screens/TranHistoryScreen.tsx @@ -24,7 +24,7 @@ import { Loading, BottomModal, } from '../components' -import {WalletStackScreenProps} from '../navigation' +import {TransactionsStackScreenProps, WalletStackScreenProps} from '../navigation' import {useHeader} from '../utils/useHeader' import {useStores} from '../models' import {GroupedByTimeAgo, maxTransactionsInModel} from '../models/TransactionsStore' @@ -34,10 +34,6 @@ import {TransactionListItem} from './Transactions/TransactionListItem' import { Transaction, TransactionStatus } from '../models/Transaction' import { height } from '@fortawesome/free-solid-svg-icons/faWallet' - -interface TranHistoryScreenProps - extends WalletStackScreenProps<'TranHistory'> {} - if (Platform.OS === 'android' && UIManager.setLayoutAnimationEnabledExperimental) { UIManager.setLayoutAnimationEnabledExperimental(true) @@ -45,7 +41,7 @@ if (Platform.OS === 'android' && // Number of transactions held in TransactionsStore model const limit = maxTransactionsInModel -export const TranHistoryScreen: FC = observer(function TranHistoryScreen(_props) { +export const TranHistoryScreen: FC> = observer(function TranHistoryScreen(_props) { const {navigation} = _props const {transactionsStore, proofsStore, mintsStore} = useStores() useHeader({ diff --git a/src/screens/Transactions/TransactionListItem.tsx b/src/screens/Transactions/TransactionListItem.tsx index 7159d0db..5d6a1d31 100644 --- a/src/screens/Transactions/TransactionListItem.tsx +++ b/src/screens/Transactions/TransactionListItem.tsx @@ -1,4 +1,4 @@ -import formatDistance from 'date-fns/formatDistance' +import {formatDistance} from 'date-fns/formatDistance' import {observer} from 'mobx-react-lite' import React from 'react' import {ScrollView, TextStyle, View, ViewStyle} from 'react-native' diff --git a/src/screens/WalletScreen.tsx b/src/screens/WalletScreen.tsx index 989f85ed..0124e0bd 100644 --- a/src/screens/WalletScreen.tsx +++ b/src/screens/WalletScreen.tsx @@ -83,9 +83,9 @@ export const WalletScreen: FC = observer( transactionsStore, paymentRequestsStore, userSettingsStore, - walletProfileStore } = useStores() + const pagerRef = useRef(null) const appState = useRef(AppState.currentState) const isInternetReachable = useIsInternetReachable() const returnWithNavigationReset = route.params?.returnWithNavigationReset @@ -158,7 +158,10 @@ export const WalletScreen: FC = observer( WalletTask.handleInFlight().catch(e => false) // Create websocket subscriptions to receive tokens or payment requests by NOSTR DMs WalletTask.receiveEventsFromRelays().catch(e => false) - // log.trace('[getInitialData]', 'walletProfile', walletProfileStore) + // log.trace('[getInitialData]', 'walletProfile', walletProfileStore) + const preferredUnit: MintUnit = userSettingsStore.preferredUnit + const pageIndex = groupedMints.findIndex(m => m.unit === preferredUnit) + pagerRef.current && pagerRef.current.setPage(pageIndex) } Linking.addEventListener('url', handleDeeplink) @@ -172,7 +175,11 @@ export const WalletScreen: FC = observer( try { const incomingData = IncomingParser.findAndExtract(url) - await IncomingParser.navigateWithIncomingData(incomingData, navigation) + await IncomingParser.navigateWithIncomingData( + incomingData, + navigation, + currentUnit + ) } catch (e: any) { handleError(e) @@ -335,8 +342,7 @@ export const WalletScreen: FC = observer( /* Mints pager */ - const width = spacing.screenWidth - const pagerRef = useRef(null) + const width = spacing.screenWidth const scrollOffsetAnimatedValue = React.useRef(new Animated.Value(0)).current const positionAnimatedValue = React.useRef(new Animated.Value(0)).current const inputRange = [0, groupedMints.length] @@ -369,10 +375,21 @@ export const WalletScreen: FC = observer( ) - const onPageSelected = (e: any) => { - const currentUnit = groupedMints[e.nativeEvent.position].unit + const onPageSelected = (e: any) => { + if(groupedMints.length === 0) { + return + } + + const currentUnit = groupedMints[e.nativeEvent.position]?.unit log.trace('[onPageSelected] currentUnit', currentUnit) + setCurrentUnit(currentUnit) + + const preferredUnit = userSettingsStore.preferredUnit + + if(currentUnit !== preferredUnit) { // prevents db write on first load + userSettingsStore.setPreferredUnit(currentUnit) + } } const handleError = function (e: AppError) { @@ -442,7 +459,7 @@ export const WalletScreen: FC = observer( ref={pagerRef} style={{flexGrow: 1}} onPageScroll={onPageScroll} - onPageSelected={onPageSelected} + onPageSelected={onPageSelected} > {groupedMints.map((mints) => ( diff --git a/src/services/incomingParser.ts b/src/services/incomingParser.ts index c95fd40a..d5ae1652 100644 --- a/src/services/incomingParser.ts +++ b/src/services/incomingParser.ts @@ -136,24 +136,24 @@ const navigateWithIncomingData = async function ( encoded: any }, navigation: StackNavigationProp, - mintAndUnit?: { - mintUrl: string, - unit: MintUnit - } + unit: MintUnit, + mintUrl?: string ) { switch (incoming.type) { case IncomingDataType.CASHU: return navigation.navigate('Receive', { encodedToken: incoming.encoded, - ...mintAndUnit + unit, + mintUrl }) case IncomingDataType.INVOICE: return navigation.navigate('Transfer', { encodedInvoice: incoming.encoded, paymentOption: SendOption.PASTE_OR_SCAN_INVOICE, - ...mintAndUnit + unit, + mintUrl }) case (IncomingDataType.LNURL): @@ -165,7 +165,8 @@ const navigateWithIncomingData = async function ( return navigation.navigate('Topup', { lnurlParams, paymentOption: ReceiveOption.LNURL_WITHDRAW, - ...mintAndUnit + unit, + mintUrl }) } @@ -173,7 +174,8 @@ const navigateWithIncomingData = async function ( return navigation.navigate('Transfer', { lnurlParams, paymentOption: SendOption.LNURL_PAY, - ...mintAndUnit + unit, + mintUrl }) } @@ -192,7 +194,8 @@ const navigateWithIncomingData = async function ( return navigation.navigate('Transfer', { lnurlParams: addressParamsResult.lnurlParams, paymentOption: SendOption.LNURL_PAY, - ...mintAndUnit + unit, + mintUrl }) } catch (e: any) { throw new AppError(Err.SERVER_ERROR, 'Could not get Lightning address details from the server.', { diff --git a/src/services/nostrService.ts b/src/services/nostrService.ts index b7f33675..15192fa5 100644 --- a/src/services/nostrService.ts +++ b/src/services/nostrService.ts @@ -23,7 +23,6 @@ import AppError, { Err } from '../utils/AppError' import { MinibitsClient } from './minibitsService' import { rootStoreInstance } from '../models' import { WalletTask } from './walletService' -import { WalletTask } from './walletService' export { NostrEvent, @@ -204,7 +203,6 @@ const decryptNip04 = async function( const publish = async function ( event: NostrUnsignedEvent, relays: string[], -): Promise { ): Promise { const keys: KeyPair = await getOrCreateKeyPair() diff --git a/src/services/sqlite.ts b/src/services/sqlite.ts index 4bbcdeac..37e78730 100644 --- a/src/services/sqlite.ts +++ b/src/services/sqlite.ts @@ -16,7 +16,6 @@ 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 @@ -71,7 +70,8 @@ const _createOrUpdateSchema = function (db: QuickSQLiteConnection) { [ `CREATE TABLE IF NOT EXISTS usersettings ( id INTEGER PRIMARY KEY NOT NULL, - walletId TEXT, + walletId TEXT, + preferredUnit TEXT, isOnboarded BOOLEAN, isStorageEncrypted BOOLEAN, isLocalBackupOn BOOLEAN, @@ -211,6 +211,9 @@ const _runMigrations = function (db: QuickSQLiteConnection) { ], [ `ALTER TABLE proofs ADD COLUMN unit TEXT`, + ],[ + `ALTER TABLE usersettings + ADD COLUMN preferredUnit TEXT`, ]) log.info(`Prepared database migrations from ${currentVersion} -> 9`) @@ -332,7 +335,8 @@ const getUserSettings = function (): UserSettings { if (!rows?.item(0)) { const walletId = _generateWalletId() const defaultSettings = updateUserSettings({ - walletId, + walletId, + preferredUnit: 'sat', isOnboarded: 0, isStorageEncrypted: 0, isLocalBackupOn: 1, @@ -358,15 +362,26 @@ const getUserSettings = function (): UserSettings { const updateUserSettings = function (settings: UserSettings): UserSettings { try { const now = new Date() - const {walletId, isOnboarded, isStorageEncrypted, isLocalBackupOn, isTorDaemonOn, isLoggerOn, isStorageMigrated, logLevel} = settings + const { + walletId, + preferredUnit, + isOnboarded, + isStorageEncrypted, + isLocalBackupOn, + isTorDaemonOn, + isLoggerOn, + isStorageMigrated, + logLevel + } = settings const query = ` - INSERT OR REPLACE INTO usersettings (id, walletId, isOnboarded, isStorageEncrypted, isLocalBackupOn, isTorDaemonOn, isLoggerOn, isStorageMigrated, logLevel, createdAt) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + INSERT OR REPLACE INTO usersettings (id, walletId, preferredUnit, isOnboarded, isStorageEncrypted, isLocalBackupOn, isTorDaemonOn, isLoggerOn, isStorageMigrated, logLevel, createdAt) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ` const params = [ 1, - walletId, + walletId, + preferredUnit, isOnboarded, isStorageEncrypted, isLocalBackupOn, diff --git a/src/services/wallet/sendTask.ts b/src/services/wallet/sendTask.ts index 53046e74..25dde25b 100644 --- a/src/services/wallet/sendTask.ts +++ b/src/services/wallet/sendTask.ts @@ -137,7 +137,7 @@ export const sendTask = async function ( WalletTask.handleSpentByMint, { interval: 6 * 1000, - maxPolls: 1, // TODO 10 + maxPolls: 10, maxErrors: 2 }, {mintUrl, isPending: true}